Subversion Repositories windmill

Compare Revisions

Rev 1 → Rev 186

dot-two-refactor/windmill/test/test_jsonrpc.py New file
0,0 → 1,33
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License a
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
import simplejson
from windmill_test_lib import setup_module, teardown_module
 
def test_add():
# Add a test
test_add = {u'method':u'click', u'params':{u'id':u'aksjdflkajsdflkjasldkfjl'}}
jsonrpc_client.add_json_test(simplejson.dumps(test_add))
x = jsonrpc_client.next_action()
assert x == {u'result': {u'version': u'0.1', u'params': {u'id': u'aksjdflkajsdflkjasldkfjl'}, u'method': u'click'}}
 
test_report = {'test':test_add, 'result': True, 'starttime':'1994-11-05T13:15:30.45Z',
'endtime':'1994-11-05T13:15:30.56Z'}
 
x = jsonrpc_client.report(**test_report)
assert x == {'result':200}
resolved_test = {'debug': None, u'method': u'click', u'params': {u'id': u'aksjdflkajsdflkjasldkfjl'}, 'result': True, 'version': '0.1'}
assert httpd.test_resolution_suite.resolved_tests.__contains__(resolved_test)
 
 
\ No newline at end of file
dot-two-refactor/windmill/test/__init__.py New file
0,0 → 1,15
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
import test_browser, test_jsonrpc, test_proxy, test_xmlrpc, windmill_test_lib
\ No newline at end of file
dot-two-refactor/windmill/test/test_proxy.py New file
0,0 → 1,23
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
 
 
 
 
 
 
 
 
 
\ No newline at end of file
dot-two-refactor/windmill/test/test_browser.py New file
0,0 → 1,18
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
 
 
 
 
\ No newline at end of file
dot-two-refactor/windmill/test/test_xmlrpc.py New file
0,0 → 1,13
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
dot-two-refactor/windmill/test/windmill_test_lib.py New file
0,0 → 1,45
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
 
def setup_module(module):
import windmill, simplejson
 
windmill.conf.configure_settings()
 
httpd, httpd_thread, console_log_handler = windmill.bin.run_server.run_threaded()
 
# setup all usefull objects
jsonrpc_client = windmill.tools.make_jsonrpc_client()
xmlrpc_client = windmill.tools.make_xmlrpc_client()
 
module.httpd = httpd
module.httpd_thread = httpd_thread
module.console_log_handler = console_log_handler
module.jsonrpc_client = jsonrpc_client
module.xmlrpc_client = xmlrpc_client
 
if hasattr(module, 'test_dict'):
for key, test in module.test_dict.items():
test.httpd = httpd
test.httpd_thread = httpd_thread
test.console_log_handler = console_log_handler
test.jsonrpc_client = jsonrpc_client
test.xmlrpc_client = xmlrpc_client
 
def teardown_module(module):
 
while module.httpd_thread.isAlive():
module.httpd.stop()
 
dot-two-refactor/windmill/tools/json_tools.py New file
0,0 → 1,132
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
import time
import httplib, urllib, copy
from urlparse import urlparse
import xmlrpclib
import simplejson
import logging
 
__version__ = str(0.1)
 
logger = logging.getLogger('tools.jsonrpc_client')
 
class _Method(object):
 
def __init__(self, call, name):
self.call = call
self.name = name
 
def __call__(self, *args, **kwargs):
 
#
# I need to handle keyword arguments per 1.1 spec
#
 
request = {}
request['version'] = '1.1'
request['method'] = self.name
if len(kwargs) is not 0:
params = copy.copy(kwargs)
index = 0
for arg in args:
params[str(index)] = arg
index = index + 1
elif len(args) is not 0:
params = copy.copy(args)
else:
params = None
request['params'] = params
logger.debug('Created python request object %s' % str(request))
return self.call(simplejson.dumps(request))
 
def __getattr__(self, name):
return _Method(self.call, "%s.%s" % (self.name, name))
 
class JSONRPCTransport:
 
def __init__(self, uri, proxy_uri=None):
 
if proxy_uri is not None:
self.connection_url = urlparse(proxy_uri)
self.request_path = uri
else:
self.connection_url = urlparse(uri)
self.request_path = self.connection_url.path
 
headers = {'User-Agent':'jsonrpclib',
'Content-Type':'application/json',
'Accept':'application/json'}
 
def request(self, request_body):
 
if self.connection_url.scheme == 'http':
if self.connection_url == '':
port = 80
else:
port = self.connection_url.port
connection = httplib.HTTPConnection(self.connection_url.hostname+':'+str(port))
elif self.connection_url.scheme == 'https':
if self.connection_url == '':
port = 443
else:
port = self.connection_url.port
connection = httplib.HTTPSConnection(self.connection_url.hostname+':'+str(port))
else:
raise Exception, 'unsupported transport'
 
connection.request('POST', self.request_path, body=request_body, headers=self.headers)
self.response = connection.getresponse()
if self.response.status == 200:
return self.response.read()
else:
return self.response.status
 
 
class ServerProxy(object):
"""Blah
"""
 
def __init__(self, uri=None, transport=None):
"""Initialization"""
if uri is None and transport is None:
raise Exception, 'either uri or transport needs to be specified'
 
if transport is None:
transport = JSONRPCTransport(uri)
self.__transport = transport
 
def __request(self, request):
# call a method on the remote server
 
response = self.__transport.request(request)
logger.debug('got response from __transport :: %s' % response)
if type(response) is not int:
return simplejson.loads(response)
else:
logger.error('Recieved status code %s' % response)
 
def __repr__(self):
return (
"<ServerProxy for %s%s>" %
(self.__host, self.__handler)
)
 
__str__ = __repr__
 
def __getattr__(self, name):
# magic method dispatcher
return _Method(self.__request, name)
 
dot-two-refactor/windmill/tools/__init__.py New file
0,0 → 1,33
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
import dev_environment, json_tools, server_tools
 
def make_xmlrpc_client():
import windmill
import xmlrpclib
proxy = windmill.tools.server_tools.ProxiedTransport('localhost:4444')
xmlrpc_client = xmlrpclib.ServerProxy(windmill.settings['TEST_URL']+'/windmill-xmlrpc/',transport=proxy)
return xmlrpc_client
 
def make_jsonrpc_client():
import windmill
proxy = windmill.tools.json_tools.JSONRPCTransport(uri=windmill.settings['TEST_URL']+'/windmill-jsonrpc/', proxy_uri='http://localhost:4444')
jsonrpc_client = windmill.tools.json_tools.ServerProxy(transport=proxy)
return jsonrpc_client
 
def start_browser():
import windmill
browser = windmill.browser.browser_tools.setup_firefox()
return browser
\ No newline at end of file
dot-two-refactor/windmill/tools/dev_environment.py New file
0,0 → 1,120
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
import time
 
try:
import IPython
import __builtin__
 
 
class IPyShell(IPython.iplib.InteractiveShell):
 
def interact(self, banner=None):
"""Closely emulate the interactive Python console.
 
The optional banner argument specify the banner to print
before the first interaction; by default it prints a banner
similar to the one printed by the real Python interpreter,
followed by the current class name in parentheses (so as not
to confuse this with the real interpreter -- since it's so
close!).
 
"""
cprt = 'Type "copyright", "credits" or "license" for more information.'
if banner is None:
self.write("Python %s on %s\n%s\n(%s)\n" %
(sys.version, sys.platform, cprt,
self.__class__.__name__))
else:
self.write(banner)
 
more = 0
 
# Mark activity in the builtins
__builtin__.__dict__['__IPYTHON__active'] += 1
 
# exit_now is set by a call to %Exit or %Quit
self.exit_now = False
while not self.exit_now:
if more:
prompt = self.outputcache.prompt2
if self.autoindent:
self.readline_startup_hook(self.pre_readline)
else:
prompt = self.outputcache.prompt1
try:
line = self.raw_input(prompt,more)
if self.autoindent:
self.readline_startup_hook(None)
except KeyboardInterrupt:
self.write('\nKeyboardInterrupt\n')
self.resetbuffer()
# keep cache in sync with the prompt counter:
self.outputcache.prompt_count -= 1
 
if self.autoindent:
self.indent_current_nsp = 0
more = 0
except EOFError:
if self.autoindent:
self.readline_startup_hook(None)
self.write('\n')
while self.httpd_thread.isAlive():
self.httpd.stop()
self.exit()
except bdb.BdbQuit:
warn('The Python debugger has exited with a BdbQuit exception.\n'
'Because of how pdb handles the stack, it is impossible\n'
'for IPython to properly format this particular exception.\n'
'IPython will resume normal operation.')
except:
# exceptions here are VERY RARE, but they can be triggered
# asynchronously by signal handlers, for example.
self.showtraceback()
else:
more = self.push(line)
if (self.SyntaxTB.last_syntax_error and
self.rc.autoedit_syntax):
self.edit_syntax_error()
 
# We are off again...
__builtin__.__dict__['__IPYTHON__active'] -= 1
 
except:
 
import code
 
 
def make_shell():
 
import run_server
 
HTTPD, HTTPD_THREAD, loggers = run_server.main()
 
import browser_tools
import server_tools
 
# Browser tests
browser = browser_tools.setup_browser()
print 'browser should be coming up'
 
try:
# try to use IPython if possible
import IPython
shell = IPython.Shell.IPShell(user_ns=locals(), shell_class=IPyShell)
shell.mainloop()
except:
import code
code.interact(local=locals())
\ No newline at end of file
dot-two-refactor/windmill/tools/server_tools.py New file
0,0 → 1,49
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
import time
import httplib, urllib
from urlparse import urlparse
import xmlrpclib
 
def get_request(url, proxy_host='localhost', proxy_port=4444):
 
connection = httplib.HTTPConnection(proxy_host + ':' + str(proxy_port))
connection.request('GET', url)
response = connection.getresponse()
response.body = response.read()
return response
 
import xmlrpclib
 
 
class ProxiedTransport(xmlrpclib.Transport):
 
def __init__(self, proxy, user_agent='python.httplib'):
"""Initialization, set the proxy location"""
xmlrpclib.Transport.__init__(self)
self.proxy = proxy
self.user_agent = user_agent
 
def make_connection(self, host):
self.realhost = host
import httplib
return httplib.HTTP(self.proxy)
 
def send_request(self, connection, handler, request_body):
connection.putrequest("POST", 'http://%s%s' % (self.realhost, handler))
 
def send_host(self, connection, host):
connection.putheader('Host', self.realhost)
 
dot-two-refactor/windmill/browser/winprocess.py New file
0,0 → 1,262
# A module to expose various thread/process/job related structures and
# methods from kernel32
#
# The MIT License
#
# Copyright (c) 2006 the Mozilla Foundation <http://www.mozilla.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
 
from ctypes import c_void_p, POINTER, sizeof, Structure, windll, WinError, WINFUNCTYPE
from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LPCWSTR, LPWSTR, UINT, WORD
 
LPVOID = c_void_p
LPBYTE = POINTER(BYTE)
LPDWORD = POINTER(DWORD)
 
def ErrCheckBool(result, func, args):
"""errcheck function for Windows functions that return a BOOL True
on success"""
if not result:
raise WinError()
return args
 
# CloseHandle()
 
CloseHandleProto = WINFUNCTYPE(BOOL, HANDLE)
CloseHandle = CloseHandleProto(("CloseHandle", windll.kernel32))
CloseHandle.errcheck = ErrCheckBool
 
# AutoHANDLE
 
class AutoHANDLE(HANDLE):
"""Subclass of HANDLE which will call CloseHandle() on deletion."""
def Close(self):
if self.value:
CloseHandle(self)
self.value = 0
 
def __del__(self):
self.Close()
 
def __int__(self):
return self.value
 
def ErrCheckHandle(result, func, args):
"""errcheck function for Windows functions that return a HANDLE."""
if not result:
raise WinError()
return AutoHANDLE(result)
 
# PROCESS_INFORMATION structure
 
class PROCESS_INFORMATION(Structure):
_fields_ = [("hProcess", HANDLE),
("hThread", HANDLE),
("dwProcessID", DWORD),
("dwThreadID", DWORD)]
 
def __init__(self):
Structure.__init__(self)
 
self.cb = sizeof(self)
 
LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION)
 
# STARTUPINFO structure
 
class STARTUPINFO(Structure):
_fields_ = [("cb", DWORD),
("lpReserved", LPWSTR),
("lpDesktop", LPWSTR),
("lpTitle", LPWSTR),
("dwX", DWORD),
("dwY", DWORD),
("dwXSize", DWORD),
("dwYSize", DWORD),
("dwXCountChars", DWORD),
("dwYCountChars", DWORD),
("dwFillAttribute", DWORD),
("dwFlags", DWORD),
("wShowWindow", WORD),
("cbReserved2", WORD),
("lpReserved2", LPBYTE),
("hStdInput", HANDLE),
("hStdOutput", HANDLE),
("hStdError", HANDLE)
]
LPSTARTUPINFO = POINTER(STARTUPINFO)
 
STARTF_USESHOWWINDOW = 0x01
STARTF_USESIZE = 0x02
STARTF_USEPOSITION = 0x04
STARTF_USECOUNTCHARS = 0x08
STARTF_USEFILLATTRIBUTE = 0x10
STARTF_RUNFULLSCREEN = 0x20
STARTF_FORCEONFEEDBACK = 0x40
STARTF_FORCEOFFFEEDBACK = 0x80
STARTF_USESTDHANDLES = 0x100
 
# EnvironmentBlock
 
class EnvironmentBlock:
"""An object which can be passed as the lpEnv parameter of CreateProcess.
It is initialized with a dictionary."""
 
def __init__(self, dict):
if not dict:
self._as_parameter_ = None
else:
values = ["%s=%s" % (key, value)
for (key, value) in dict.iteritems()]
values.append("")
self._as_parameter_ = LPCWSTR("\0".join(values))
 
# CreateProcess()
 
CreateProcessProto = WINFUNCTYPE(BOOL, # Return type
LPCWSTR, # lpApplicationName
LPWSTR, # lpCommandLine
LPVOID, # lpProcessAttributes
LPVOID, # lpThreadAttributes
BOOL, # bInheritHandles
DWORD, # dwCreationFlags
LPVOID, # lpEnvironment
LPCWSTR, # lpCurrentDirectory
LPSTARTUPINFO, # lpStartupInfo
LPPROCESS_INFORMATION # lpProcessInformation
)
 
CreateProcessFlags = ((1, "lpApplicationName", None),
(1, "lpCommandLine"),
(1, "lpProcessAttributes", None),
(1, "lpThreadAttributes", None),
(1, "bInheritHandles", True),
(1, "dwCreationFlags", 0),
(1, "lpEnvironment", None),
(1, "lpCurrentDirectory", None),
(1, "lpStartupInfo"),
(2, "lpProcessInformation"))
 
def ErrCheckCreateProcess(result, func, args):
ErrCheckBool(result, func, args)
# return a tuple (hProcess, hThread, dwProcessID, dwThreadID)
pi = args[9]
return AutoHANDLE(pi.hProcess), AutoHANDLE(pi.hThread), pi.dwProcessID, pi.dwThreadID
 
CreateProcess = CreateProcessProto(("CreateProcessW", windll.kernel32),
CreateProcessFlags)
CreateProcess.errcheck = ErrCheckCreateProcess
 
CREATE_BREAKAWAY_FROM_JOB = 0x01000000
CREATE_DEFAULT_ERROR_MODE = 0x04000000
CREATE_NEW_CONSOLE = 0x00000010
CREATE_NEW_PROCESS_GROUP = 0x00000200
CREATE_NO_WINDOW = 0x08000000
CREATE_SUSPENDED = 0x00000004
CREATE_UNICODE_ENVIRONMENT = 0x00000400
DEBUG_ONLY_THIS_PROCESS = 0x00000002
DEBUG_PROCESS = 0x00000001
DETACHED_PROCESS = 0x00000008
 
# CreateJobObject()
 
CreateJobObjectProto = WINFUNCTYPE(HANDLE, # Return type
LPVOID, # lpJobAttributes
LPCWSTR # lpName
)
 
CreateJobObjectFlags = ((1, "lpJobAttributes", None),
(1, "lpName", None))
 
CreateJobObject = CreateJobObjectProto(("CreateJobObjectW", windll.kernel32),
CreateJobObjectFlags)
CreateJobObject.errcheck = ErrCheckHandle
 
# AssignProcessToJobObject()
 
AssignProcessToJobObjectProto = WINFUNCTYPE(BOOL, # Return type
HANDLE, # hJob
HANDLE # hProcess
)
AssignProcessToJobObjectFlags = ((1, "hJob"),
(1, "hProcess"))
AssignProcessToJobObject = AssignProcessToJobObjectProto(
("AssignProcessToJobObject", windll.kernel32),
AssignProcessToJobObjectFlags)
AssignProcessToJobObject.errcheck = ErrCheckBool
 
# ResumeThread()
 
def ErrCheckResumeThread(result, func, args):
if result == -1:
raise WinError()
 
return args
 
ResumeThreadProto = WINFUNCTYPE(DWORD, # Return type
HANDLE # hThread
)
ResumeThreadFlags = ((1, "hThread"),)
ResumeThread = ResumeThreadProto(("ResumeThread", windll.kernel32),
ResumeThreadFlags)
ResumeThread.errcheck = ErrCheckResumeThread
 
# TerminateJobObject()
 
TerminateJobObjectProto = WINFUNCTYPE(BOOL, # Return type
HANDLE, # hJob
UINT # uExitCode
)
TerminateJobObjectFlags = ((1, "hJob"),
(1, "uExitCode", 127))
TerminateJobObject = TerminateJobObjectProto(
("TerminateJobObject", windll.kernel32),
TerminateJobObjectFlags)
TerminateJobObject.errcheck = ErrCheckBool
 
# WaitForSingleObject()
 
WaitForSingleObjectProto = WINFUNCTYPE(DWORD, # Return type
HANDLE, # hHandle
DWORD, # dwMilliseconds
)
WaitForSingleObjectFlags = ((1, "hHandle"),
(1, "dwMilliseconds", -1))
WaitForSingleObject = WaitForSingleObjectProto(
("WaitForSingleObject", windll.kernel32),
WaitForSingleObjectFlags)
 
INFINITE = -1
WAIT_TIMEOUT = 0x0102
WAIT_OBJECT_0 = 0x0
WAIT_ABANDONED = 0x0080
 
# GetExitCodeProcess()
 
GetExitCodeProcessProto = WINFUNCTYPE(BOOL, # Return type
HANDLE, # hProcess
LPDWORD, # lpExitCode
)
GetExitCodeProcessFlags = ((1, "hProcess"),
(2, "lpExitCode"))
GetExitCodeProcess = GetExitCodeProcessProto(
("GetExitCodeProcess", windll.kernel32),
GetExitCodeProcessFlags)
GetExitCodeProcess.errcheck = ErrCheckBool
Property changes : Added: svn:mime-type + text/plain Added: svn:eol-style + native
dot-two-refactor/windmill/browser/ie.py New file
0,0 → 1,83
import windmill
import exceptions
import os, sys, shutil, time, signal
import killableprocess
import logging
from urlparse import urlparse
 
if sys.platform == "win32" or sys.platform == "cygwin":
import _winreg as wreg
 
logger = logging.getLogger(__name__)
 
PROXY_PORT = windmill.settings['SERVER_HTTP_PORT']
DEFAULT_TEST_URL = windmill.settings['TEST_URL']+'/windmill-serv/start.html'
IE_BINARY = windmill.settings['IE_BINARY']
 
class InternetExplorer(object):
 
 
registry_modifications = {'MigrateProxy': {'type': wreg.REG_DWORD, 'new_value':1},
'ProxyEnable': {'type': wreg.REG_DWORD, 'new_value':1},
'ProxyHttp1.1': {'type': wreg.REG_DWORD, 'new_value':0},
'ProxyServer': {'type': wreg.REG_SZ}}
 
def __init__(self, proxy_port=PROXY_PORT, test_url=DEFAULT_TEST_URL, ie_binary=IE_BINARY):
 
self.proxy_port = proxy_port
self.test_url = test_url
self.registry_modifications['ProxyServer']['new_value'] = "http://localhost:%s" % self.proxy_port
self.reg = wreg.OpenKey(wreg.HKEY_CURRENT_USER,
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, wreg.KEY_ALL_ACCESS)
 
for key, value in self.registry_modifications.items():
try:
result = wreg.QueryValueEx(self.reg, key)
self.registry_modifications[key]['previous_value'] = result[0]
except exceptions.WindowsError:
self.registry_modifications[key]['previous_value'] = None
 
self.cmd = [ie_binary, self.test_url]
 
 
 
def start(self):
 
for key, value in self.registry_modifications.items():
wreg.SetValueEx(self.reg, key, 0, value['type'], value['new_value'])
 
allow_reg = wreg.OpenKey(wreg.HKEY_CURRENT_USER,
"Software\\Microsoft\\Internet Explorer\\New Windows\\Allow", 0, wreg.KEY_ALL_ACCESS)
 
wreg.SetValueEx(allow_reg, urlparse(windmill.settings['TEST_URL']).hostname,
0, wreg.REG_BINARY, None)
 
self.p_handle = killableprocess.Popen(self.cmd)
 
def stop(self):
 
for key, value in self.registry_modifications.items():
if value['previous_value'] is not None:
wreg.SetValueEx(self.reg, key, 0, value['type'], value['previous_value'])
else:
wreg.DeleteValue(self.reg, key)
 
try:
self.p_handle.kill(group=True)
except:
logger.error('Cannot kill firefox')
 
def is_alive(self):
 
if self.p_handle.poll() is None:
return False
 
try:
self.p_handle.kill(group=True)
return True
except exceptions.OSError:
return False
 
 
 
 
\ No newline at end of file
dot-two-refactor/windmill/browser/killableprocess.py New file
0,0 → 1,207
# killableprocess - subprocesses which can be reliably killed
#
# Parts of this module are copied from the subprocess.py file contained
# in the Python distribution.
#
# Copyright (c) 2003-2004 by Peter Astrand <astrand@lysator.liu.se>
#
# Additions and modifications written by Benjamin Smedberg
# <benjamin@smedbergs.us> are Copyright (c) 2006 by the Mozilla Foundation
# <http://www.mozilla.org/>
#
# By obtaining, using, and/or copying this software and/or its
# associated documentation, you agree that you have read, understood,
# and will comply with the following terms and conditions:
#
# Permission to use, copy, modify, and distribute this software and
# its associated documentation for any purpose and without fee is
# hereby granted, provided that the above copyright notice appears in
# all copies, and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of the
# author not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
"""killableprocess - Subprocesses which can be reliably killed
 
This module is a subclass of the builtin "subprocess" module. It allows
processes that launch subprocesses to be reliably killed on Windows (via the Popen.kill() method.
 
It also adds a timeout argument to Wait() for a limited period of time before
forcefully killing the process.
 
Note: On Windows, this module requires Windows 2000 or higher (no support for
Windows 95, 98, or NT 4.0). It also requires ctypes, which is bundled with
Python 2.5+ or available from http://python.net/crew/theller/ctypes/
"""
 
import subprocess
import sys
import os
import time
import types
 
try:
from subprocess import CalledProcessError
except ImportError:
# Python 2.4 doesn't implement CalledProcessError
class CalledProcessError(Exception):
"""This exception is raised when a process run by check_call() returns
a non-zero exit status. The exit status will be stored in the
returncode attribute."""
def __init__(self, returncode, cmd):
self.returncode = returncode
self.cmd = cmd
def __str__(self):
return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
 
mswindows = (sys.platform == "win32")
 
if mswindows:
import winprocess
else:
import signal
 
def call(*args, **kwargs):
waitargs = {}
if "timeout" in kwargs:
waitargs["timeout"] = kwargs.pop("timeout")
 
return Popen(*args, **kwargs).wait(**waitargs)
 
def check_call(*args, **kwargs):
"""Call a program with an optional timeout. If the program has a non-zero
exit status, raises a CalledProcessError."""
 
retcode = call(*args, **kwargs)
if retcode:
cmd = kwargs.get("args")
if cmd is None:
cmd = args[0]
raise CalledProcessError(retcode, cmd)
 
if not mswindows:
def DoNothing(*args):
pass
 
class Popen(subprocess.Popen):
if mswindows:
def _execute_child(self, args, executable, preexec_fn, close_fds,
cwd, env, universal_newlines, startupinfo,
creationflags, shell,
p2cread, p2cwrite,
c2pread, c2pwrite,
errread, errwrite):
if not isinstance(args, types.StringTypes):
args = subprocess.list2cmdline(args)
 
if startupinfo is None:
startupinfo = winprocess.STARTUPINFO()
 
if None not in (p2cread, c2pwrite, errwrite):
startupinfo.dwFlags |= winprocess.STARTF_USESTDHANDLES
 
startupinfo.hStdInput = int(p2cread)
startupinfo.hStdOutput = int(c2pwrite)
startupinfo.hStdError = int(errwrite)
if shell:
startupinfo.dwFlags |= winprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = winprocess.SW_HIDE
comspec = os.environ.get("COMSPEC", "cmd.exe")
args = comspec + " /c " + args
 
# We create a new job for this process, so that we can kill
# the process and any sub-processes
self._job = winprocess.CreateJobObject()
 
creationflags |= winprocess.CREATE_SUSPENDED
creationflags |= winprocess.CREATE_UNICODE_ENVIRONMENT
 
hp, ht, pid, tid = winprocess.CreateProcess(
executable, args,
None, None, # No special security
1, # Must inherit handles!
creationflags,
winprocess.EnvironmentBlock(env),
cwd, startupinfo)
 
self._child_created = True
self._handle = hp
self._thread = ht
self.pid = pid
 
winprocess.AssignProcessToJobObject(self._job, hp)
winprocess.ResumeThread(ht)
 
if p2cread is not None:
p2cread.Close()
if c2pwrite is not None:
c2pwrite.Close()
if errwrite is not None:
errwrite.Close()
 
def kill(self, group=True):
"""Kill the process. If group=True, all sub-processes will also be killed."""
if mswindows:
if group:
winprocess.TerminateJobObject(self._job, 127)
else:
winprocess.TerminateProcess(self._handle, 127)
self.returncode = 127
else:
if group:
os.killpg(self.pid, signal.SIGKILL)
else:
os.kill(self.pid, signal.SIGKILL)
self.returncode = -9
 
def wait(self, timeout=-1, group=True):
"""Wait for the process to terminate. Returns returncode attribute.
If timeout seconds are reached and the process has not terminated,
it will be forcefully killed. If timeout is -1, wait will not
time out."""
 
if self.returncode is not None:
return self.returncode
 
if mswindows:
if timeout != -1:
timeout = timeout * 1000
rc = winprocess.WaitForSingleObject(self._handle, timeout)
if rc == winprocess.WAIT_TIMEOUT:
self.kill(group)
else:
self.returncode = winprocess.GetExitCodeProcess(self._handle)
else:
if timeout == -1:
subprocess.Popen.wait(self)
return self.returncode
 
starttime = time.time()
 
# Make sure there is a signal handler for SIGCHLD installed
oldsignal = signal.signal(signal.SIGCHLD, DoNothing)
 
while time.time() < starttime + timeout - 0.01:
pid, sts = os.waitpid(self.pid, os.WNOHANG)
if pid != 0:
self._handle_exitstatus(sts)
signal.signal(signal.SIGCHLD, oldsignal)
return self.returncode
 
# time.sleep is interrupted by signals (good!)
newtimeout = timeout - time.time() + starttime
time.sleep(newtimeout)
self.kill(group)
signal.signal(signal.SIGCHLD, oldsignal)
subprocess.Popen.wait(self)
 
return self.returncode
Property changes : Added: svn:mime-type + text/plain Added: svn:eol-style + native
dot-two-refactor/windmill/browser/__init__.py New file
0,0 → 1,32
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
import windmill
import webbrowser
 
windmill.browser_registry = {}
 
def get_firefox_controller():
 
import firefox
profile = firefox.MozillaProfile()
 
browser = firefox.MozillaBrowser(profile)
return browser
 
def get_ie_controller():
import ie
browser = ie.InternetExplorer()
 
return browser
\ No newline at end of file
dot-two-refactor/windmill/browser/firefox.py New file
0,0 → 1,188
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
import windmill
import exceptions
import os, sys, shutil, time, signal
import killableprocess
import logging
import urlparse
 
logger = logging.getLogger(__name__)
 
os.environ['MOZ_NO_REMOTE'] = str(1)
PROXY_PORT = windmill.settings['SERVER_HTTP_PORT']
 
# Deal with queries in urls
url_split = windmill.settings['TEST_URL'].split('?')
if len(url_split) > 1:
DEFAULT_TEST_URL = url_split[0]+'/windmill-serv/start.html'+'?'+url_split[1]
else:
DEFAULT_TEST_URL = windmill.settings['TEST_URL']+'/windmill-serv/start.html'
 
MOZILLA_PROFILE_PATH = windmill.settings['MOZILLA_PROFILE_PATH']
MOZILLA_DEFAULT_PROFILE = windmill.settings['MOZILLA_DEFAULT_PROFILE']
 
def setpgid_preexec_fn():
os.setpgid(0, 0)
 
def runCommand(cmd):
if sys.platform != "win32":
return killableprocess.Popen(cmd, preexec_fn=setpgid_preexec_fn)
else:
return killableprocess.Popen(cmd)
 
class MozillaProfile(object):
 
def __init__(self, path=MOZILLA_PROFILE_PATH, default_profile=MOZILLA_DEFAULT_PROFILE,
proxy_port=PROXY_PORT, test_url=DEFAULT_TEST_URL):
"""Create profile dir, and populate it"""
 
self.proxy_host = 'localhost'
self.proxy_port = proxy_port
self.test_url = test_url
 
self.profile_path = path
 
if windmill.settings['MOZILLA_CREATE_NEW_PROFILE']:
if os.path.exists(self.profile_path) is True:
shutil.rmtree(self.profile_path)
 
shutil.copytree(default_profile, self.profile_path)
 
self.prefs_js_filename = self.profile_path + '/prefs.js'
self.prefs_js_f = open( self.prefs_js_filename, 'w')
self.initial_prefs()
 
 
def initial_prefs(self):
"""Initial prefs population, separated form __init__ for ease of subclassing"""
 
# Get rid of default browser check
self.user_pref('"browser.shell.checkDefaultBrowser", false')
# Suppress authentication confirmations
self.user_pref('"network.http.phishy-userpass-length", 255')
# Disable pop-up blocking
self.user_pref('"browser.allowpopups", true')
self.user_pref('"dom.disable_open_during_load", false')
# Open links in new windows (Firefox 2.0)
self.user_pref('"browser.link.open_external", 2')
self.user_pref('"browser.link.open_newwindow", 2')
# Configure local proxy
self.user_pref('"network.proxy.http", "%s"' % self.proxy_host)
self.user_pref('"network.proxy.http_port", %s' % str(self.proxy_port))
self.user_pref('"network.proxy.no_proxies_on", ""')
self.user_pref('"network.proxy.type", 1')
 
self.user_pref('"network.http.max-connections", 40')
self.user_pref('"network.http.max-connections-per-server", 16')
self.user_pref('"network.http.max-persistent-connections-per-proxy", 12')
self.user_pref('"network.http.max-persistent-connections-per-server", 6')
self.user_pref('"network.http.pipelining.maxrequests", 6')
 
# Turn off favicon requests, no need for even more requests
self.user_pref('"browser.chrome.favicons", false')
 
self.user_pref('"startup.homepage_override_url", "' + self.test_url + '"')
self.user_pref('"browser.startup.homepage", "' + self.test_url + '"')
self.user_pref('"startup.homepage_welcome_url", ""')
# Disable security warnings
self.user_pref('"security.warn_submit_insecure", false')
self.user_pref('"security.warn_submit_insecure.show_once", false')
self.user_pref('"security.warn_entering_secure", false')
self.user_pref('"security.warn_entering_secure.show_once", false')
self.user_pref('"security.warn_entering_weak", false')
self.user_pref('"security.warn_entering_weak.show_once", false')
self.user_pref('"security.warn_leaving_secure", false')
self.user_pref('"security.warn_leaving_secure.show_once", false')
self.user_pref('"security.warn_viewing_mixed", false')
self.user_pref('"security.warn_viewing_mixed.show_once", false')
# Disable cache
self.user_pref('"browser.cache.disk.enable", false')
self.user_pref('"browser.cache.memory.enable", false')
# Disable "do you want to remember this password?"
self.user_pref('"signon.rememberSignons", false')
 
 
def user_pref(self, string):
self.prefs_js_f.write('user_pref(' + string + ');\n')
self.prefs_js_f.flush()
 
def add_js(self, string):
self.prefs_js_f.write(string + '\n')
self.prefs_js_f.flush()
 
def clean_up(self):
shutil.rmtree(self.profile_path)
 
 
MOZILLA_BINARY = windmill.settings['MOZILLA_BINARY']
 
def convertPath(linuxPath):
sysdrive = os.environ.get('SYSTEMDRIVE')
cygdrive = '/cygdrive/%s' % sysdrive.lower().replace(':', '')
 
return linuxPath.replace(cygdrive, '%s' % sysdrive).replace('/', '\\')
 
class MozillaBrowser(object):
"""MozillaBrowser class, init requires MozillaProfile instance"""
def __init__(self, profile, mozilla_bin=MOZILLA_BINARY):
 
self.profile = profile
self.mozilla_bin = mozilla_bin
self.p_handle = None
 
if sys.platform == 'cygwin':
profile_path = convertPath(self.profile.profile_path)
else:
profile_path = self.profile.profile_path
 
self.command = [self.mozilla_bin, '-profile', profile_path]
 
def start(self):
 
self.p_handle = runCommand(self.command)
 
logger.info(self.command)
 
def is_alive(self):
 
if self.p_handle.poll() is None:
return False
 
try:
self.p_handle.kill(group=True)
return True
except exceptions.OSError:
return False
 
def kill(self, kill_signal):
 
if sys.platform == 'darwin':
try:
os.kill(self.p_handle.pid+1, kill_signal)
except:
logger.error('Cannot kill firefox')
else:
try:
self.p_handle.kill(group=True)
except:
logger.error('Cannot kill firefox')
 
def stop(self):
 
self.kill(signal.SIGTERM)
 
 
 
dot-two-refactor/windmill/conf/__init__.py New file
0,0 → 1,28
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
import global_settings
 
def configure_settings(local_settings=object(), windmill_settings={}):
"""Override global settings with any locals and configure the windmill_settings dict."""
for setting in dir(global_settings):
if not setting.startswith('__'):
windmill_settings[setting] = getattr(global_settings, setting)
for setting in dir(local_settings):
if not setting.startswith('__'):
windmill_settings[setting] = getattr(local_settings, setting)
 
import windmill
windmill.settings = windmill_settings
return windmill_settings
\ No newline at end of file
dot-two-refactor/windmill/conf/global_settings.py New file
0,0 → 1,71
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
import logging, os, sys, tempfile, shutil
 
PLATFORM = sys.platform
 
TEST_URL = 'http://www.google.com'
 
SERVER_HTTP_PORT = 4444
 
CONSOLE_LOG_LEVEL = logging.INFO
FILE_LOG_LEVEL = logging.INFO
 
JS_PATH = os.path.dirname(sys.modules['windmill'].__file__)+os.path.sep+'js'
 
TEST_FILE = None
TEST_DIR = None
EXIT_ON_DONE = False
 
START_FIREFOX = False
 
CONTINUE_ON_FAILURE = False
 
# Browser prefs
 
# Mozilla prefs
MOZILLA_PROFILE_PATH = tempfile.mkdtemp()
if MOZILLA_PROFILE_PATH.find('-') is not -1:
shutil.rmtree(MOZILLA_PROFILE_PATH)
MOZILLA_PROFILE_PATH = tempfile.mkdtemp()
MOZILLA_CREATE_NEW_PROFILE = True
MOZILLA_REMOVE_PROFILE_ON_EXIT = True
 
if sys.platform == 'darwin':
if os.path.isdir(os.path.expanduser('~/Applications/Firefox.app/')):
MOZILLA_DEFAULT_PROFILE = os.path.expanduser('~/Applications/Firefox.app/Contents/MacOS/defaults/profile/')
MOZILLA_BINARY = os.path.expanduser('~/Applications/Firefox.app/Contents/MacOS/firefox-bin')
elif os.path.isdir('/Applications/Firefox.app/'):
MOZILLA_DEFAULT_PROFILE = '/Applications/Firefox.app/Contents/MacOS/defaults/profile/'
MOZILLA_BINARY = '/Applications/Firefox.app/Contents/MacOS/firefox-bin'
 
elif sys.platform == 'linux2':
if os.path.isfile('/usr/bin/firefox'):
MOZILLA_BINARY = '/usr/bin/firefox'
 
if os.path.isdir('/usr/share/firefox/defaults/profile'):
MOZILLA_DEFAULT_PROFILE = '/usr/share/firefox/defaults/profile'
 
elif sys.platform == 'cygwin':
if os.path.isfile('/cygdrive/c/Program Files/Mozilla Firefox/firefox.exe'):
MOZILLA_BINARY = '/cygdrive/c/Program\\ Files/Mozilla\\ Firefox/firefox.exe'
 
if os.path.isdir('/cygdrive/c/Program Files/Mozilla Firefox/defaults/profile'):
MOZILLA_DEFAULT_PROFILE = '/cygdrive/c/Program Files/Mozilla Firefox/defaults/profile'
 
elif sys.platform == 'win32':
MOZILLA_BINARY = "C:\\Program Files\\Mozilla Firefox\\firefox.exe"
MOZILLA_DEFAULT_PROFILE = "C:\Program Files\Mozilla Firefox\defaults\profile"
IE_BINARY = "C:\\Program Files\\Internet Explorer\\iexplore.exe"
dot-two-refactor/windmill/__init__.py New file
0,0 → 1,15
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
import bin, browser, server, conf, test, tools
\ No newline at end of file
dot-two-refactor/windmill/server/wsgi.py New file
0,0 → 1,330
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
from urlparse import urlparse, urljoin
import httplib, os.path, copy, time, socket, logging, sys, traceback
from StringIO import StringIO
 
import jsonrpc, logger
import windmill
 
import logging
logger = logging.getLogger('server.wsgi')
 
PORT = 4444
 
# wsgiref.utils.is_hop_by_hop doesn't pick up proxy-connection so we need to write our own
_hoppish = {
'connection':1, 'keep-alive':1, 'proxy-authenticate':1,
'proxy-authorization':1, 'te':1, 'trailers':1, 'transfer-encoding':1,
'upgrade':1, 'proxy-connection':1 }
 
def is_hop_by_hop(header):
return _hoppish.has_key(header.lower())
 
def reconstruct_url(environ):
# From WSGI spec, PEP 333
from urllib import quote
url = environ['wsgi.url_scheme']+'://'
 
if environ.get('HTTP_HOST'):
url += environ['HTTP_HOST']
else:
url += environ['SERVER_NAME']
 
if environ['wsgi.url_scheme'] == 'https':
if environ['SERVER_PORT'] != '443':
url += ':' + environ['SERVER_PORT']
else:
if environ['SERVER_PORT'] != '80':
url += ':' + environ['SERVER_PORT']
 
url += quote(environ.get('SCRIPT_NAME',''))
url += quote(environ.get('PATH_INFO',''))
if environ.get('QUERY_STRING'):
url += '?' + environ['QUERY_STRING']
 
environ['reconstructed_url'] = url
 
return url
 
 
class WindmillServApplication(object):
"""Application to serve out windmill provided"""
 
def __init__(self, logger, js_path):
self.path = js_path
self.logger = logger
 
def handler(self, environ, start_response):
"""Application to serve out windmill provided"""
url = urlparse(reconstruct_url(environ))
split_url = url.path.split('/windmill-serv/')
serve_file = split_url[1]
 
#Open file
try:
f = open('%s/%s' % (self.path, serve_file), 'r')
self.logger.debug('opened file %s' % serve_file)
except:
self.logger.error('failed to open file %s/%s' % (self.path, serve_file))
start_response('404 Not found', [('Content-Type', 'text/plain')])
return ['404 Not Found']
content_type = self.guess_content_type(environ['PATH_INFO'])
start_response('200 OK', [('Cache-Control','no-cache'), ('Pragma','no-cache'),
('Content-Type', content_type)])
return [f.read()]
 
def guess_content_type(self, path_info):
if path_info.endswith('.js'):
return 'application/x-javascript'
elif path_info.endswith('.html'):
return 'text/html'
elif path_info.endswith('.css'):
return 'text/css'
else:
return 'text/plain'
 
def __call__(self, environ, start_response):
return self.handler(environ, start_response)
 
 
class WindmillJSONRPCApplication(object):
"""Application to handle requests to the JSONRPC service"""
 
def __init__(self, json_methods_instance, logger):
"""Create windmill jsonrpc dispatcher"""
self.jsonrpc_dispatcher = jsonrpc.WSGIJSONRPCDispatcher(instance=json_methods_instance)
self.logger = logger
 
def handler(self, environ, start_response):
"""JSONRPC service for windmill browser core to communicate with"""
 
return self.jsonrpc_dispatcher.handler(environ, start_response)
 
def __call__(self, environ, start_response):
return self.handler(environ, start_response)
 
 
class WindmillXMLRPCApplication(object):
"""Application to handle requests to the XMLRPC service"""
 
def __init__(self, xmlrpc_methods_instance, logger):
"""Create windmill xmlrpc dispatcher"""
from SimpleXMLRPCServer import SimpleXMLRPCDispatcher
self.dispatcher = SimpleXMLRPCDispatcher(allow_none=False, encoding=None)
self.dispatcher.register_instance(xmlrpc_methods_instance)
self.dispatcher.register_introspection_functions()
self.logger = logger
 
def handler(self, environ, start_response):
"""XMLRPC service for windmill browser core to communicate with"""
 
if environ['REQUEST_METHOD'] == 'POST':
return self.handle_POST(environ, start_response)
else:
start_response("400 Bad request", [('Content-Type','text/plain')])
return ['']
 
def handle_POST(self, environ, start_response):
"""Handles the HTTP POST request.
 
Attempts to interpret all HTTP POST requests as XML-RPC calls,
which are forwarded to the server's _dispatch method for handling.
 
Most code taken from SimpleXMLRPCServer with modifications for wsgi and my custom dispatcher.
"""
 
try:
# Get arguments by reading body of request.
# We read this in chunks to avoid straining
# socket.read(); around the 10 or 15Mb mark, some platforms
# begin to have problems (bug #792570).
 
length = int(environ['CONTENT_LENGTH'])
data = environ['wsgi.input'].read(length)
 
max_chunk_size = 10*1024*1024
size_remaining = length
 
# In previous versions of SimpleXMLRPCServer, _dispatch
# could be overridden in this class, instead of in
# SimpleXMLRPCDispatcher. To maintain backwards compatibility,
# check to see if a subclass implements _dispatch and
# using that method if present.
response = self.dispatcher._marshaled_dispatch(
data, getattr(self.dispatcher, '_dispatch', None)
)
except: # This should only happen if the module is buggy
# internal error, report as HTTP server error
start_response("500 Server error", [('Content-Type', 'text/plain')])
return []
else:
# got a valid XML RPC response
start_response("200 OK", [('Content-Type','text/xml')])
return [response]
 
 
def __call__(self, environ, start_response):
return self.handler(environ, start_response)
 
 
class WindmillProxyApplication(object):
"""Application to handle requests that need to be proxied"""
 
def __init__(self, logger):
self.logger = logger
 
ConnectionClass = httplib.HTTPConnection
 
def handler(self, environ, start_response):
"""Proxy for requests to the actual http server"""
url = urlparse(environ['reconstructed_url'])
 
# Create connection object
try:
connection = self.ConnectionClass(url.netloc)
# Build path
path = url.geturl().replace('%s://%s' % (url.scheme, url.netloc), '')
except:
start_response("501 Gateway error", [('Content-Type', 'text/html')])
return ['<H1>Could not connect</H1>']
 
 
# Read in request body if it exists
body = None
if environ.get('CONTENT_LENGTH'):
length = int(environ['CONTENT_LENGTH'])
body = environ['wsgi.input'].read(length)
 
 
# Build headers
headers = {}
self.logger.debug('Environ ; %s' % str(environ))
for key in environ.keys():
# Keys that start with HTTP_ are all headers
if key.startswith('HTTP_'):
# This is a hacky way of getting the header names right
value = environ[key]
key = key.replace('HTTP_', '', 1).swapcase().replace('_', '-')
if is_hop_by_hop(key) is False:
headers[key] = value
 
# Handler headers that aren't HTTP_ in environ
if environ.get('CONTENT_TYPE'):
headers['content-type'] = environ['CONTENT_TYPE']
 
# Add our host if one isn't defined
if not headers.has_key('host'):
headers['host'] = environ['SERVER_NAME']
 
# Make the remote request
try:
self.logger.debug('%s %s %s' % (environ['REQUEST_METHOD'], path, str(headers)))
connection.request(environ['REQUEST_METHOD'], path, body=body, headers=headers)
except:
# We need exception handling in the case the server fails, it's an edge case but I've seen it
start_response("501 Gateway error", [('Content-Type', 'text/html')])
return ['<H1>Could not connect</H1>']
 
response = connection.getresponse()
 
hopped_headers = response.getheaders()
headers = copy.copy(hopped_headers)
for header in hopped_headers:
if is_hop_by_hop(header[0]):
headers.remove(header)
 
start_response(response.status.__str__()+' '+response.reason, headers)
 
 
# Return the proper wsgi response
# response_body = response.read()
# 737019
return [response.read(response.length)]
 
 
def __call__(self, environ, start_response):
return self.handler(environ, start_response)
 
class WindmillSSLProxyApplication(WindmillProxyApplication):
 
ConnectionClass = httplib.HTTPSConnection
 
class WindmillChooserApplication(object):
"""Application to handle choosing the proper application to handle each request"""
def __init__(self, windmill_serv_app, windmill_jsonrpc_app, windmill_xmlrpc_app, windmill_proxy_app, logger):
self.windmill_serv_app = windmill_serv_app
self.windmill_jsonrpc_app = windmill_jsonrpc_app
self.windmill_xmlrpc_app = windmill_xmlrpc_app
self.windmill_proxy_app = windmill_proxy_app
self.logger = logger
 
def handler(self, environ, start_response):
"""Windmill app chooser"""
 
reconstruct_url(environ)
 
if environ['PATH_INFO'].find('/windmill-serv/') is not -1:
self.logger.debug('dispatching request %s to WindmillServApplication' % environ['reconstructed_url'])
return self.windmill_serv_app(environ, start_response)
elif environ['PATH_INFO'].find('/windmill-jsonrpc/') is not -1:
self.logger.debug('dispatching request %s to WindmillJSONRPCApplication' % environ['reconstructed_url'])
return self.windmill_jsonrpc_app(environ, start_response)
elif environ['PATH_INFO'].find('/windmill-xmlrpc/') is not -1:
self.logger.debug('dispatching request %s to WindmillXMLRPCApplication' % environ['reconstructed_url'])
return self.windmill_xmlrpc_app(environ, start_response)
else:
self.logger.debug('dispatching request %s to WindmillProxyApplication' % reconstruct_url(environ))
return self.windmill_proxy_app(environ, start_response)
 
def __call__(self, environ, start_response):
return self.handler(environ, start_response)
 
 
def make_windmill_server(http_port=None, js_path=None):
 
if http_port is None:
http_port = windmill.settings['SERVER_HTTP_PORT']
if js_path is None:
js_path = windmill.settings['JS_PATH']
 
import convergence
queue = convergence.ControllerQueue()
test_resolution_suite = convergence.TestResolutionSuite()
command_resolution_suite = convergence.CommandResolutionSuite()
xmlrpc_methods_instance = convergence.XMLRPCMethods(queue, test_resolution_suite, command_resolution_suite)
jsonrpc_methods_instance = convergence.JSONRPCMethods(queue, test_resolution_suite, command_resolution_suite)
 
windmill_serv_app = WindmillServApplication(logger=logging.getLogger('server.serv'), js_path=js_path)
windmill_proxy_app = WindmillProxyApplication(logger=logging.getLogger('server.proxy'))
windmill_xmlrpc_app = WindmillXMLRPCApplication(xmlrpc_methods_instance, logger=logging.getLogger('server.xmlrpc'))
windmill_jsonrpc_app = WindmillJSONRPCApplication(jsonrpc_methods_instance,
logger=logging.getLogger('server.jsonrpc'))
windmill_chooser_app = WindmillChooserApplication(windmill_serv_app, windmill_jsonrpc_app,
windmill_xmlrpc_app, windmill_proxy_app,
logger=logging.getLogger('server.chooser'))
 
 
import cherrypy
httpd = cherrypy.wsgiserver.CherryPyWSGIServer(('', http_port), windmill_chooser_app, server_name='windmill-http')
 
httpd.controller_queue = queue
httpd.test_resolution_suite = test_resolution_suite
httpd.command_resolution_suite = command_resolution_suite
httpd.xmlrpc_methods_instance = xmlrpc_methods_instance
httpd.jsonrpc_methods_instance = jsonrpc_methods_instance
 
return httpd
 
dot-two-refactor/windmill/server/convergence.py New file
0,0 → 1,244
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
"""This module provides the communication and management between the various
server interfaces and the browser's js interface"""
 
import copy
import simplejson
import logging
import uuid
import windmill
from dateutil.parser import parse as dateutil_parse
 
test_results_logger = logging.getLogger('test_results')
 
class ControllerQueue(object):
 
def __init__(self):
 
self.command_queue = []
self.test_queue = []
 
def add_command(self, command):
 
self.command_queue.append(command)
 
def add_test(self, test):
 
self.test_queue.append(test)
 
def command(self, command):
 
self.command_queue.insert(0, command)
 
def next_action(self):
 
if len(self.command_queue) is not 0:
return self.command_queue.pop(0)
elif len(self.test_queue) is not 0:
return self.test_queue.pop(0)
else:
return None
 
callback = {'version':'0.1'}
 
class TestResolutionSuite(object):
"""Collection of tests run and results"""
result_processor = None
 
def __init__(self):
self.unresolved_tests = {}
self.resolved_tests = {}
 
def resolve_test(self, result, uuid, starttime, endtime, debug=None):
"""Resolve test by uuid"""
starttime = dateutil_parse(starttime)
endtime = dateutil_parse(endtime)
test = self.unresolved_tests.pop(uuid)
test['result'] = result
test['starttime'] = starttime
test['endtime'] = endtime
test['totaltime'] = endtime - starttime
self.resolved_tests[uuid] = test
 
if result is False:
test_results_logger.error('Test Failue in test %s' % test)
elif result is True:
test_results_logger.debug('Test Success in test %s' % test)
 
if self.result_processor is not None:
if result is False:
self.result_processor.failure(test, debug=debug)
elif result is True:
self.result_processor.success(test, debug=debug)
 
if test.has_key('result_callback'):
test['result_callback'](result, debug)
 
def add_test(self, test):
self.unresolved_tests[test['params']['uuid']] = test
 
class CommandResolutionSuite(object):
 
def __init__(self):
self.unresolved_commands = {}
self.resolved_commands ={}
 
def resolve_command(self, status, uuid, result):
"""Resolve command by uuid"""
command = self.unresolved_commands.pop(uuid)
command['status'] = status
command['result'] = result
self.resolved_commands[uuid] = command
 
if status is False:
test_results_logger.error('Command Failure in command %s' % command)
elif status is True:
test_results_logger.debug('Command Succes in command %s' % command)
 
if command.has_key('result_callback'):
command['result_callback'](status, result)
 
def add_command(self, command):
self.unresolved_commands[command['params']['uuid']] = command
 
class RPCMethods(object):
 
def __init__(self, queue, test_resolution_suite, command_resolution_suite):
self._queue = queue
self._logger = logging.getLogger('jsonrpc_methods_instance')
self._test_resolution_suite = test_resolution_suite
self._command_resolution_suite = command_resolution_suite
 
def add_object(self, queue_method, resolution_suite, action_object):
callback_object = copy.copy(callback)
callback_object.update(action_object)
callback_object['params']['uuid'] = str(uuid.uuid1())
self._logger.debug('Adding object %s' % str(callback_object))
queue_method(callback_object)
resolution_suite.add_test(callback_object)
 
def add_json_test(self, json):
"""Add test from json object with 'method' and 'params' defined"""
action_object = simplejson.loads(json)
self.add_object(self._queue.add_test, self._test_resolution_suite, action_object)
 
def add_test(self, test_object):
self.add_object(self._queue.add_test, self._test_resolution_suite, test_object)
 
def add_json_command(self, json):
"""Add command from json object with 'method' and 'params' defined"""
action_object = simplejson.loads(json)
self.add_object(self._queue.add_command, self._command_resolution_suite, action_object)
 
def add_command(self, command_object):
self.add_object(self._queue.add_command, self._command_resolution_suite, command_object)
 
def execute_object(self, queue_method, resolution_suite, action_object):
callback_object = copy.copy(callback)
callback_object.update(action_object)
callback_object['params']['uuid'] = str(uuid.uuid1())
self._logger.debug('Adding command object %s' % str(callback_object))
 
returned_result = None
def result_callback(status, result):
if status is True:
returned_result = result
else:
returned_result = False
 
callback_object['result_callback'] = result_callback
queue_method(callback_object)
resolution_suite.add_command(callback_object)
 
while returned_result is None:
pass
return returned_result
 
def execute_json_command(self, json):
"""Add command from json object with 'method' and 'params' defined"""
action_object = simplejson.loads(json)
self.execute_object(self._queue.add_command, self._command_resolution_suite, action_object)
 
def execute_json_test(self, json):
"""Add test from json object with 'method' and 'params' defined"""
action_object = simplejson.loads(json)
self.execute_object(self._queue.add_test, self._test_resolution_suite, action_object)
 
def execute_command(self, action_object):
self.execute_object(self._queue.add_command, self._command_resolution_suite, action_object)
 
def execute_test(self, action_object):
self.execute_object(self._queue.add_test, self._test_resolution_suite, action_object)
 
def run_json_tests(self, tests):
for test in tests:
self.add_json_test(test)
 
def run_tests(self, tests):
for test in tests:
self.add_test(test)
 
 
class JSONRPCMethods(RPCMethods):
 
def next_action(self):
"""The next action for the browser to execute"""
action = self._queue.next_action()
if action is not None:
self._logger.debug('queue has next_action %s' % str(action))
return action
else:
self._logger.debug('queue has no next_action, returning "pause" method')
action = copy.copy(callback)
action.update({'method':'defer'})
return action
 
def report(self, uuid, result, starttime, endtime, debug=None):
"""Report fass/fail for a test"""
self._test_resolution_suite.resolve_test(result, uuid, starttime, endtime, debug)
 
def command_result(self, status, uuid, result):
self._command_resolution_suite.resolve_command(status, uuid, result)
 
def status_change(self, status):
pass
 
def clear_queue(self):
"""Clear the server queue"""
self._queue.command_queue = []
self._queue.test_queue = []
 
class XMLRPCMethods(RPCMethods):
 
def __getattr__(self, key):
"""Call a method on the controller as if it was a local method"""
class ExecuteJSONTestRecursiveAttribute(object):
def __init__(self, name, execution_method):
self.name = name
self.execution_method = execution_method
def __call__(self, **kwargs):
rpc = {'method':self.name, 'params':kwargs}
self.execution_method(simplejson.dumps(rpc))
def __getattr__(self, key):
return ExecuteJSONTestRecursiveAttribute(self.name+'.'+key, self.execution_method)
 
return ExecuteJSONTestRecursiveAttribute(key, self.execute_json_test)
 
 
 
 
 
 
\ No newline at end of file
dot-two-refactor/windmill/server/jsonrpc.py New file
0,0 → 1,241
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
from datetime import datetime
from StringIO import StringIO
import simplejson
import sys, traceback
import logging
 
logger = logging.getLogger('server.jsonrpc')
 
_descriptions = set(['summary', 'help', 'idempotent', 'params', 'return'])
 
def describe_method(method):
"""Check is a callable object has description params set"""
description = {}
for key in _descriptions:
if hasattr(method, key):
description[key] = getattr(method, key)
return description
 
class JSONRPCError(Exception):
"""JSONRPC Error"""
 
class JSONRPCDispatcher(object):
 
def __init__(self, instance=None, name='Python JSONRPC Service', summary='Service dispatched by python JSONRPCDispatcher', help=None, address=None):
"""Initialization. Can take an instance to register upon initialization"""
self.instances = []
self.name = name
self.help = help
self.address = address
self.summary = summary
 
# Store all attributes of class before any methods are added for negative lookup later
self.base_attributes = set(dir(self))
self.base_attributes.add('base_attributes')
 
# If instance was given during initialization then register it
if instance is not None:
self.register_instance(instance)
 
self.__dict__['system.list_methods'] = self.system_list_methods
self.__dict__['system.describe'] = self.system_describe
 
def get_valid_methods(self):
valid_methods = {}
for key, value in self.__dict__.items():
if key.startswith('_') is False:
if key not in self.base_attributes:
valid_methods[key] = value
return valid_methods
 
def register_instance(self, instance):
"""Registers all attributes of class instance to dispatcher"""
for attribute in dir(instance):
if attribute.startswith('_') is False:
# All public attributes
self.register_method(getattr(instance, attribute), name=attribute)
 
# Store it in the list for convienience
self.instances.append(instance)
 
 
def register_method(self, function, name=None):
"""Registers a method with the dispatcher"""
# If the name isn't passed try to find it. If we can't fail gracefully.
if name is None:
try:
name = function.__name__
except:
if hasattr(function, __call__):
raise """Callable class instances must be passed with name parameter"""
else:
raise """Not a function"""
 
if self.__dict__.has_key(name) is False:
self.__dict__[unicode(name)] = function
else:
print 'Attribute name conflict -- %s must be removed before attribute of the same name can be added'
 
 
def system_list_methods(self):
"""List all the available methods and return a object parsable that conforms to the JSONRPC Service Procedure Description specification"""
method_list = []
for key, value in self.get_valid_methods().items():
method = {}
method['name'] = key
method.update(describe_method(value))
method_list.append(method)
method_list.sort()
logger.debug('system.list_methods created list %s' % str(method_list))
return method_list
 
def system_describe(self):
"""Service description"""
description = {}
description['sdversion'] = '1.0'
description['name'] = self.name
description['summary'] = self.summary
if self.help is not None:
description['help'] = self.help
if self.address is not None:
description['address'] = self.address
description['procs'] = self.system_list_methods()
return description
 
def dispatch(self, json):
"""Public dispatcher, verifies that a method exists in it's method dictionary and calls it"""
rpc_request = self._decode(json)
logger.debug('decoded to python object %s' % str(rpc_request))
 
if self.__dict__.has_key(rpc_request[u'method']):
logger.debug('dispatcher has key %s' % rpc_request[u'method'])
return self._dispatch(rpc_request)
else:
logger.debug('returning jsonrpc error')
return self.encode(result=None, error=JSONRPCError('no such method'), id=rpc_request[u'id'])
 
def _dispatch(self, rpc_request):
"""Internal dispatcher, handles all the error checking and calling of methods"""
result = None
error = None
jsonrpc_id = None
 
# If someone sends an empty json array or object we need to treat it as Null.
# This way we don't expand None during the method call.
if rpc_request[u'params'] is not None and len(rpc_request[u'params']) is 0:
rpc_request[u'params'] = None
 
logged_failure = False
 
try:
# Account for each type
if type(rpc_request[u'params']) is list or type(rpc_request[u'params']) is tuple:
try:
result = self.__dict__[rpc_request[u'method']](*rpc_request[u'params'])
except Exception, e:
if type(rpc_request[u'params'][-1]) is dict:
result = self.__dict__[rpc_request[u'method']](*rpc_request[u'params'], **rpc_request[u'params'][-1])
else:
logger.exception('JSONRPC Dispatcher encountered exception')
logged_failure = True
raise Exception, e
elif type(rpc_request[u'params']) is dict:
ascii_params = {}
[ascii_params.__setitem__(str(key), rpc_request[u'params'][key]) for key in rpc_request[u'params'].keys()]
result = self.__dict__[rpc_request[u'method']](**ascii_params)
elif rpc_request[u'params'] is None:
result = self.__dict__[rpc_request[u'method']]()
else:
# If type was something weird just return a JSONRPC Error
logger.warning('received params type %s ' % type(rpc_request[u'params']))
raise JSONRPCError, 'params not array or object type'
# Turn python errors into JSONRPC errors
except JSONRPCError, e:
logger.exception('JSONRPCError %s' % e)
except Exception, e:
error = JSONRPCError('Server Exception :: %s' % e)
error.type = e.__class__
if logged_failure is False:
logger.exception('JSONRPC Dispatcher encountered exception')
 
if rpc_request.has_key('id'):
jsonrpc_id = rpc_request[u'id']
 
if result is None and error is None:
result = 200
 
return self._encode(result=result, error=error, jsonrpc_id=jsonrpc_id)
 
 
def _encode(self, result=None, error=None, jsonrpc_id=None):
"""Internal encoder method, handles error formatting, id persistence, and encoding via simplejson"""
response = {}
response['result'] = result
if jsonrpc_id is not None:
response['id'] = jsonrpc_id
 
 
if error is not None:
if hasattr(error, 'type'):
error_type = str(error.type)
error_message = str(error)
else:
error_type = 'JSONRPCError'
error_message = str(error).strip('JSONRPC Error in: ')
 
response['error'] = {'type':error_type,
'message':error_message}
logger.debug('serializing %s' % str(response))
return simplejson.dumps(response)
 
def _decode(self, json):
"""Internal method for decoding json objects, uses simplejson"""
 
return simplejson.loads(json)
 
class WSGIJSONRPCDispatcher(JSONRPCDispatcher):
"""A WSGI Application for generic JSONRPC requests."""
 
def handler(self, environ, start_response):
"""A WSGI handler for generic JSONRPC requests."""
 
if environ['REQUEST_METHOD'] == 'POST':
body = None
if environ.get('CONTENT_LENGTH'):
length = int(environ['CONTENT_LENGTH'])
body = environ['wsgi.input'].read(length)
 
try:
logger.debug('Sending %s to dispatcher' % body)
response = self.dispatch(body)
start_response('200 OK', [('Cache-Control','no-cache'), ('Pragma','no-cache'),
('Content-Type', 'application/json')])
return [response]
except Exception, e:
logger.exception('WSGIJSONRPCDispatcher Dispatcher excountered exception')
start_response('500 Internal Server Error', [('Cache-Control','no-cache'), ('Content-Type', 'text/plain')])
return ['500 Internal Server Error']
 
else:
start_response('405 Method Not Allowed', [('Cache-Control','no-cache'), ('Content-Type', 'text/plain')])
return ['405 Method Not Allowed. This JSONRPC interface only supports POST.']
 
def __call__(self, environ, start_response):
return self.handler(environ, start_response)
 
 
 
\ No newline at end of file
dot-two-refactor/windmill/server/__init__.py New file
0,0 → 1,15
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
import jsonrpc, logger, wsgi, convergence
\ No newline at end of file
dot-two-refactor/windmill/server/logger.py New file
0,0 → 1,42
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
import logging
 
def setup_root_logger(console_level=logging.NOTSET, file_level=logging.NOTSET,
file_obj=None):
"""Setup the root logger, console_level and file_level must be levels from the logging module"""
# Set global level to 0 so we can catch anything with our handlers
logging.getLogger().setLevel(0)
 
if file_obj is not None:
logging.basicConfig(level=file_level,
format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
datefmt='%m-%d %H:%M',
stream=file_obj)
 
console = logging.StreamHandler()
console.setLevel(console_level)
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
console.setFormatter(formatter)
 
logging.getLogger('').addHandler(console)
return console
 
def setup_individual_logger(name, level=logging.NOTSET):
 
logger = logging.getLogger(name)
logger.setLevel(0)
return logger
 
\ No newline at end of file
dot-two-refactor/windmill/bin/admin_lib.py New file
0,0 → 1,230
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
import windmill
import logging, time
import os, sys, inspect, shutil
from datetime import datetime
from threading import Thread
 
def process_options(argv_list):
"""Process all the command line options"""
import admin_options
admin_options.process_module(admin_options)
argv_list.pop(0)
 
action = None
 
for index in range(len(argv_list)):
if index <= len(argv_list):
if argv_list[index].startswith('http://'):
windmill.settings['TEST_URL'] = argv_list[index]
elif not argv_list[index].startswith('-'):
if argv_list[index][0].islower():
value = None
if argv_list[index].find('=') is not -1:
name, value = argv_list[index].split('=')
else:
name = argv_list[index]
 
if admin_options.options_dict.has_key(name):
processor = admin_options.options_dict[name]
if value is None:
processor()
else:
processor(value)
elif name in action_mapping.keys():
action = action_mapping[name]
elif argv_list[index][0].isupper():
value = argv_list[index][0]
if value.find('=') is not -1:
name, value = value.split('=')
windmill.settings[name] = value
else:
if windmill.settings[value] is True:
windmill.settings[value] = False
elif windmill.settings[value] is False:
windmill.settings[value] = True
 
elif argv_list[index].startswith('-'):
options = argv_list[index].replace('-')
for option in options:
admin_options.flags_dict[option]()
 
 
 
if action is None:
return action_mapping['runserver']
else:
return action
 
 
def setup_servers(console_level=logging.INFO):
"""Setup the server and return httpd and loggers"""
console_handler = windmill.server.logger.setup_root_logger(console_level=console_level)
httpd = windmill.server.wsgi.make_windmill_server()
return httpd, console_handler
 
def run_threaded(console_level=logging.INFO):
"""Run the server with various values"""
 
httpd, console_handler = setup_servers(console_level)
 
httpd_thread = Thread(target=httpd.start)
httpd_thread.start()
 
time.sleep(1)
return httpd, httpd_thread, console_handler
 
def configure_global_settings():
# Get local config
 
if os.environ.has_key('WINDMILL_CONFIG_FILE'):
sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.expanduser(os.environ['WINDMILL_CONFIG_FILE']))))
local_settings = __import__(os.path.basename(os.environ['WINDMILL_CONFIG_FILE'].split('.')[0]))
sys.path.remove(os.path.abspath(os.path.dirname(os.path.expanduser(os.environ['WINDMILL_CONFIG_FILE']))))
else:
try:
import windmill_settings as local_settings
except:
local_settings = object()
 
windmill.settings = windmill.conf.configure_settings(local_settings)
 
 
 
def setup():
"""Setup server and shell objects"""
from windmill.bin import admin_lib
 
shell_objects_dict = {}
 
httpd, httpd_thread, console_log_handler = admin_lib.run_threaded(windmill.settings['CONSOLE_LOG_LEVEL'])
 
shell_objects_dict['httpd'] = httpd
shell_objects_dict['httpd_thread'] = httpd_thread
 
from windmill.bin import shell_objects
 
if windmill.settings['CONTINUE_ON_FAILURE'] is not False:
jsonrpc_client.add_json_command('{"method": "setOptions", "params": {"stopOnFailure" : false}}')
 
if windmill.settings['TEST_FILE'] is not None:
shell_objects.run_test_file(windmill.settings['TEST_FILE'], shell_objects.jsonrpc_client)
 
if windmill.settings['TEST_DIR'] is not None:
shell_objects.run_given_test_dir()
 
browser = [setting for setting in windmill.settings.keys() if setting.startswith('START_') and \
windmill.settings[setting] is True]
 
import shell_objects
 
if len(browser) is 1:
shell_objects_dict['browser'] = getattr(shell_objects, browser[0].lower())()
 
for attribute in dir(shell_objects):
shell_objects_dict[attribute] = getattr(shell_objects, attribute)
 
windmill.settings['shell_objects'] = shell_objects_dict
return shell_objects_dict
 
 
def teardown(shell_objects):
 
for controller in windmill.settings['controllers']:
controller.stop()
time.sleep(1)
 
if windmill.settings['MOZILLA_REMOVE_PROFILE_ON_EXIT'] is True:
# Windows holds on to the file handlers for prefs.js indefinitely, we leave tempfiles and let the OS handle cleaning them up at a later time
if sys.platform != "win32":
shutil.rmtree(windmill.settings['MOZILLA_PROFILE_PATH'])
 
while shell_objects['httpd_thread'].isAlive():
shell_objects['httpd'].stop()
 
def runserver_action(shell_objects):
 
try:
 
print 'Server running...'
if not windmill.settings['EXIT_ON_DONE']:
while 1:
pass
else:
while len(shell_objects['httpd'].controller_queue.test_queue) is not 0 and \
len(shell_objects['httpd'].test_resolution_suite.unresolved_tests) is not 0:
pass
 
teardown(shell_objects)
 
except KeyboardInterrupt:
teardown(shell_objects)
 
def shell_action(shell_objects):
 
# If ipython is installed and we weren't given the usecode option
try:
from IPython.Shell import IPShellEmbed
ipshell = IPShellEmbed()
 
ipshell(local_ns=shell_objects)
except:
import code
code.interact(local=shell_objects)
 
teardown(shell_objects)
 
def tinderbox_action(shell_objects):
 
shell_objects['jsonrpc_client'].add_json_command('{"method": "setOptions", "params": {"stopOnFailure" : false}}')
 
class ResultsProcessor(object):
passed = 0
failed = 0
def success(self, test, debug):
self.passed += 1
def failure(self, test, debug):
self.failed += 1
 
result_processor = ResultsProcessor()
shell_objects['httpd'].test_resolution_suite.result_processor = result_processor
 
try:
starttime = datetime.now()
while len(shell_objects['httpd'].controller_queue.test_queue) is not 0 and \
len(shell_objects['httpd'].test_resolution_suite.unresolved_tests) is not 0:
pass
 
print '#TINDERBOX# Testname = FullSuite'
print '#TINDERBOX# Time elapsed = %s' % str (datetime.now() - starttime)
 
if result_processor.failed > 0 or result_processor.passed is 0:
result = "FAILED"
else:
result = "PASSED"
 
print '#TINDERBOX# Status = %s' % result
teardown(shell_objects)
if result == "FAILED":
sys.exit(1)
 
except KeyboardInterrupt:
teardown(shell_objects)
if result == "FAILED":
sys.exit(1)
 
action_mapping = {'shell':shell_action, 'runserver':runserver_action, 'tbox':tinderbox_action}
 
dot-two-refactor/windmill/bin/__init__.py New file
0,0 → 1,13
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
dot-two-refactor/windmill/bin/shell_objects.py New file
0,0 → 1,64
#!/usr/bin/env python
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
import windmill
import sys, os, logging
 
logger = logging.getLogger(__name__)
 
jsonrpc_client = windmill.tools.make_jsonrpc_client()
xmlrpc_client = windmill.tools.make_xmlrpc_client()
 
def clear_queue():
jsonrpc_client.clear_queue()
 
windmill.settings['controllers'] = []
 
def start_firefox():
controller = windmill.browser.get_firefox_controller()
controller.start()
windmill.settings['controllers'].append(controller)
return controller
 
def start_ie():
controller = windmill.browser.get_ie_controller()
controller.start()
windmill.settings['controllers'].append(controller)
return controller
 
def run_test_file(filename):
f = open(filename)
test_strings = f.read().splitlines()
jsonrpc_client.run_json_tests(test_strings)
logger.info('Added tests from %s' % filename)
 
def run_test_dir(directory):
# Try to import test_conf
sys.path.insert(0, os.path.abspath(directory))
try:
import test_conf
test_list = test_conf.test_list
except:
print 'No test_conf.py for this directory, executing all test in directory'
test_list = [test_name for test_name in os.listdir(os.path.abspath(directory)) if not test_name.startswith('.') and test_name.endswith('.json')]
 
for test in test_list:
run_test_file(os.path.abspath(directory)+os.path.sep+test)
 
def run_given_test_dir():
run_test_dir(windmill.settings['TEST_DIR'])
 
logger = logging.getLogger(__name__)
 
dot-two-refactor/windmill/bin/admin_options.py New file
0,0 → 1,93
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
import windmill
import os, sys, logging
 
class LogLevel(object):
"""Log level command, sets the global logging level"""
option_names = (None, 'loglevel')
 
def __call__(self, value):
level = getattr(logging, value)
windmill.settings['CONSOLE_LOG_LEVEL'] = getattr(logging, value)
return level
 
class ExitOnDone(object):
"""Exit after all tests have run"""
option_names = ('e', 'exit')
 
def __call__(self):
windmill.settings['EXIT_ON_DONE'] = True
 
class Debug(object):
"""Turn on debugging"""
option_names = ('d', 'debug')
 
def __call__(self):
windmill.settings['CONSOLE_LOG_LEVEL'] = getattr(logging, 'DEBUG')
 
class TestFile(object):
"""Set the test file to run on startup"""
option_names = (None, 'testfile')
 
def __call__(self, value):
windmill.settings['TEST_FILE'] = os.path.abspath(os.path.expanduser(value))
 
class TestDir(object):
"""Set the test directory to run on startup"""
option_names = (None, 'testdir')
 
def __call__(self, value):
windmill.settings['TEST_DIR'] = os.path.abspath(os.path.expanduser(value))
 
class GeneralBoolSettingToTrue(object):
"""Base class for setting a generic value to True"""
def __call__(self):
windmill.settings[self.setting] = True
 
class GeneralBoolSettingToFalse(object):
"""Base class for setting a generic value to False"""
def __call__(self):
windmill.settings[self.setting] = False
 
class StartFirefox(GeneralBoolSettingToTrue):
option_names = ('m', 'firefox')
setting = 'START_FIREFOX'
 
class StartIE(GeneralBoolSettingToTrue):
option_names = ('x', 'ie')
setting = 'START_IE'
 
class ContinueOnFailure(GeneralBoolSettingToTrue):
option_names = ('c', 'continueonfailure')
setting = 'CONTINUE_ON_FAILURE'
 
def process_module(module):
"""Process this modules option list"""
options_dict = {}
flags_dict = {}
 
for klass in [getattr(module, cname) for cname in dir(module) if hasattr(getattr(module, cname), 'option_names')]:
if klass.option_names[0] is not None:
flags_dict[klass.option_names[0]] = klass()
options_dict[klass.option_names[1]] = klass()
 
module.options_dict = options_dict
module.flags_dict = flags_dict
 
 
 
 
 
dot-two-refactor/windmill/bin/windmill-admin.py New file
0,0 → 1,41
#!/usr/bin/env python
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
 
import os, sys, time
 
WINDMILL_DIR = os.path.abspath(os.path.expanduser(sys.modules[__name__].__file__)+os.path.sep+os.path.pardir+os.path.sep+os.path.pardir+os.path.sep+os.path.pardir)
sys.path.insert(0, WINDMILL_DIR)
 
import windmill
 
if __name__ == "__main__":
 
from windmill.bin import admin_lib
 
admin_lib.configure_global_settings()
 
action = admin_lib.process_options(sys.argv)
 
shell_objects = admin_lib.setup()
 
action(shell_objects)
 
 
 
 
 
 
 
 
dot-two-refactor/windmill/js/extensions/extensions.js New file
0,0 → 1,97
/*
Copyright 2006, Open Source Applications Foundation
 
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
 
http://www.apache.org/licenses/LICENSE2.0
 
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
 
//Author: Adam Christian, Open Source Applications Foundation
//Email: adam@osafoundation.org
//
//Description: The following function allows a selenium test to accurately move an event DIV
//to a destination time/day using the Cosmo UI objects to calculate and compensate for
//The relative DIV offsets
//
//Since the div names are a concatonation of their hash key and a prepending string
//I will just recreate that before I actually look up the dom element, then click
//the appropriate element
 
windmill.controller.extensions.clickLozenge =function(param_object){
var hash_key;
 
eval ("hash_key=" + param_object.jsid + ";");
//hash_key = eval('('+ param_object.jsid + ')');
param_object.id = "eventDivContent__" + hash_key;
delete param_object.jsid;
 
//Since id comes before jsid in the lookup order
//we don't need to reset it, now go ahead and click it!
return this.click(param_object);
}
 
 
windmill.controller.extensions.cosmoDragDrop = function(param_object){
 
 
var p = param_object;
var hash_key;
 
eval ("hash_key=" + p.dragged.jsid + ";");
p.dragged.id = p.dragged.pfx+ hash_key;
delete p.dragged.jsid;
 
function getPos(elem, evType) {
// param_object.mouseDownPos or param_obj.mouseUpPos
var t = evType + 'Pos';
var res = [];
// Explicit function for getting XY of both
// start and end position for drag start
// to be calculated from the initial pos of the
// dragged, and end to be calculated from the
// position of the destination
if (p[t]) {
var f = eval(p[t]);
res = f(elem);
}
// Otherwise naively assume top/left XY for both
// start (dragged) and end (destination)
else {
res = [elem.offsetLeft, elem.offsetTop];
}
 
return res;
}
 
 
var dragged = windmill.controller._lookupDispatch(p.dragged);
var dest = windmill.controller._lookupDispatch(p.destination);
//var mouseDownPos = getPos(dragged, 'mouseDown');
//var mouseUpPos = getPos(dest, 'mouseUp');
 
var webApp = parent.frames['webapp'];
var mouseDownX = dragged.parentNode.offsetLeft + (webApp.LEFT_SIDEBAR_WIDTH + webApp.HOUR_LISTING_WIDTH + 2) + 12;
var mouseDownY = dragged.parentNode.offsetTop - (webApp.cosmo.view.cal.canvas.getTimedCanvasScrollTop() - webApp.TOP_MENU_HEIGHT) + 12;
 
var webApp = parent.frames['webapp'];
var mouseUpX = dest.parentNode.offsetLeft + (webApp.LEFT_SIDEBAR_WIDTH + webApp.HOUR_LISTING_WIDTH + 2) + 12;
var mouseUpY = dest.offsetTop - (webApp.cosmo.view.cal.canvas.getTimedCanvasScrollTop() - webApp.TOP_MENU_HEIGHT) + 12;
 
var webApp = parent.frames['webapp'];
windmill.events.triggerMouseEvent(webApp.document.body, 'mousemove', true, mouseDownX, mouseDownY);
windmill.events.triggerMouseEvent(dragged, 'mousedown', true);
windmill.events.triggerMouseEvent(webApp.document.body, 'mousemove', true, mouseUpX, mouseUpY);
windmill.events.triggerMouseEvent(dragged, 'mouseup', true);
 
return true;
}
 
 
\ No newline at end of file Property changes : Added: svn:eol-style + native
dot-two-refactor/windmill/js/start.html New file
0,0 → 1,76
<!--
Copyright 2006, Open Source Applications Foundation
 
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
 
http://www.apache.org/licenses/LICENSE-2.0
 
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
 
<html>
<head>
<script type="text/javascript" src="lib/browserdetect.js"></script>
<script type="text/javascript" src="lib/windmill.js"></script>
<script type="text/javascript" src="lib/controller.js"></script>
<script type="text/javascript">
var url = 'lib/mozcontroller.js';
 
if (browser.isIE){
url = 'lib/iecontroller.js';
}
else if(browser.isSafari){
url = 'lib/safcontroller.js';
}
document.write('<script src="', url, '" type="text/javascript"><\/script>');
</script>
<script type="text/javascript" src="lib/fleegix.js"></script>
<script type="text/javascript" src="lib/timing.js"></script>
<script type="text/javascript" src="lib/performance.js"></script>
<script type="text/javascript" src="lib/events.js"></script>
 
<script type="text/javascript" src="lib/log4js.js" ></script>
<script type="text/javascript" src="lib/ui.js"></script>
<script type="text/javascript" src="lib/xhr.js"></script>
<script type="text/javascript" src="lib/xpath/misc.js"></script>
<script type="text/javascript" src="lib/xpath/dom.js"></script>
<script type="text/javascript" src="lib/xpath/xpath.js"></script>
 
<script type="text/javascript" src="lib/windmill-exec.js"></script>
<script type="text/javascript" src="lib/helpers.js"></script>
<script type="text/javascript" src="extensions/extensions.js"></script>
 
<script type="text/javascript">
//Timing the load of the first page
var load_timer = new TimeObj();
load_timer.setName('Loading first page');
load_timer.startTime();
 
//Enabling the ability to start out out a page that requires query parameters
//By parsing the url they requested and opening it and storing that url
//Then when we open write the iframe src we can give it the correct starting src attrib
var urlSTR = window.location.href.replace("/windmill-serv/start.html","");
windmill.initURL = urlSTR;
 
//Set the title
document.title = 'Windmill Testing Framework: ' + windmill.initURL;
 
</script>
</head>
<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
<title>Windmill Testing Framework</title>
 
<body topmargin="0" leftmargin="0" onload="javascript:Load()">
 
<script type="text/javascript">
document.write("<div id=\"pagediv\"><IFRAME style=\"position:absolute;overflow:auto;width:100%;height:100%;\" id=\"webapp\" name=\"webapp\" onload=\"javascript:windmill.controller.continueLoop();windmill.ui.setRecState();\" target=\"_self\" frameborder=0 SRC=\""+windmill.initURL+"\"></iframe></div>");
</script>
 
</body>
<html>
\ No newline at end of file Property changes : Added: svn:eol-style + native
dot-two-refactor/windmill/js/ide/css/button.css New file
0,0 → 1,21
.buttonElem {
font-size:11px;
color:#333399;
border:1px solid #6666aa;
height:24px;
padding:0px;
padding-left:8px;
padding-right:8px;
cursor:pointer;
}
 
.buttonElemSm {
font-size:9px;
color:#333399;
border:1px solid #6666aa;
height:18px;
padding:0px;
padding-left:8px;
padding-right:8px;
cursor:pointer;
}
\ No newline at end of file
dot-two-refactor/windmill/js/ide/css/windmill.css New file
0,0 → 1,113
body, html
{
 
font-size: 13px;
margin: 0px;
padding: 0px;
text-align: left;
font-family: "Verdana", "Arial", sans-serif;
width: 100%; /* make the body expand to fill the visible window */
height: 100%;
overflow: hidden; /* erase window level scrollbars */
padding: 0 0 0 0;
margin: 0 0 0 0;
 
}
a:link { text-decoration : none; color : #6699cc; border: 0px;}
a:active { text-decoration : underline; color : #6699cc; border: 0px;}
a:visited { text-decoration : none; color : #6699cc; border: 0px;}
a:hover { text-decoration : underline; color : #336699; border: 0px; cursor: pointer; }
 
 
#pagediv{
left:0px;
top:0px;
height:100%;
width:100%;
border:0px;
position:absolute;
visibility: visible;
z-index:1;
overflow: auto;
 
}
 
#resultsdiv{
width:100%;
top:0px;
position:absolute;
height:100%;
border:0px solid black;
font-size:10px;
z-index:5000;
 
 
}
 
#performancediv{
width:100%;
top:0px;
position:absolute;
height:100%;
border:0px solid black;
font-size:10px;
z-index:5000;
 
}
 
#tabBlock {
 
top: 0px;
left: 0px;
width: 100%;
height: 100%;
border: none;
z-index:5000;
 
}
 
#mainTabContainer {
top: 0px;
left: 0%;
width: 85%;
height: 150px;
border: none;
z-index:5000;
}
 
#welcome{
width:300px;
position: absolute;
top: 20px;
left: 100px;
font-size:10px;
z-index:5000;
 
}
 
#Info{
width:320px;
position: absolute;
top: 20px;
left: 100px;
font-size:10px;
z-index:5000;
 
}
 
#RC{
width:320px;
position: absolute;
top: 20px;
left: 100px;
font-size:10px;
z-index:5000;
 
}
 
.remoteTabs {
display:none;
overflow:scroll;
height:465px;
width:445px;
}
dot-two-refactor/windmill/js/ide/tabs/tab.js New file
0,0 → 1,246
/*
* Danne Lundqvist
* http://www.dotvoid.com
*
* May 19, 2004, Danne Lundqvist
* Verson 1.0 - Inital version
*
* June 23, 2004
* Version 1.1 - Functions for Post-Drawing Manipulation
*/
 
function TabFocus(event)
{
if (!event) event = window.event;
evtTarget = (browser.isIE5up) ? event.srcElement : event.target;
evtTarget.tab.group.focus(evtTarget.tab.id);
 
return false;
}
 
function TabFocus(e)
{
var targ;
 
if (!e) var e = window.event;
if (e.target) targ = e.target;
else if (e.srcElement) targ = e.srcElement;
 
if (targ.nodeType == 3) // defeat Safari bug
targ = targ.parentNode;
targ.tab.group.focus(targ.tab.id);
 
return false;
}
 
function Tab(group, id, title)
{
this.id = id;
this.tid = id + '_tab';
this.group = group;
 
var tab = document.createElement('div');
var a = document.createElement('a');
 
if (browser.isIE5up)
{
a.attachEvent("onclick", TabFocus);
}
else
{
a.addEventListener("click", TabFocus, false);
}
a.tab = this;
a.href = '#';
a.innerHTML = title;
 
tab.id = this.tid;
tab.className = 'TabStyleTabNormal';
tab.appendChild(a);
 
group.tablist.appendChild(tab);
 
return this;
}
 
 
function TabGroupTabFocus(id)
{
var toId = id;
var fromId = this.aid;
 
if (fromId)
{
var e = document.getElementById(fromId);
if (e) e.style.display = 'none';
}
if (toId)
{
var e = document.getElementById(toId);
if (e) e.style.display = 'block';
}
 
if (!id)
id = this.aid;
else
this.aid = id;
 
for (n = 0; n < this.length(); n++)
{
var t = document.getElementById(this.tabs[n].tid);
if (this.tabs[n].id == id)
{
t.className = 'TabStyleTabActive';
this.onTabFocusGained(this.tabs[n].id);
}
else if (t.className = 'TabStyleTabActive')
{
t.className = 'TabStyleTabNormal';
this.onTabFocusLost(this.tabs[n].id);
}
}
}
 
 
 
 
function TabGroupLength()
{
return this.tabs.length;
}
 
function TabGroupAdd(id, title)
{
this.tabs[this.tabs.length] = new Tab(this, id, title, this.tabs.length + 1);
}
 
function TabGroupRemove(id)
{
if (this.tabs.length==1) { this.tabs=new Array(); return; }
var newTabs = new Array(this.tabs.length - 1);
var m = 0;
for (n = 0; n < this.tabs.length; n++)
{
if (this.tabs[n].id != id)
newTabs[m++] = this.tabs[n];
}
this.tabs = newTabs;
}
 
 
function TabGroupDraw()
{
if (!this.id)
return;
 
this.content.innerHTML = '';
for (n = 0; n < this.length(); n++)
{
var t = document.getElementById(this.tabs[n].id);
var p = t.parentNode;
if (p) t = p.removeChild(t);
this.content.appendChild(t);
}
}
 
 
/**************************************************************
* Extension Functions for Post-Drawing Manipulation
***************************************************************/
 
function TabGroupTabSetForeground(id,c){
var t=document.getElementById(id+"_tab");
if (t){
var links=t.getElementsByTagName("a");
if(links&&links.length>0){
links[0].style.color=c;
}
}
}
 
 
function TabGroupTabSetTitle(id,title){
var t=document.getElementById(id+"_tab");
if (t){
var links=t.getElementsByTagName("a");
if(links&&links.length>0){
links[0].innerHTML=title;
}
}
}
 
function TabGroupRemoveEx(id){
if (! document.getElementById(id)) return;
this.remove(id);
var e = document.getElementById(id);
if (e){
e.style.display='none';
if (e.parentNode) e.parentNode.removeChild(e);
}
e = document.getElementById(id+"_tab");
if (e){
e.style.display='none';
if (e.parentNode) e.parentNode.removeChild(e);
}
 
if (this.aid==id && this.length()>0){
this.focus(this.tabs[0].id);
}
}
 
 
function TabGroupAddEx(id, title){
var t = document.getElementById(id);
if (! t){
t=document.createElement('div');
t.innerHTML="";
t.style.display='none';
t.id=id;
}
 
this.add(id,title);
if (t){
var p = t.parentNode;
if (p) t = p.removeChild(t);
this.content.appendChild(t);
}
}
 
/**************************************************************
* END: Dov Katz Function Extensions
**************************************************************/
 
function TabGroup(id)
{
this.id = id;
this.tabs = new Array();
this.aid = '';
 
this.add = TabGroupAdd;
this.remove = TabGroupRemove;
this.length = TabGroupLength;
this.focus = TabGroupTabFocus;
this.draw = TabGroupDraw;
 
this.removeEx = TabGroupRemoveEx;
this.addEx = TabGroupAddEx;
this.setTitle=TabGroupTabSetTitle;
this.onTabFocusGained=function(id){this.setTabForeground(id,'#448');};
this.onTabFocusLost=function(id){this.setTabForeground(id,'#448');};
 
// Not sure this should exist. Should really be in the css!!!
this.setTabForeground=TabGroupTabSetForeground;
 
this.content = document.createElement('div');
this.content.className = 'TabStyleContent';
this.content.appendChild(document.createTextNode('Init...'));
 
this.tablist = document.createElement('div');
this.tablist.className = 'TabStyleTabList';
 
e = document.getElementById(id);
e.appendChild(this.tablist);
e.appendChild(this.content);
 
return this;
}
\ No newline at end of file Property changes : Added: svn:executable + *
dot-two-refactor/windmill/js/ide/tabs/README New file
0,0 → 1,25
INFORMATION
Idea and implementation by Danne Lundqvist, http://www.dotvoid.com
 
 
LICENSE
The content of this file is free to use any way you want.
However, I will like you better if you improve it and tell me or at
least tell me whether you like it or not! Contact me through the
contact form on dotvoid.com.
 
 
HISTORY & CONTRIBUTIONS
 
* Danne Lundqvist, May 19, 2004
Initial version
 
* Dov Katz, June 23, 2004
Extension Functions for Post-Drawing Manipulation
By Dov Katz - http://dovkatz.com - http://www.onlysimchas.com
AIM: dovkatzoffice
 
 
BUGS
Currently the css style information for active and normal tabs
disappear when the class is changed.
\ No newline at end of file Property changes : Added: svn:executable + *
dot-two-refactor/windmill/js/ide/tabs/tab.css New file
0,0 → 1,74
/*
* Danne Lundqvist
* http://www.dotvoid.com
*
* More information see the README file
*/
 
 
body, p, li {
font-size: 9pt;
}
 
 
div.TabStyleTabList
{
margin: 0px;
padding: 0px;
}
 
div.TabStyleTabActive
{
border: 1px solid #999;
border-bottom: 1px solid #fff;
background-color: #fff;
margin-left: 5px;
padding: 3px;
font-size: 10px;
display: inline;
 
}
 
.TabStyleTabActive a {
text-decoration: none;
color: #448;
cursor: default;
}
 
div.TabStyleTabNormal
{
display: inline;
border: 1px solid #ccc;
border-bottom: 1px solid #ccc;
background-color: #DDE;
margin-left: 5px;
padding: 3px;
font-size: 10px;
}
 
div.TabStyleTabNormal a {
text-decoration: none;
}
 
div.TabStyleTabNormal a:hover {
text-decoration: underline;
}
 
div.TabStyleContainer
{
margin: 0px;
padding: 0px;
}
 
div.TabStyleContent
{
padding: 4px;
margin: 0px;
margin-top: 3px !important;
margin-top: -1px;
height:98%;
width:99%;
overflow:scroll;
position:absolute;
 
}
Property changes : Added: svn:executable + *
dot-two-refactor/windmill/js/ide/img/logo.jpg Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes : Added: svn:mime-type + application/octet-stream
dot-two-refactor/windmill/js/lib/events.js New file
0,0 → 1,165
//All the functionaly relating to launching events in javascript
//windmill.events.*
windmill.events = new function (){
 
// Returns the text in this element
this.getText = function(element) {
var text = "";
 
var isRecentFirefox = (browser.isMozilla);
if (isRecentFirefox || browser.isKonqueror || browser.isSafari || browser.isOpera) {
text = windmill.events.getTextContent(element);
} else if (element.textContent) {
text = element.textContent;
} else if (element.innerText) {
text = element.innerText;
}
 
text = windmill.helpers.normalizeNewlines(text);
text = windmill.helpers.normalizeSpaces(text);
return text.trim();
}
 
this.getTextContent = function(element, preformatted) {
if (element.nodeType == 3 /*Node.TEXT_NODE*/) {
var text = element.data;
if (!preformatted) {
text = text.replace(/\n|\r|\t/g, " ");
}
return text;
}
if (element.nodeType == 1 /*Node.ELEMENT_NODE*/) {
 
var childrenPreformatted = preformatted || (element.tagName == "PRE");
var text = "";
for (var i = 0; i < element.childNodes.length; i++) {
var child = element.childNodes.item(i);
text += windmill.events.getTextContent(child, childrenPreformatted);
}
// Handle block elements that introduce newlines
// -- From HTML spec:
//<!ENTITY % block
// "P | %heading; | %list; | %preformatted; | DL | DIV | NOSCRIPT |
// BLOCKQUOTE | F:wORM | HR | TABLE | FIELDSET | ADDRESS">
//
// TODO: should potentially introduce multiple newlines to separate blocks
if (element.tagName == "P" || element.tagName == "BR" || element.tagName == "HR" || element.tagName == "DIV") {
text += "\n";
}
return text;
}
return '';
}
 
 
this.createEventObject = function(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
var evt = element.ownerDocument.createEventObject();
evt.shiftKey = shiftKeyDown;
evt.metaKey = metaKeyDown;
evt.altKey = altKeyDown;
evt.ctrlKey = controlKeyDown;
return evt;
}
 
/* Fire an event in a browser-compatible manner */
this.triggerEvent = function(element, eventType, canBubble, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
 
canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
if (element.fireEvent) {
//alert(eventType)
var evt = windmill.events.createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown);
element.fireEvent('on' + eventType, evt);
//Fix for IE6-- this does work but isn't needed the bug was in the type function
//eval("element." + eventType + "();");
}
else {
var evt = document.createEvent('HTMLEvents');
 
evt.shiftKey = shiftKeyDown;
evt.metaKey = metaKeyDown;
evt.altKey = altKeyDown;
evt.ctrlKey = controlKeyDown;
 
evt.initEvent(eventType, canBubble, true);
element.dispatchEvent(evt);
}
}
 
/* Fire a mouse event in a browser-compatible manner */
this.triggerMouseEvent = function(element, eventType, canBubble, clientX, clientY, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
clientX = clientX ? clientX : 0;
clientY = clientY ? clientY : 0;
 
//LOG.warn("windmill.events.triggerMouseEvent assumes setting screenX and screenY to 0 is ok");
var screenX = 0;
var screenY = 0;
 
canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
 
if (element.fireEvent) {
//LOG.info("element has fireEvent");
 
var evt = windmill.events.createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown);
evt.detail = 0;
evt.button = 1;
evt.relatedTarget = null;
if (!screenX && !screenY && !clientX && !clientY) {
//element.click();
if(eventType == "click"){
element.click();
}
element.fireEvent('on' + eventType);
//eval("element." + eventType + "();");
}
else {
evt.screenX = screenX;
evt.screenY = screenY;
evt.clientX = clientX;
evt.clientY = clientY;
 
// when we go this route, window.event is never set to contain the event we have just created.
// ideally we could just slide it in as follows in the try-block below, but this normally
// doesn't work. This is why I try to avoid this code path, which is only required if we need to
// set attributes on the event (e.g., clientX).
try {
window.event = evt;
}
catch(e) {
// getting an "Object does not support this action or property" error. Save the event away
// for future reference.
// TODO: is there a way to update window.event?
 
// work around for http://jira.openqa.org/browse/SEL-280 -- make the event available somewhere:
//selenium.browserbot.getCurrentWindow().selenium_event = evt;
}
 
element.fireEvent('on' + eventType, evt);
}
}
else {
 
//LOG.info("element doesn't have fireEvent");
var evt = document.createEvent('MouseEvents');
if (evt.initMouseEvent)
{
//LOG.info("element has initMouseEvent");
//Safari
evt.initMouseEvent(eventType, canBubble, true, document.defaultView, 1, screenX, screenY, clientX, clientY, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown, 0, null)
}
else {
//LOG.warn("element doesn't have initMouseEvent; firing an event which should -- but doesn't -- have other mouse-event related attributes here, as well as controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown");
evt.initEvent(eventType, canBubble, true);
 
evt.shiftKey = shiftKeyDown;
evt.metaKey = metaKeyDown;
evt.altKey = altKeyDown;
evt.ctrlKey = controlKeyDown;
 
}
//Used by safari
element.dispatchEvent(evt);
}
}
 
 
};
\ No newline at end of file
dot-two-refactor/windmill/js/lib/xpath/xpath.js New file
0,0 → 1,2182
// Copyright 2005 Google Inc.
// All Rights Reserved
//
// An XPath parser and evaluator written in JavaScript. The
// implementation is complete except for functions handling
// namespaces.
//
// Reference: [XPATH] XPath Specification
// <http://www.w3.org/TR/1999/REC-xpath-19991116>.
//
//
// The API of the parser has several parts:
//
// 1. The parser function xpathParse() that takes a string and returns
// an expession object.
//
// 2. The expression object that has an evaluate() method to evaluate the
// XPath expression it represents. (It is actually a hierarchy of
// objects that resembles the parse tree, but an application will call
// evaluate() only on the top node of this hierarchy.)
//
// 3. The context object that is passed as an argument to the evaluate()
// method, which represents the DOM context in which the expression is
// evaluated.
//
// 4. The value object that is returned from evaluate() and represents
// values of the different types that are defined by XPath (number,
// string, boolean, and node-set), and allows to convert between them.
//
// These parts are near the top of the file, the functions and data
// that are used internally follow after them.
//
//
// TODO(mesch): add jsdoc comments. Use more coherent naming.
//
//
// Author: Steffen Meschkat <mesch@google.com>
 
 
// The entry point for the parser.
//
// @param expr a string that contains an XPath expression.
// @return an expression object that can be evaluated with an
// expression context.
 
function xpathParse(expr) {
if (xpathdebug) {
Log.write('XPath parse ' + expr);
}
xpathParseInit();
 
var cached = xpathCacheLookup(expr);
if (cached) {
if (xpathdebug) {
Log.write(' ... cached');
}
return cached;
}
 
// Optimize for a few common cases: simple attribute node tests
// (@id), simple element node tests (page), variable references
// ($address), numbers (4), multi-step path expressions where each
// step is a plain element node test
// (page/overlay/locations/location).
 
if (expr.match(/^(\$|@)?\w+$/i)) {
var ret = makeSimpleExpr(expr);
xpathParseCache[expr] = ret;
if (xpathdebug) {
Log.write(' ... simple');
}
return ret;
}
 
if (expr.match(/^\w+(\/\w+)*$/i)) {
var ret = makeSimpleExpr2(expr);
xpathParseCache[expr] = ret;
if (xpathdebug) {
Log.write(' ... simple 2');
}
return ret;
}
 
var cachekey = expr; // expr is modified during parse
if (xpathdebug) {
Timer.start('XPath parse', cachekey);
}
 
var stack = [];
var ahead = null;
var previous = null;
var done = false;
 
var parse_count = 0;
var lexer_count = 0;
var reduce_count = 0;
 
while (!done) {
parse_count++;
expr = expr.replace(/^\s*/, '');
previous = ahead;
ahead = null;
 
var rule = null;
var match = '';
for (var i = 0; i < xpathTokenRules.length; ++i) {
var result = xpathTokenRules[i].re.exec(expr);
lexer_count++;
if (result && result.length > 0 && result[0].length > match.length) {
rule = xpathTokenRules[i];
match = result[0];
break;
}
}
 
// Special case: allow operator keywords to be element and
// variable names.
 
// NOTE(mesch): The parser resolves conflicts by looking ahead,
// and this is the only case where we look back to
// disambiguate. So this is indeed something different, and
// looking back is usually done in the lexer (via states in the
// general case, called "start conditions" in flex(1)). Also,the
// conflict resolution in the parser is not as robust as it could
// be, so I'd like to keep as much off the parser as possible (all
// these precedence values should be computed from the grammar
// rules and possibly associativity declarations, as in bison(1),
// and not explicitly set.
 
if (rule &&
(rule == TOK_DIV ||
rule == TOK_MOD ||
rule == TOK_AND ||
rule == TOK_OR) &&
(!previous ||
previous.tag == TOK_AT ||
previous.tag == TOK_DSLASH ||
previous.tag == TOK_SLASH ||
previous.tag == TOK_AXIS ||
previous.tag == TOK_DOLLAR)) {
rule = TOK_QNAME;
}
 
if (rule) {
expr = expr.substr(match.length);
if (xpathdebug) {
Log.write('token: ' + match + ' -- ' + rule.label);
}
ahead = {
tag: rule,
match: match,
prec: rule.prec ? rule.prec : 0, // || 0 is removed by the compiler
expr: makeTokenExpr(match)
};
 
} else {
if (xpathdebug) {
Log.write('DONE');
}
done = true;
}
 
while (xpathReduce(stack, ahead)) {
reduce_count++;
if (xpathdebug) {
Log.write('stack: ' + stackToString(stack));
}
}
}
 
if (xpathdebug) {
Log.write(stackToString(stack));
}
 
if (stack.length != 1) {
throw 'XPath parse error ' + cachekey + ':\n' + stackToString(stack);
}
 
var result = stack[0].expr;
xpathParseCache[cachekey] = result;
 
if (xpathdebug) {
Timer.end('XPath parse', cachekey);
}
 
if (xpathdebug) {
Log.write('XPath parse: ' + parse_count + ' / ' +
lexer_count + ' / ' + reduce_count);
}
 
return result;
}
 
var xpathParseCache = {};
 
function xpathCacheLookup(expr) {
return xpathParseCache[expr];
}
 
function xpathReduce(stack, ahead) {
var cand = null;
 
if (stack.length > 0) {
var top = stack[stack.length-1];
var ruleset = xpathRules[top.tag.key];
 
if (ruleset) {
for (var i = 0; i < ruleset.length; ++i) {
var rule = ruleset[i];
var match = xpathMatchStack(stack, rule[1]);
if (match.length) {
cand = {
tag: rule[0],
rule: rule,
match: match
};
cand.prec = xpathGrammarPrecedence(cand);
break;
}
}
}
}
 
var ret;
if (cand && (!ahead || cand.prec > ahead.prec ||
(ahead.tag.left && cand.prec >= ahead.prec))) {
for (var i = 0; i < cand.match.matchlength; ++i) {
stack.pop();
}
 
if (xpathdebug) {
Log.write('reduce ' + cand.tag.label + ' ' + cand.prec +
' ahead ' + (ahead ? ahead.tag.label + ' ' + ahead.prec +
(ahead.tag.left ? ' left' : '')
: ' none '));
}
 
var matchexpr = mapExpr(cand.match, function(m) { return m.expr; });
cand.expr = cand.rule[3].apply(null, matchexpr);
 
stack.push(cand);
ret = true;
 
} else {
if (ahead) {
if (xpathdebug) {
Log.write('shift ' + ahead.tag.label + ' ' + ahead.prec +
(ahead.tag.left ? ' left' : '') +
' over ' + (cand ? cand.tag.label + ' ' +
cand.prec : ' none'));
}
stack.push(ahead);
}
ret = false;
}
return ret;
}
 
function xpathMatchStack(stack, pattern) {
 
// NOTE(mesch): The stack matches for variable cardinality are
// greedy but don't do backtracking. This would be an issue only
// with rules of the form A* A, i.e. with an element with variable
// cardinality followed by the same element. Since that doesn't
// occur in the grammar at hand, all matches on the stack are
// unambiguous.
 
var S = stack.length;
var P = pattern.length;
var p, s;
var match = [];
match.matchlength = 0;
var ds = 0;
for (p = P - 1, s = S - 1; p >= 0 && s >= 0; --p, s -= ds) {
ds = 0;
var qmatch = [];
if (pattern[p] == Q_MM) {
p -= 1;
match.push(qmatch);
while (s - ds >= 0 && stack[s - ds].tag == pattern[p]) {
qmatch.push(stack[s - ds]);
ds += 1;
match.matchlength += 1;
}
 
} else if (pattern[p] == Q_01) {
p -= 1;
match.push(qmatch);
while (s - ds >= 0 && ds < 2 && stack[s - ds].tag == pattern[p]) {
qmatch.push(stack[s - ds]);
ds += 1;
match.matchlength += 1;
}
 
} else if (pattern[p] == Q_1M) {
p -= 1;
match.push(qmatch);
if (stack[s].tag == pattern[p]) {
while (s - ds >= 0 && stack[s - ds].tag == pattern[p]) {
qmatch.push(stack[s - ds]);
ds += 1;
match.matchlength += 1;
}
} else {
return [];
}
 
} else if (stack[s].tag == pattern[p]) {
match.push(stack[s]);
ds += 1;
match.matchlength += 1;
 
} else {
return [];
}
 
reverseInplace(qmatch);
qmatch.expr = mapExpr(qmatch, function(m) { return m.expr; });
}
 
reverseInplace(match);
 
if (p == -1) {
return match;
 
} else {
return [];
}
}
 
function xpathTokenPrecedence(tag) {
return tag.prec || 2;
}
 
function xpathGrammarPrecedence(frame) {
var ret = 0;
 
if (frame.rule) { /* normal reduce */
if (frame.rule.length >= 3 && frame.rule[2] >= 0) {
ret = frame.rule[2];
 
} else {
for (var i = 0; i < frame.rule[1].length; ++i) {
var p = xpathTokenPrecedence(frame.rule[1][i]);
ret = Math.max(ret, p);
}
}
} else if (frame.tag) { /* TOKEN match */
ret = xpathTokenPrecedence(frame.tag);
 
} else if (frame.length) { /* Q_ match */
for (var j = 0; j < frame.length; ++j) {
var p = xpathGrammarPrecedence(frame[j]);
ret = Math.max(ret, p);
}
}
 
return ret;
}
 
function stackToString(stack) {
var ret = '';
for (var i = 0; i < stack.length; ++i) {
if (ret) {
ret += '\n';
}
ret += stack[i].tag.label;
}
return ret;
}
 
 
// XPath expression evaluation context. An XPath context consists of a
// DOM node, a list of DOM nodes that contains this node, a number
// that represents the position of the single node in the list, and a
// current set of variable bindings. (See XPath spec.)
//
// The interface of the expression context:
//
// Constructor -- gets the node, its position, the node set it
// belongs to, and a parent context as arguments. The parent context
// is used to implement scoping rules for variables: if a variable
// is not found in the current context, it is looked for in the
// parent context, recursively. Except for node, all arguments have
// default values: default position is 0, default node set is the
// set that contains only the node, and the default parent is null.
//
// Notice that position starts at 0 at the outside interface;
// inside XPath expressions this shows up as position()=1.
//
// clone() -- creates a new context with the current context as
// parent. If passed as argument to clone(), the new context has a
// different node, position, or node set. What is not passed is
// inherited from the cloned context.
//
// setVariable(name, expr) -- binds given XPath expression to the
// name.
//
// getVariable(name) -- what the name says.
//
// setNode(node, position) -- sets the context to the new node and
// its corresponding position. Needed to implement scoping rules for
// variables in XPath. (A variable is visible to all subsequent
// siblings, not only to its children.)
 
function ExprContext(node, position, nodelist, parent) {
this.node = node;
this.position = position || 0;
this.nodelist = nodelist || [ node ];
this.variables = {};
this.parent = parent || null;
this.root = parent ? parent.root : node.ownerDocument;
}
 
ExprContext.prototype.clone = function(node, position, nodelist) {
return new
ExprContext(node || this.node,
typeof position != 'undefined' ? position : this.position,
nodelist || this.nodelist, this);
};
 
ExprContext.prototype.setVariable = function(name, value) {
this.variables[name] = value;
};
 
ExprContext.prototype.getVariable = function(name) {
if (typeof this.variables[name] != 'undefined') {
return this.variables[name];
 
} else if (this.parent) {
return this.parent.getVariable(name);
 
} else {
return null;
}
}
 
ExprContext.prototype.setNode = function(node, position) {
this.node = node;
this.position = position;
}
 
 
// XPath expression values. They are what XPath expressions evaluate
// to. Strangely, the different value types are not specified in the
// XPath syntax, but only in the semantics, so they don't show up as
// nonterminals in the grammar. Yet, some expressions are required to
// evaluate to particular types, and not every type can be coerced
// into every other type. Although the types of XPath values are
// similar to the types present in JavaScript, the type coercion rules
// are a bit peculiar, so we explicitly model XPath types instead of
// mapping them onto JavaScript types. (See XPath spec.)
//
// The four types are:
//
// StringValue
//
// NumberValue
//
// BooleanValue
//
// NodeSetValue
//
// The common interface of the value classes consists of methods that
// implement the XPath type coercion rules:
//
// stringValue() -- returns the value as a JavaScript String,
//
// numberValue() -- returns the value as a JavaScript Number,
//
// booleanValue() -- returns the value as a JavaScript Boolean,
//
// nodeSetValue() -- returns the value as a JavaScript Array of DOM
// Node objects.
//
 
function StringValue(value) {
this.value = value;
this.type = 'string';
}
 
StringValue.prototype.stringValue = function() {
return this.value;
}
 
StringValue.prototype.booleanValue = function() {
return this.value.length > 0;
}
 
StringValue.prototype.numberValue = function() {
return this.value - 0;
}
 
StringValue.prototype.nodeSetValue = function() {
throw this + ' ' + Error().stack;
}
 
function BooleanValue(value) {
this.value = value;
this.type = 'boolean';
}
 
BooleanValue.prototype.stringValue = function() {
return '' + this.value;
}
 
BooleanValue.prototype.booleanValue = function() {
return this.value;
}
 
BooleanValue.prototype.numberValue = function() {
return this.value ? 1 : 0;
}
 
BooleanValue.prototype.nodeSetValue = function() {
throw this + ' ' + Error().stack;
}
 
function NumberValue(value) {
this.value = value;
this.type = 'number';
}
 
NumberValue.prototype.stringValue = function() {
return '' + this.value;
}
 
NumberValue.prototype.booleanValue = function() {
return !!this.value;
}
 
NumberValue.prototype.numberValue = function() {
return this.value - 0;
}
 
NumberValue.prototype.nodeSetValue = function() {
throw this + ' ' + Error().stack;
}
 
function NodeSetValue(value) {
this.value = value;
this.type = 'node-set';
}
 
NodeSetValue.prototype.stringValue = function() {
if (this.value.length == 0) {
return '';
} else {
return xmlValue(this.value[0]);
}
}
 
NodeSetValue.prototype.booleanValue = function() {
return this.value.length > 0;
}
 
NodeSetValue.prototype.numberValue = function() {
return this.stringValue() - 0;
}
 
NodeSetValue.prototype.nodeSetValue = function() {
return this.value;
};
 
// XPath expressions. They are used as nodes in the parse tree and
// possess an evaluate() method to compute an XPath value given an XPath
// context. Expressions are returned from the parser. Teh set of
// expression classes closely mirrors the set of non terminal symbols
// in the grammar. Every non trivial nonterminal symbol has a
// corresponding expression class.
//
// The common expression interface consists of the following methods:
//
// evaluate(context) -- evaluates the expression, returns a value.
//
// toString() -- returns the XPath text representation of the
// expression (defined in xsltdebug.js).
//
// parseTree(indent) -- returns a parse tree representation of the
// expression (defined in xsltdebug.js).
 
function TokenExpr(m) {
this.value = m;
}
 
TokenExpr.prototype.evaluate = function() {
return new StringValue(this.value);
};
 
function LocationExpr() {
this.absolute = false;
this.steps = [];
}
 
LocationExpr.prototype.appendStep = function(s) {
this.steps.push(s);
}
 
LocationExpr.prototype.prependStep = function(s) {
var steps0 = this.steps;
this.steps = [ s ];
for (var i = 0; i < steps0.length; ++i) {
this.steps.push(steps0[i]);
}
};
 
LocationExpr.prototype.evaluate = function(ctx) {
var start;
if (this.absolute) {
start = ctx.root;
 
} else {
start = ctx.node;
}
 
var nodes = [];
xPathStep(nodes, this.steps, 0, start, ctx);
return new NodeSetValue(nodes);
};
 
function xPathStep(nodes, steps, step, input, ctx) {
var s = steps[step];
var ctx2 = ctx.clone(input);
var nodelist = s.evaluate(ctx2).nodeSetValue();
 
for (var i = 0; i < nodelist.length; ++i) {
if (step == steps.length - 1) {
nodes.push(nodelist[i]);
} else {
xPathStep(nodes, steps, step + 1, nodelist[i], ctx);
}
}
}
 
function StepExpr(axis, nodetest, predicate) {
this.axis = axis;
this.nodetest = nodetest;
this.predicate = predicate || [];
}
 
StepExpr.prototype.appendPredicate = function(p) {
this.predicate.push(p);
}
 
StepExpr.prototype.evaluate = function(ctx) {
var input = ctx.node;
var nodelist = [];
 
// NOTE(mesch): When this was a switch() statement, it didn't work
// in Safari/2.0. Not sure why though; it resulted in the JavaScript
// console output "undefined" (without any line number or so).
 
if (this.axis == xpathAxis.ANCESTOR_OR_SELF) {
nodelist.push(input);
for (var n = input.parentNode; n; n = input.parentNode) {
nodelist.push(n);
}
 
} else if (this.axis == xpathAxis.ANCESTOR) {
for (var n = input.parentNode; n; n = input.parentNode) {
nodelist.push(n);
}
 
} else if (this.axis == xpathAxis.ATTRIBUTE) {
copyArray(nodelist, input.attributes);
 
} else if (this.axis == xpathAxis.CHILD) {
copyArray(nodelist, input.childNodes);
 
} else if (this.axis == xpathAxis.DESCENDANT_OR_SELF) {
nodelist.push(input);
xpathCollectDescendants(nodelist, input);
 
} else if (this.axis == xpathAxis.DESCENDANT) {
xpathCollectDescendants(nodelist, input);
 
} else if (this.axis == xpathAxis.FOLLOWING) {
for (var n = input.parentNode; n; n = n.parentNode) {
for (var nn = n.nextSibling; nn; nn = nn.nextSibling) {
nodelist.push(nn);
xpathCollectDescendants(nodelist, nn);
}
}
 
} else if (this.axis == xpathAxis.FOLLOWING_SIBLING) {
for (var n = input.nextSibling; n; n = input.nextSibling) {
nodelist.push(n);
}
 
} else if (this.axis == xpathAxis.NAMESPACE) {
alert('not implemented: axis namespace');
 
} else if (this.axis == xpathAxis.PARENT) {
if (input.parentNode) {
nodelist.push(input.parentNode);
}
 
} else if (this.axis == xpathAxis.PRECEDING) {
for (var n = input.parentNode; n; n = n.parentNode) {
for (var nn = n.previousSibling; nn; nn = nn.previousSibling) {
nodelist.push(nn);
xpathCollectDescendantsReverse(nodelist, nn);
}
}
 
} else if (this.axis == xpathAxis.PRECEDING_SIBLING) {
for (var n = input.previousSibling; n; n = input.previousSibling) {
nodelist.push(n);
}
 
} else if (this.axis == xpathAxis.SELF) {
nodelist.push(input);
 
} else {
throw 'ERROR -- NO SUCH AXIS: ' + this.axis;
}
 
// process node test
var nodelist0 = nodelist;
nodelist = [];
for (var i = 0; i < nodelist0.length; ++i) {
var n = nodelist0[i];
if (this.nodetest.evaluate(ctx.clone(n, i, nodelist0)).booleanValue()) {
nodelist.push(n);
}
}
 
// process predicates
for (var i = 0; i < this.predicate.length; ++i) {
var nodelist0 = nodelist;
nodelist = [];
for (var ii = 0; ii < nodelist0.length; ++ii) {
var n = nodelist0[ii];
if (this.predicate[i].evaluate(ctx.clone(n, ii, nodelist0)).booleanValue()) {
nodelist.push(n);
}
}
}
 
return new NodeSetValue(nodelist);
};
 
function NodeTestAny() {
this.value = new BooleanValue(true);
}
 
NodeTestAny.prototype.evaluate = function(ctx) {
return this.value;
};
 
function NodeTestElement() {}
 
NodeTestElement.prototype.evaluate = function(ctx) {
return new BooleanValue(ctx.node.nodeType == DOM_ELEMENT_NODE);
}
 
function NodeTestText() {}
 
NodeTestText.prototype.evaluate = function(ctx) {
return new BooleanValue(ctx.node.nodeType == DOM_TEXT_NODE);
}
 
function NodeTestComment() {}
 
NodeTestComment.prototype.evaluate = function(ctx) {
return new BooleanValue(ctx.node.nodeType == DOM_COMMENT_NODE);
}
 
function NodeTestPI(target) {
this.target = target;
}
 
NodeTestPI.prototype.evaluate = function(ctx) {
return new
BooleanValue(ctx.node.nodeType == DOM_PROCESSING_INSTRUCTION_NODE &&
(!this.target || ctx.node.nodeName == this.target));
}
 
function NodeTestNC(nsprefix) {
this.regex = new RegExp("^" + nsprefix + ":");
this.nsprefix = nsprefix;
}
 
NodeTestNC.prototype.evaluate = function(ctx) {
var n = ctx.node;
return new BooleanValue(this.regex.match(n.nodeName));
}
 
function NodeTestName(name) {
this.name = name;
}
 
NodeTestName.prototype.evaluate = function(ctx) {
var n = ctx.node;
// NOTE (Patrick Lightbody): this change allows node selection to be case-insensitive
return new BooleanValue(n.nodeName.toUpperCase() == this.name.toUpperCase());
}
 
function PredicateExpr(expr) {
this.expr = expr;
}
 
PredicateExpr.prototype.evaluate = function(ctx) {
var v = this.expr.evaluate(ctx);
if (v.type == 'number') {
// NOTE(mesch): Internally, position is represented starting with
// 0, however in XPath position starts with 1. See functions
// position() and last().
return new BooleanValue(ctx.position == v.numberValue() - 1);
} else {
return new BooleanValue(v.booleanValue());
}
};
 
function FunctionCallExpr(name) {
this.name = name;
this.args = [];
}
 
FunctionCallExpr.prototype.appendArg = function(arg) {
this.args.push(arg);
};
 
FunctionCallExpr.prototype.evaluate = function(ctx) {
var fn = '' + this.name.value;
var f = this.xpathfunctions[fn];
if (f) {
return f.call(this, ctx);
} else {
Log.write('XPath NO SUCH FUNCTION ' + fn);
return new BooleanValue(false);
}
};
 
FunctionCallExpr.prototype.xpathfunctions = {
'last': function(ctx) {
assert(this.args.length == 0);
// NOTE(mesch): XPath position starts at 1.
return new NumberValue(ctx.nodelist.length);
},
 
'position': function(ctx) {
assert(this.args.length == 0);
// NOTE(mesch): XPath position starts at 1.
return new NumberValue(ctx.position + 1);
},
 
'count': function(ctx) {
assert(this.args.length == 1);
var v = this.args[0].evaluate(ctx);
return new NumberValue(v.nodeSetValue().length);
},
 
'id': function(ctx) {
assert(this.args.length == 1);
var e = this.args.evaluate(ctx);
var ret = [];
var ids;
if (e.type == 'node-set') {
ids = [];
for (var i = 0; i < e.length; ++i) {
var v = xmlValue(e[i]).split(/\s+/);
for (var ii = 0; ii < v.length; ++ii) {
ids.push(v[ii]);
}
}
} else {
ids = e.split(/\s+/);
}
var d = ctx.node.ownerDocument;
for (var i = 0; i < ids.length; ++i) {
var n = d.getElementById(ids[i]);
if (n) {
ret.push(n);
}
}
return new NodeSetValue(ret);
},
 
'local-name': function(ctx) {
alert('not implmented yet: XPath function local-name()');
},
 
'namespace-uri': function(ctx) {
alert('not implmented yet: XPath function namespace-uri()');
},
 
'name': function(ctx) {
assert(this.args.length == 1 || this.args.length == 0);
var n;
if (this.args.length == 0) {
n = [ ctx.node ];
} else {
n = this.args[0].evaluate(ctx).nodeSetValue();
}
 
if (n.length == 0) {
return new StringValue('');
} else {
return new StringValue(n[0].nodeName);
}
},
 
'string': function(ctx) {
assert(this.args.length == 1 || this.args.length == 0);
if (this.args.length == 0) {
return new StringValue(new NodeSetValue([ ctx.node ]).stringValue());
} else {
return new StringValue(this.args[0].evaluate(ctx).stringValue());
}
},
 
'concat': function(ctx) {
var ret = '';
for (var i = 0; i < this.args.length; ++i) {
ret += this.args[i].evaluate(ctx).stringValue();
}
return new StringValue(ret);
},
 
'starts-with': function(ctx) {
assert(this.args.length == 2);
var s0 = this.args[0].evaluate(ctx).stringValue();
var s1 = this.args[1].evaluate(ctx).stringValue();
return new BooleanValue(s0.indexOf(s1) == 0);
},
 
'contains': function(ctx) {
assert(this.args.length == 2);
var s0 = this.args[0].evaluate(ctx).stringValue();
var s1 = this.args[1].evaluate(ctx).stringValue();
return new BooleanValue(s0.indexOf(s1) != -1);
},
 
'substring-before': function(ctx) {
assert(this.args.length == 2);
var s0 = this.args[0].evaluate(ctx).stringValue();
var s1 = this.args[1].evaluate(ctx).stringValue();
var i = s0.indexOf(s1);
var ret;
if (i == -1) {
ret = '';
} else {
ret = s0.substr(0,i);
}
return new StringValue(ret);
},
 
'substring-after': function(ctx) {
assert(this.args.length == 2);
var s0 = this.args[0].evaluate(ctx).stringValue();
var s1 = this.args[1].evaluate(ctx).stringValue();
var i = s0.indexOf(s1);
var ret;
if (i == -1) {
ret = '';
} else {
ret = s0.substr(i + s1.length);
}
return new StringValue(ret);
},
 
'substring': function(ctx) {
// NOTE: XPath defines the position of the first character in a
// string to be 1, in JavaScript this is 0 ([XPATH] Section 4.2).
assert(this.args.length == 2 || this.args.length == 3);
var s0 = this.args[0].evaluate(ctx).stringValue();
var s1 = this.args[1].evaluate(ctx).numberValue();
var ret;
if (this.args.length == 2) {
var i1 = Math.max(0, Math.round(s1) - 1);
ret = s0.substr(i1);
 
} else {
var s2 = this.args[2].evaluate(ctx).numberValue();
var i0 = Math.round(s1) - 1;
var i1 = Math.max(0, i0);
var i2 = Math.round(s2) - Math.max(0, -i0);
ret = s0.substr(i1, i2);
}
return new StringValue(ret);
},
 
'string-length': function(ctx) {
var s;
if (this.args.length > 0) {
s = this.args[0].evaluate(ctx).stringValue();
} else {
s = new NodeSetValue([ ctx.node ]).stringValue();
}
return new NumberValue(s.length);
},
 
'normalize-space': function(ctx) {
var s;
if (this.args.length > 0) {
s = this.args[0].evaluate(ctx).stringValue();
} else {
s = new NodeSetValue([ ctx.node ]).stringValue();
}
s = s.replace(/^\s*/,'').replace(/\s*$/,'').replace(/\s+/g, ' ');
return new StringValue(s);
},
 
'translate': function(ctx) {
assert(this.args.length == 3);
var s0 = this.args[0].evaluate(ctx).stringValue();
var s1 = this.args[1].evaluate(ctx).stringValue();
var s2 = this.args[2].evaluate(ctx).stringValue();
 
for (var i = 0; i < s1.length; ++i) {
s0 = s0.replace(new RegExp(s1.charAt(i), 'g'), s2.charAt(i));
}
return new StringValue(s0);
},
 
'boolean': function(ctx) {
assert(this.args.length == 1);
return new BooleanValue(this.args[0].evaluate(ctx).booleanValue());
},
 
'not': function(ctx) {
assert(this.args.length == 1);
var ret = !this.args[0].evaluate(ctx).booleanValue();
return new BooleanValue(ret);
},
 
'true': function(ctx) {
assert(this.args.length == 0);
return new BooleanValue(true);
},
 
'false': function(ctx) {
assert(this.args.length == 0);
return new BooleanValue(false);
},
 
'lang': function(ctx) {
assert(this.args.length == 1);
var lang = this.args[0].evaluate(ctx).stringValue();
var xmllang;
var n = ctx.node;
while (n && n != n.parentNode /* just in case ... */) {
xmllang = n.getAttribute('xml:lang');
if (xmllang) {
break;
}
n = n.parentNode;
}
if (!xmllang) {
return new BooleanValue(false);
} else {
var re = new RegExp('^' + lang + '$', 'i');
return new BooleanValue(xmllang.match(re) ||
xmllang.replace(/_.*$/,'').match(re));
}
},
 
'number': function(ctx) {
assert(this.args.length == 1 || this.args.length == 0);
 
if (this.args.length == 1) {
return new NumberValue(this.args[0].evaluate(ctx).numberValue());
} else {
return new NumberValue(new NodeSetValue([ ctx.node ]).numberValue());
}
},
 
'sum': function(ctx) {
assert(this.args.length == 1);
var n = this.args[0].evaluate(ctx).nodeSetValue();
var sum = 0;
for (var i = 0; i < n.length; ++i) {
sum += xmlValue(n[i]) - 0;
}
return new NumberValue(sum);
},
 
'floor': function(ctx) {
assert(this.args.length == 1);
var num = this.args[0].evaluate(ctx).numberValue();
return new NumberValue(Math.floor(num));
},
 
'ceiling': function(ctx) {
assert(this.args.length == 1);
var num = this.args[0].evaluate(ctx).numberValue();
return new NumberValue(Math.ceil(num));
},
 
'round': function(ctx) {
assert(this.args.length == 1);
var num = this.args[0].evaluate(ctx).numberValue();
return new NumberValue(Math.round(num));
},
 
// TODO(mesch): The following functions are custom. There is a
// standard that defines how to add functions, which should be
// applied here.
 
'ext-join': function(ctx) {
assert(this.args.length == 2);
var nodes = this.args[0].evaluate(ctx).nodeSetValue();
var delim = this.args[1].evaluate(ctx).stringValue();
var ret = '';
for (var i = 0; i < nodes.length; ++i) {
if (ret) {
ret += delim;
}
ret += xmlValue(nodes[i]);
}
return new StringValue(ret);
},
 
// ext-if() evaluates and returns its second argument, if the
// boolean value of its first argument is true, otherwise it
// evaluates and returns its third argument.
 
'ext-if': function(ctx) {
assert(this.args.length == 3);
if (this.args[0].evaluate(ctx).booleanValue()) {
return this.args[1].evaluate(ctx);
} else {
return this.args[2].evaluate(ctx);
}
},
 
'ext-sprintf': function(ctx) {
assert(this.args.length >= 1);
var args = [];
for (var i = 0; i < this.args.length; ++i) {
args.push(this.args[i].evaluate(ctx).stringValue());
}
return new StringValue(sprintf.apply(null, args));
},
 
// ext-cardinal() evaluates its single argument as a number, and
// returns the current node that many times. It can be used in the
// select attribute to iterate over an integer range.
 
'ext-cardinal': function(ctx) {
assert(this.args.length >= 1);
var c = this.args[0].evaluate(ctx).numberValue();
var ret = [];
for (var i = 0; i < c; ++i) {
ret.push(ctx.node);
}
return new NodeSetValue(ret);
}
};
 
function UnionExpr(expr1, expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
 
UnionExpr.prototype.evaluate = function(ctx) {
var nodes1 = this.expr1.evaluate(ctx).nodeSetValue();
var nodes2 = this.expr2.evaluate(ctx).nodeSetValue();
var I1 = nodes1.length;
for (var i2 = 0; i2 < nodes2.length; ++i2) {
for (var i1 = 0; i1 < I1; ++i1) {
if (nodes1[i1] == nodes2[i2]) {
// break inner loop and continue outer loop, labels confuse
// the js compiler, so we don't use them here.
i1 = I1;
}
}
nodes1.push(nodes2[i2]);
}
return new NodeSetValue(nodes2);
};
 
function PathExpr(filter, rel) {
this.filter = filter;
this.rel = rel;
}
 
PathExpr.prototype.evaluate = function(ctx) {
var nodes = this.filter.evaluate(ctx).nodeSetValue();
var nodes1 = [];
for (var i = 0; i < nodes.length; ++i) {
var nodes0 = this.rel.evaluate(ctx.clone(nodes[i], i, nodes)).nodeSetValue();
for (var ii = 0; ii < nodes0.length; ++ii) {
nodes1.push(nodes0[ii]);
}
}
return new NodeSetValue(nodes1);
};
 
function FilterExpr(expr, predicate) {
this.expr = expr;
this.predicate = predicate;
}
 
FilterExpr.prototype.evaluate = function(ctx) {
var nodes = this.expr.evaluate(ctx).nodeSetValue();
for (var i = 0; i < this.predicate.length; ++i) {
var nodes0 = nodes;
nodes = [];
for (var j = 0; j < nodes0.length; ++j) {
var n = nodes0[j];
if (this.predicate[i].evaluate(ctx.clone(n, j, nodes0)).booleanValue()) {
nodes.push(n);
}
}
}
 
return new NodeSetValue(nodes);
}
 
function UnaryMinusExpr(expr) {
this.expr = expr;
}
 
UnaryMinusExpr.prototype.evaluate = function(ctx) {
return new NumberValue(-this.expr.evaluate(ctx).numberValue());
};
 
function BinaryExpr(expr1, op, expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
this.op = op;
}
 
BinaryExpr.prototype.evaluate = function(ctx) {
var ret;
switch (this.op.value) {
case 'or':
ret = new BooleanValue(this.expr1.evaluate(ctx).booleanValue() ||
this.expr2.evaluate(ctx).booleanValue());
break;
 
case 'and':
ret = new BooleanValue(this.expr1.evaluate(ctx).booleanValue() &&
this.expr2.evaluate(ctx).booleanValue());
break;
 
case '+':
ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() +
this.expr2.evaluate(ctx).numberValue());
break;
 
case '-':
ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() -
this.expr2.evaluate(ctx).numberValue());
break;
 
case '*':
ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() *
this.expr2.evaluate(ctx).numberValue());
break;
 
case 'mod':
ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() %
this.expr2.evaluate(ctx).numberValue());
break;
 
case 'div':
ret = new NumberValue(this.expr1.evaluate(ctx).numberValue() /
this.expr2.evaluate(ctx).numberValue());
break;
 
case '=':
ret = this.compare(ctx, function(x1, x2) { return x1 == x2; });
break;
 
case '!=':
ret = this.compare(ctx, function(x1, x2) { return x1 != x2; });
break;
 
case '<':
ret = this.compare(ctx, function(x1, x2) { return x1 < x2; });
break;
 
case '<=':
ret = this.compare(ctx, function(x1, x2) { return x1 <= x2; });
break;
 
case '>':
ret = this.compare(ctx, function(x1, x2) { return x1 > x2; });
break;
 
case '>=':
ret = this.compare(ctx, function(x1, x2) { return x1 >= x2; });
break;
 
default:
alert('BinaryExpr.evaluate: ' + this.op.value);
}
return ret;
};
 
BinaryExpr.prototype.compare = function(ctx, cmp) {
var v1 = this.expr1.evaluate(ctx);
var v2 = this.expr2.evaluate(ctx);
 
var ret;
if (v1.type == 'node-set' && v2.type == 'node-set') {
var n1 = v1.nodeSetValue();
var n2 = v2.nodeSetValue();
ret = false;
for (var i1 = 0; i1 < n1.length; ++i1) {
for (var i2 = 0; i2 < n2.length; ++i2) {
if (cmp(xmlValue(n1[i1]), xmlValue(n2[i2]))) {
ret = true;
// Break outer loop. Labels confuse the jscompiler and we
// don't use them.
i2 = n2.length;
i1 = n1.length;
}
}
}
 
} else if (v1.type == 'node-set' || v2.type == 'node-set') {
 
if (v1.type == 'number') {
var s = v1.numberValue();
var n = v2.nodeSetValue();
 
ret = false;
for (var i = 0; i < n.length; ++i) {
var nn = xmlValue(n[i]) - 0;
if (cmp(s, nn)) {
ret = true;
break;
}
}
 
} else if (v2.type == 'number') {
var n = v1.nodeSetValue();
var s = v2.numberValue();
 
ret = false;
for (var i = 0; i < n.length; ++i) {
var nn = xmlValue(n[i]) - 0;
if (cmp(nn, s)) {
ret = true;
break;
}
}
 
} else if (v1.type == 'string') {
var s = v1.stringValue();
var n = v2.nodeSetValue();
 
ret = false;
for (var i = 0; i < n.length; ++i) {
var nn = xmlValue(n[i]);
if (cmp(s, nn)) {
ret = true;
break;
}
}
 
} else if (v2.type == 'string') {
var n = v1.nodeSetValue();
var s = v2.stringValue();
 
ret = false;
for (var i = 0; i < n.length; ++i) {
var nn = xmlValue(n[i]);
if (cmp(nn, s)) {
ret = true;
break;
}
}
 
} else {
ret = cmp(v1.booleanValue(), v2.booleanValue());
}
 
} else if (v1.type == 'boolean' || v2.type == 'boolean') {
ret = cmp(v1.booleanValue(), v2.booleanValue());
 
} else if (v1.type == 'number' || v2.type == 'number') {
ret = cmp(v1.numberValue(), v2.numberValue());
 
} else {
ret = cmp(v1.stringValue(), v2.stringValue());
}
 
return new BooleanValue(ret);
}
 
function LiteralExpr(value) {
this.value = value;
}
 
LiteralExpr.prototype.evaluate = function(ctx) {
return new StringValue(this.value);
};
 
function NumberExpr(value) {
this.value = value;
}
 
NumberExpr.prototype.evaluate = function(ctx) {
return new NumberValue(this.value);
};
 
function VariableExpr(name) {
this.name = name;
}
 
VariableExpr.prototype.evaluate = function(ctx) {
return ctx.getVariable(this.name);
}
 
// Factory functions for semantic values (i.e. Expressions) of the
// productions in the grammar. When a production is matched to reduce
// the current parse state stack, the function is called with the
// semantic values of the matched elements as arguments, and returns
// another semantic value. The semantic value is a node of the parse
// tree, an expression object with an evaluate() method that evaluates the
// expression in an actual context. These factory functions are used
// in the specification of the grammar rules, below.
 
function makeTokenExpr(m) {
return new TokenExpr(m);
}
 
function passExpr(e) {
return e;
}
 
function makeLocationExpr1(slash, rel) {
rel.absolute = true;
return rel;
}
 
function makeLocationExpr2(dslash, rel) {
rel.absolute = true;
rel.prependStep(makeAbbrevStep(dslash.value));
return rel;
}
 
function makeLocationExpr3(slash) {
var ret = new LocationExpr();
ret.appendStep(makeAbbrevStep('.'));
ret.absolute = true;
return ret;
}
 
function makeLocationExpr4(dslash) {
var ret = new LocationExpr();
ret.absolute = true;
ret.appendStep(makeAbbrevStep(dslash.value));
return ret;
}
 
function makeLocationExpr5(step) {
var ret = new LocationExpr();
ret.appendStep(step);
return ret;
}
 
function makeLocationExpr6(rel, slash, step) {
rel.appendStep(step);
return rel;
}
 
function makeLocationExpr7(rel, dslash, step) {
rel.appendStep(makeAbbrevStep(dslash.value));
return rel;
}
 
function makeStepExpr1(dot) {
return makeAbbrevStep(dot.value);
}
 
function makeStepExpr2(ddot) {
return makeAbbrevStep(ddot.value);
}
 
function makeStepExpr3(axisname, axis, nodetest) {
return new StepExpr(axisname.value, nodetest);
}
 
function makeStepExpr4(at, nodetest) {
return new StepExpr('attribute', nodetest);
}
 
function makeStepExpr5(nodetest) {
return new StepExpr('child', nodetest);
}
 
function makeStepExpr6(step, predicate) {
step.appendPredicate(predicate);
return step;
}
 
function makeAbbrevStep(abbrev) {
switch (abbrev) {
case '//':
return new StepExpr('descendant-or-self', new NodeTestAny);
 
case '.':
return new StepExpr('self', new NodeTestAny);
 
case '..':
return new StepExpr('parent', new NodeTestAny);
}
}
 
function makeNodeTestExpr1(asterisk) {
return new NodeTestElement;
}
 
function makeNodeTestExpr2(ncname, colon, asterisk) {
return new NodeTestNC(ncname.value);
}
 
function makeNodeTestExpr3(qname) {
return new NodeTestName(qname.value);
}
 
function makeNodeTestExpr4(typeo, parenc) {
var type = typeo.value.replace(/\s*\($/, '');
switch(type) {
case 'node':
return new NodeTestAny;
 
case 'text':
return new NodeTestText;
 
case 'comment':
return new NodeTestComment;
 
case 'processing-instruction':
return new NodeTestPI;
}
}
 
function makeNodeTestExpr5(typeo, target, parenc) {
var type = typeo.replace(/\s*\($/, '');
if (type != 'processing-instruction') {
throw type + ' ' + Error().stack;
}
return new NodeTestPI(target.value);
}
 
function makePredicateExpr(pareno, expr, parenc) {
return new PredicateExpr(expr);
}
 
function makePrimaryExpr(pareno, expr, parenc) {
return expr;
}
 
function makeFunctionCallExpr1(name, pareno, parenc) {
return new FunctionCallExpr(name);
}
 
function makeFunctionCallExpr2(name, pareno, arg1, args, parenc) {
var ret = new FunctionCallExpr(name);
ret.appendArg(arg1);
for (var i = 0; i < args.length; ++i) {
ret.appendArg(args[i]);
}
return ret;
}
 
function makeArgumentExpr(comma, expr) {
return expr;
}
 
function makeUnionExpr(expr1, pipe, expr2) {
return new UnionExpr(expr1, expr2);
}
 
function makePathExpr1(filter, slash, rel) {
return new PathExpr(filter, rel);
}
 
function makePathExpr2(filter, dslash, rel) {
rel.prependStep(makeAbbrevStep(dslash.value));
return new PathExpr(filter, rel);
}
 
function makeFilterExpr(expr, predicates) {
if (predicates.length > 0) {
return new FilterExpr(expr, predicates);
} else {
return expr;
}
}
 
function makeUnaryMinusExpr(minus, expr) {
return new UnaryMinusExpr(expr);
}
 
function makeBinaryExpr(expr1, op, expr2) {
return new BinaryExpr(expr1, op, expr2);
}
 
function makeLiteralExpr(token) {
// remove quotes from the parsed value:
var value = token.value.substring(1, token.value.length - 1);
return new LiteralExpr(value);
}
 
function makeNumberExpr(token) {
return new NumberExpr(token.value);
}
 
function makeVariableReference(dollar, name) {
return new VariableExpr(name.value);
}
 
// Used before parsing for optimization of common simple cases. See
// the begin of xpathParse() for which they are.
function makeSimpleExpr(expr) {
if (expr.charAt(0) == '$') {
return new VariableExpr(expr.substr(1));
} else if (expr.charAt(0) == '@') {
var a = new NodeTestName(expr.substr(1));
var b = new StepExpr('attribute', a);
var c = new LocationExpr();
c.appendStep(b);
return c;
} else if (expr.match(/^[0-9]+$/)) {
return new NumberExpr(expr);
} else {
var a = new NodeTestName(expr);
var b = new StepExpr('child', a);
var c = new LocationExpr();
c.appendStep(b);
return c;
}
}
 
function makeSimpleExpr2(expr) {
var steps = expr.split('/');
var c = new LocationExpr();
for (var i in steps) {
var a = new NodeTestName(steps[i]);
var b = new StepExpr('child', a);
c.appendStep(b);
}
return c;
}
 
// The axes of XPath expressions.
 
var xpathAxis = {
ANCESTOR_OR_SELF: 'ancestor-or-self',
ANCESTOR: 'ancestor',
ATTRIBUTE: 'attribute',
CHILD: 'child',
DESCENDANT_OR_SELF: 'descendant-or-self',
DESCENDANT: 'descendant',
FOLLOWING_SIBLING: 'following-sibling',
FOLLOWING: 'following',
NAMESPACE: 'namespace',
PARENT: 'parent',
PRECEDING_SIBLING: 'preceding-sibling',
PRECEDING: 'preceding',
SELF: 'self'
};
 
var xpathAxesRe = [
xpathAxis.ANCESTOR_OR_SELF,
xpathAxis.ANCESTOR,
xpathAxis.ATTRIBUTE,
xpathAxis.CHILD,
xpathAxis.DESCENDANT_OR_SELF,
xpathAxis.DESCENDANT,
xpathAxis.FOLLOWING_SIBLING,
xpathAxis.FOLLOWING,
xpathAxis.NAMESPACE,
xpathAxis.PARENT,
xpathAxis.PRECEDING_SIBLING,
xpathAxis.PRECEDING,
xpathAxis.SELF
].join('|');
 
 
// The tokens of the language. The label property is just used for
// generating debug output. The prec property is the precedence used
// for shift/reduce resolution. Default precedence is 0 as a lookahead
// token and 2 on the stack. TODO(mesch): this is certainly not
// necessary and too complicated. Simplify this!
 
// NOTE: tabular formatting is the big exception, but here it should
// be OK.
 
var TOK_PIPE = { label: "|", prec: 17, re: new RegExp("^\\|") };
var TOK_DSLASH = { label: "//", prec: 19, re: new RegExp("^//") };
var TOK_SLASH = { label: "/", prec: 30, re: new RegExp("^/") };
var TOK_AXIS = { label: "::", prec: 20, re: new RegExp("^::") };
var TOK_COLON = { label: ":", prec: 1000, re: new RegExp("^:") };
var TOK_AXISNAME = { label: "[axis]", re: new RegExp('^(' + xpathAxesRe + ')') };
var TOK_PARENO = { label: "(", prec: 34, re: new RegExp("^\\(") };
var TOK_PARENC = { label: ")", re: new RegExp("^\\)") };
var TOK_DDOT = { label: "..", prec: 34, re: new RegExp("^\\.\\.") };
var TOK_DOT = { label: ".", prec: 34, re: new RegExp("^\\.") };
var TOK_AT = { label: "@", prec: 34, re: new RegExp("^@") };
 
var TOK_COMMA = { label: ",", re: new RegExp("^,") };
 
var TOK_OR = { label: "or", prec: 10, re: new RegExp("^or\\b") };
var TOK_AND = { label: "and", prec: 11, re: new RegExp("^and\\b") };
var TOK_EQ = { label: "=", prec: 12, re: new RegExp("^=") };
var TOK_NEQ = { label: "!=", prec: 12, re: new RegExp("^!=") };
var TOK_GE = { label: ">=", prec: 13, re: new RegExp("^>=") };
var TOK_GT = { label: ">", prec: 13, re: new RegExp("^>") };
var TOK_LE = { label: "<=", prec: 13, re: new RegExp("^<=") };
var TOK_LT = { label: "<", prec: 13, re: new RegExp("^<") };
var TOK_PLUS = { label: "+", prec: 14, re: new RegExp("^\\+"), left: true };
var TOK_MINUS = { label: "-", prec: 14, re: new RegExp("^\\-"), left: true };
var TOK_DIV = { label: "div", prec: 15, re: new RegExp("^div\\b"), left: true };
var TOK_MOD = { label: "mod", prec: 15, re: new RegExp("^mod\\b"), left: true };
 
var TOK_BRACKO = { label: "[", prec: 32, re: new RegExp("^\\[") };
var TOK_BRACKC = { label: "]", re: new RegExp("^\\]") };
var TOK_DOLLAR = { label: "$", re: new RegExp("^\\$") };
 
var TOK_NCNAME = { label: "[ncname]", re: new RegExp('^[a-z][-\\w]*','i') };
 
var TOK_ASTERISK = { label: "*", prec: 15, re: new RegExp("^\\*"), left: true };
var TOK_LITERALQ = { label: "[litq]", prec: 20, re: new RegExp("^'[^\\']*'") };
var TOK_LITERALQQ = {
label: "[litqq]",
prec: 20,
re: new RegExp('^"[^\\"]*"')
};
 
var TOK_NUMBER = {
label: "[number]",
prec: 35,
re: new RegExp('^\\d+(\\.\\d*)?') };
 
var TOK_QNAME = {
label: "[qname]",
re: new RegExp('^([a-z][-\\w]*:)?[a-z][-\\w]*','i')
};
 
var TOK_NODEO = {
label: "[nodetest-start]",
re: new RegExp('^(processing-instruction|comment|text|node)\\(')
};
 
// The table of the tokens of our grammar, used by the lexer: first
// column the tag, second column a regexp to recognize it in the
// input, third column the precedence of the token, fourth column a
// factory function for the semantic value of the token.
//
// NOTE: order of this list is important, because the first match
// counts. Cf. DDOT and DOT, and AXIS and COLON.
 
var xpathTokenRules = [
TOK_DSLASH,
TOK_SLASH,
TOK_DDOT,
TOK_DOT,
TOK_AXIS,
TOK_COLON,
TOK_AXISNAME,
TOK_NODEO,
TOK_PARENO,
TOK_PARENC,
TOK_BRACKO,
TOK_BRACKC,
TOK_AT,
TOK_COMMA,
TOK_OR,
TOK_AND,
TOK_NEQ,
TOK_EQ,
TOK_GE,
TOK_GT,
TOK_LE,
TOK_LT,
TOK_PLUS,
TOK_MINUS,
TOK_ASTERISK,
TOK_PIPE,
TOK_MOD,
TOK_DIV,
TOK_LITERALQ,
TOK_LITERALQQ,
TOK_NUMBER,
TOK_QNAME,
TOK_NCNAME,
TOK_DOLLAR
];
 
// All the nonterminals of the grammar. The nonterminal objects are
// identified by object identity; the labels are used in the debug
// output only.
var XPathLocationPath = { label: "LocationPath" };
var XPathRelativeLocationPath = { label: "RelativeLocationPath" };
var XPathAbsoluteLocationPath = { label: "AbsoluteLocationPath" };
var XPathStep = { label: "Step" };
var XPathNodeTest = { label: "NodeTest" };
var XPathPredicate = { label: "Predicate" };
var XPathLiteral = { label: "Literal" };
var XPathExpr = { label: "Expr" };
var XPathPrimaryExpr = { label: "PrimaryExpr" };
var XPathVariableReference = { label: "Variablereference" };
var XPathNumber = { label: "Number" };
var XPathFunctionCall = { label: "FunctionCall" };
var XPathArgumentRemainder = { label: "ArgumentRemainder" };
var XPathPathExpr = { label: "PathExpr" };
var XPathUnionExpr = { label: "UnionExpr" };
var XPathFilterExpr = { label: "FilterExpr" };
var XPathDigits = { label: "Digits" };
 
var xpathNonTerminals = [
XPathLocationPath,
XPathRelativeLocationPath,
XPathAbsoluteLocationPath,
XPathStep,
XPathNodeTest,
XPathPredicate,
XPathLiteral,
XPathExpr,
XPathPrimaryExpr,
XPathVariableReference,
XPathNumber,
XPathFunctionCall,
XPathArgumentRemainder,
XPathPathExpr,
XPathUnionExpr,
XPathFilterExpr,
XPathDigits
];
 
// Quantifiers that are used in the productions of the grammar.
var Q_01 = { label: "?" };
var Q_MM = { label: "*" };
var Q_1M = { label: "+" };
 
// Tag for left associativity (right assoc is implied by undefined).
var ASSOC_LEFT = true;
 
// The productions of the grammar. Columns of the table:
//
// - target nonterminal,
// - pattern,
// - precedence,
// - semantic value factory
//
// The semantic value factory is a function that receives parse tree
// nodes from the stack frames of the matched symbols as arguments and
// returns an a node of the parse tree. The node is stored in the top
// stack frame along with the target object of the rule. The node in
// the parse tree is an expression object that has an evaluate() method
// and thus evaluates XPath expressions.
//
// The precedence is used to decide between reducing and shifting by
// comparing the precendence of the rule that is candidate for
// reducing with the precedence of the look ahead token. Precedence of
// -1 means that the precedence of the tokens in the pattern is used
// instead. TODO: It shouldn't be necessary to explicitly assign
// precedences to rules.
 
var xpathGrammarRules =
[
[ XPathLocationPath, [ XPathRelativeLocationPath ], 18,
passExpr ],
[ XPathLocationPath, [ XPathAbsoluteLocationPath ], 18,
passExpr ],
 
[ XPathAbsoluteLocationPath, [ TOK_SLASH, XPathRelativeLocationPath ], 18,
makeLocationExpr1 ],
[ XPathAbsoluteLocationPath, [ TOK_DSLASH, XPathRelativeLocationPath ], 18,
makeLocationExpr2 ],
 
[ XPathAbsoluteLocationPath, [ TOK_SLASH ], 0,
makeLocationExpr3 ],
[ XPathAbsoluteLocationPath, [ TOK_DSLASH ], 0,
makeLocationExpr4 ],
 
[ XPathRelativeLocationPath, [ XPathStep ], 31,
makeLocationExpr5 ],
[ XPathRelativeLocationPath,
[ XPathRelativeLocationPath, TOK_SLASH, XPathStep ], 31,
makeLocationExpr6 ],
[ XPathRelativeLocationPath,
[ XPathRelativeLocationPath, TOK_DSLASH, XPathStep ], 31,
makeLocationExpr7 ],
 
[ XPathStep, [ TOK_DOT ], 33,
makeStepExpr1 ],
[ XPathStep, [ TOK_DDOT ], 33,
makeStepExpr2 ],
[ XPathStep,
[ TOK_AXISNAME, TOK_AXIS, XPathNodeTest ], 33,
makeStepExpr3 ],
[ XPathStep, [ TOK_AT, XPathNodeTest ], 33,
makeStepExpr4 ],
[ XPathStep, [ XPathNodeTest ], 33,
makeStepExpr5 ],
[ XPathStep, [ XPathStep, XPathPredicate ], 33,
makeStepExpr6 ],
 
[ XPathNodeTest, [ TOK_ASTERISK ], 33,
makeNodeTestExpr1 ],
[ XPathNodeTest, [ TOK_NCNAME, TOK_COLON, TOK_ASTERISK ], 33,
makeNodeTestExpr2 ],
[ XPathNodeTest, [ TOK_QNAME ], 33,
makeNodeTestExpr3 ],
[ XPathNodeTest, [ TOK_NODEO, TOK_PARENC ], 33,
makeNodeTestExpr4 ],
[ XPathNodeTest, [ TOK_NODEO, XPathLiteral, TOK_PARENC ], 33,
makeNodeTestExpr5 ],
 
[ XPathPredicate, [ TOK_BRACKO, XPathExpr, TOK_BRACKC ], 33,
makePredicateExpr ],
 
[ XPathPrimaryExpr, [ XPathVariableReference ], 33,
passExpr ],
[ XPathPrimaryExpr, [ TOK_PARENO, XPathExpr, TOK_PARENC ], 33,
makePrimaryExpr ],
[ XPathPrimaryExpr, [ XPathLiteral ], 30,
passExpr ],
[ XPathPrimaryExpr, [ XPathNumber ], 30,
passExpr ],
[ XPathPrimaryExpr, [ XPathFunctionCall ], 30,
passExpr ],
 
[ XPathFunctionCall, [ TOK_QNAME, TOK_PARENO, TOK_PARENC ], -1,
makeFunctionCallExpr1 ],
[ XPathFunctionCall,
[ TOK_QNAME, TOK_PARENO, XPathExpr, XPathArgumentRemainder, Q_MM,
TOK_PARENC ], -1,
makeFunctionCallExpr2 ],
[ XPathArgumentRemainder, [ TOK_COMMA, XPathExpr ], -1,
makeArgumentExpr ],
 
[ XPathUnionExpr, [ XPathPathExpr ], 20,
passExpr ],
[ XPathUnionExpr, [ XPathUnionExpr, TOK_PIPE, XPathPathExpr ], 20,
makeUnionExpr ],
 
[ XPathPathExpr, [ XPathLocationPath ], 20,
passExpr ],
[ XPathPathExpr, [ XPathFilterExpr ], 19,
passExpr ],
[ XPathPathExpr,
[ XPathFilterExpr, TOK_SLASH, XPathRelativeLocationPath ], 20,
makePathExpr1 ],
[ XPathPathExpr,
[ XPathFilterExpr, TOK_DSLASH, XPathRelativeLocationPath ], 20,
makePathExpr2 ],
 
[ XPathFilterExpr, [ XPathPrimaryExpr, XPathPredicate, Q_MM ], 20,
makeFilterExpr ],
 
[ XPathExpr, [ XPathPrimaryExpr ], 16,
passExpr ],
[ XPathExpr, [ XPathUnionExpr ], 16,
passExpr ],
 
[ XPathExpr, [ TOK_MINUS, XPathExpr ], -1,
makeUnaryMinusExpr ],
 
[ XPathExpr, [ XPathExpr, TOK_OR, XPathExpr ], -1,
makeBinaryExpr ],
[ XPathExpr, [ XPathExpr, TOK_AND, XPathExpr ], -1,
makeBinaryExpr ],
 
[ XPathExpr, [ XPathExpr, TOK_EQ, XPathExpr ], -1,
makeBinaryExpr ],
[ XPathExpr, [ XPathExpr, TOK_NEQ, XPathExpr ], -1,
makeBinaryExpr ],
 
[ XPathExpr, [ XPathExpr, TOK_LT, XPathExpr ], -1,
makeBinaryExpr ],
[ XPathExpr, [ XPathExpr, TOK_LE, XPathExpr ], -1,
makeBinaryExpr ],
[ XPathExpr, [ XPathExpr, TOK_GT, XPathExpr ], -1,
makeBinaryExpr ],
[ XPathExpr, [ XPathExpr, TOK_GE, XPathExpr ], -1,
makeBinaryExpr ],
 
[ XPathExpr, [ XPathExpr, TOK_PLUS, XPathExpr ], -1,
makeBinaryExpr, ASSOC_LEFT ],
[ XPathExpr, [ XPathExpr, TOK_MINUS, XPathExpr ], -1,
makeBinaryExpr, ASSOC_LEFT ],
 
[ XPathExpr, [ XPathExpr, TOK_ASTERISK, XPathExpr ], -1,
makeBinaryExpr, ASSOC_LEFT ],
[ XPathExpr, [ XPathExpr, TOK_DIV, XPathExpr ], -1,
makeBinaryExpr, ASSOC_LEFT ],
[ XPathExpr, [ XPathExpr, TOK_MOD, XPathExpr ], -1,
makeBinaryExpr, ASSOC_LEFT ],
 
[ XPathLiteral, [ TOK_LITERALQ ], -1,
makeLiteralExpr ],
[ XPathLiteral, [ TOK_LITERALQQ ], -1,
makeLiteralExpr ],
 
[ XPathNumber, [ TOK_NUMBER ], -1,
makeNumberExpr ],
 
[ XPathVariableReference, [ TOK_DOLLAR, TOK_QNAME ], 200,
makeVariableReference ]
];
 
// That function computes some optimizations of the above data
// structures and will be called right here. It merely takes the
// counter variables out of the global scope.
 
var xpathRules = [];
 
function xpathParseInit() {
if (xpathRules.length) {
return;
}
 
// Some simple optimizations for the xpath expression parser: sort
// grammar rules descending by length, so that the longest match is
// first found.
 
xpathGrammarRules.sort(function(a,b) {
var la = a[1].length;
var lb = b[1].length;
if (la < lb) {
return 1;
} else if (la > lb) {
return -1;
} else {
return 0;
}
});
 
var k = 1;
for (var i = 0; i < xpathNonTerminals.length; ++i) {
xpathNonTerminals[i].key = k++;
}
 
for (i = 0; i < xpathTokenRules.length; ++i) {
xpathTokenRules[i].key = k++;
}
 
Log.write('XPath parse INIT: ' + k + ' rules');
 
// Another slight optimization: sort the rules into bins according
// to the last element (observing quantifiers), so we can restrict
// the match against the stack to the subest of rules that match the
// top of the stack.
//
// TODO(mesch): What we actually want is to compute states as in
// bison, so that we don't have to do any explicit and iterated
// match against the stack.
 
function push_(array, position, element) {
if (!array[position]) {
array[position] = [];
}
array[position].push(element);
}
 
for (i = 0; i < xpathGrammarRules.length; ++i) {
var rule = xpathGrammarRules[i];
var pattern = rule[1];
 
for (var j = pattern.length - 1; j >= 0; --j) {
if (pattern[j] == Q_1M) {
push_(xpathRules, pattern[j-1].key, rule);
break;
 
} else if (pattern[j] == Q_MM || pattern[j] == Q_01) {
push_(xpathRules, pattern[j-1].key, rule);
--j;
 
} else {
push_(xpathRules, pattern[j].key, rule);
break;
}
}
}
 
Log.write('XPath parse INIT: ' + xpathRules.length + ' rule bins');
 
var sum = 0;
mapExec(xpathRules, function(i) {
if (i) {
sum += i.length;
}
});
 
Log.write('XPath parse INIT: ' + (sum / xpathRules.length) + ' average bin size');
}
 
// Local utility functions that are used by the lexer or parser.
 
function xpathCollectDescendants(nodelist, node) {
for (var n = node.firstChild; n; n = n.nextSibling) {
nodelist.push(n);
arguments.callee(nodelist, n);
}
}
 
function xpathCollectDescendantsReverse(nodelist, node) {
for (var n = node.lastChild; n; n = n.previousSibling) {
nodelist.push(n);
arguments.callee(nodelist, n);
}
}
 
 
// The entry point for the library: match an expression against a DOM
// node. Returns an XPath value.
function xpathDomEval(expr, node) {
var expr1 = xpathParse(expr);
var ret = expr1.evaluate(new ExprContext(node));
return ret;
}
 
// Utility function to sort a list of nodes. Used by xsltSort() and
// nxslSelect().
function xpathSort(input, sort) {
if (sort.length == 0) {
return;
}
 
var sortlist = [];
 
for (var i = 0; i < input.nodelist.length; ++i) {
var node = input.nodelist[i];
var sortitem = { node: node, key: [] };
var context = input.clone(node, 0, [ node ]);
 
for (var j = 0; j < sort.length; ++j) {
var s = sort[j];
var value = s.expr.evaluate(context);
 
var evalue;
if (s.type == 'text') {
evalue = value.stringValue();
} else if (s.type == 'number') {
evalue = value.numberValue();
}
sortitem.key.push({ value: evalue, order: s.order });
}
 
// Make the sort stable by adding a lowest priority sort by
// id. This is very convenient and furthermore required by the
// spec ([XSLT] - Section 10 Sorting).
sortitem.key.push({ value: i, order: 'ascending' });
 
sortlist.push(sortitem);
}
 
sortlist.sort(xpathSortByKey);
 
var nodes = [];
for (var i = 0; i < sortlist.length; ++i) {
nodes.push(sortlist[i].node);
}
input.nodelist = nodes;
input.setNode(nodes[0], 0);
}
 
 
// Sorts by all order criteria defined. According to the JavaScript
// spec ([ECMA] Section 11.8.5), the compare operators compare strings
// as strings and numbers as numbers.
//
// NOTE: In browsers which do not follow the spec, this breaks only in
// the case that numbers should be sorted as strings, which is very
// uncommon.
 
function xpathSortByKey(v1, v2) {
// NOTE: Sort key vectors of different length never occur in
// xsltSort.
 
for (var i = 0; i < v1.key.length; ++i) {
var o = v1.key[i].order == 'descending' ? -1 : 1;
if (v1.key[i].value > v2.key[i].value) {
return +1 * o;
} else if (v1.key[i].value < v2.key[i].value) {
return -1 * o;
}
}
 
return 0;
}
dot-two-refactor/windmill/js/lib/xpath/misc.js New file
0,0 → 1,255
// Copyright 2005 Google Inc.
// All Rights Reserved
//
// Miscellania that support the ajaxslt implementation.
//
// Author: Steffen Meschkat <mesch@google.com>
//
 
function el(i) {
return document.getElementById(i);
}
 
function px(x) {
return x + 'px';
}
 
// Split a string s at all occurrences of character c. This is like
// the split() method of the string object, but IE omits empty
// strings, which violates the invariant (s.split(x).join(x) == s).
function stringSplit(s, c) {
var a = s.indexOf(c);
if (a == -1) {
return [ s ];
}
 
var parts = [];
parts.push(s.substr(0,a));
while (a != -1) {
var a1 = s.indexOf(c, a + 1);
if (a1 != -1) {
parts.push(s.substr(a + 1, a1 - a - 1));
} else {
parts.push(s.substr(a + 1));
}
a = a1;
}
 
return parts;
}
 
// Returns the text value if a node; for nodes without children this
// is the nodeValue, for nodes with children this is the concatenation
// of the value of all children.
function xmlValue(node) {
if (!node) {
return '';
}
 
var ret = '';
if (node.nodeType == DOM_TEXT_NODE ||
node.nodeType == DOM_CDATA_SECTION_NODE ||
node.nodeType == DOM_ATTRIBUTE_NODE) {
ret += node.nodeValue;
 
} else if (node.nodeType == DOM_ELEMENT_NODE ||
node.nodeType == DOM_DOCUMENT_NODE ||
node.nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
for (var i = 0; i < node.childNodes.length; ++i) {
ret += arguments.callee(node.childNodes[i]);
}
}
return ret;
}
 
// Returns the representation of a node as XML text.
function xmlText(node) {
var ret = '';
if (node.nodeType == DOM_TEXT_NODE) {
ret += xmlEscapeText(node.nodeValue);
 
} else if (node.nodeType == DOM_ELEMENT_NODE) {
ret += '<' + node.nodeName;
for (var i = 0; i < node.attributes.length; ++i) {
var a = node.attributes[i];
if (a && a.nodeName && a.nodeValue) {
ret += ' ' + a.nodeName;
ret += '="' + xmlEscapeAttr(a.nodeValue) + '"';
}
}
 
if (node.childNodes.length == 0) {
ret += '/>';
 
} else {
ret += '>';
for (var i = 0; i < node.childNodes.length; ++i) {
ret += arguments.callee(node.childNodes[i]);
}
ret += '</' + node.nodeName + '>';
}
 
} else if (node.nodeType == DOM_DOCUMENT_NODE ||
node.nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
for (var i = 0; i < node.childNodes.length; ++i) {
ret += arguments.callee(node.childNodes[i]);
}
}
 
return ret;
}
 
// Applies the given function to each element of the array.
function mapExec(array, func) {
for (var i = 0; i < array.length; ++i) {
func(array[i]);
}
}
 
// Returns an array that contains the return value of the given
// function applied to every element of the input array.
function mapExpr(array, func) {
var ret = [];
for (var i = 0; i < array.length; ++i) {
ret.push(func(array[i]));
}
return ret;
};
 
// Reverses the given array in place.
function reverseInplace(array) {
for (var i = 0; i < array.length / 2; ++i) {
var h = array[i];
var ii = array.length - i - 1;
array[i] = array[ii];
array[ii] = h;
}
}
 
// Shallow-copies an array.
function copyArray(dst, src) {
for (var i = 0; i < src.length; ++i) {
dst.push(src[i]);
}
}
 
function assert(b) {
if (!b) {
throw 'assertion failed';
}
}
 
// Based on
// <http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247>
var DOM_ELEMENT_NODE = 1;
var DOM_ATTRIBUTE_NODE = 2;
var DOM_TEXT_NODE = 3;
var DOM_CDATA_SECTION_NODE = 4;
var DOM_ENTITY_REFERENCE_NODE = 5;
var DOM_ENTITY_NODE = 6;
var DOM_PROCESSING_INSTRUCTION_NODE = 7;
var DOM_COMMENT_NODE = 8;
var DOM_DOCUMENT_NODE = 9;
var DOM_DOCUMENT_TYPE_NODE = 10;
var DOM_DOCUMENT_FRAGMENT_NODE = 11;
var DOM_NOTATION_NODE = 12;
 
 
var xpathdebug = false; // trace xpath parsing
var xsltdebug = false; // trace xslt processing
 
 
// Escape XML special markup chracters: tag delimiter < > and entity
// reference start delimiter &. The escaped string can be used in XML
// text portions (i.e. between tags).
function xmlEscapeText(s) {
return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
 
// Escape XML special markup characters: tag delimiter < > entity
// reference start delimiter & and quotes ". The escaped string can be
// used in double quoted XML attribute value portions (i.e. in
// attributes within start tags).
function xmlEscapeAttr(s) {
return xmlEscapeText(s).replace(/\"/g, '&quot;');
}
 
// Escape markup in XML text, but don't touch entity references. The
// escaped string can be used as XML text (i.e. between tags).
function xmlEscapeTags(s) {
return s.replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
 
// An implementation of the debug log.
 
var logging__ = false;
 
function Log() {};
 
Log.lines = [];
 
Log.write = function(s) {
if (logging__) {
this.lines.push(xmlEscapeText(s));
this.show();
}
};
 
// Writes the given XML with every tag on a new line.
Log.writeXML = function(xml) {
if (logging__) {
var s0 = xml.replace(/</g, '\n<');
var s1 = xmlEscapeText(s0);
var s2 = s1.replace(/\s*\n(\s|\n)*/g, '<br/>');
this.lines.push(s2);
this.show();
}
}
 
// Writes without any escaping
Log.writeRaw = function(s) {
if (logging__) {
this.lines.push(s);
this.show();
}
}
 
Log.clear = function() {
if (logging__) {
var l = this.div();
l.innerHTML = '';
this.lines = [];
}
}
 
Log.show = function() {
var l = this.div();
l.innerHTML += this.lines.join('<br/>') + '<br/>';
this.lines = [];
l.scrollTop = l.scrollHeight;
}
 
Log.div = function() {
var l = document.getElementById('log');
if (!l) {
l = document.createElement('div');
l.id = 'log';
l.style.position = 'absolute';
l.style.right = '5px';
l.style.top = '5px';
l.style.width = '250px';
l.style.height = '150px';
l.style.overflow = 'auto';
l.style.backgroundColor = '#f0f0f0';
l.style.border = '1px solid gray';
l.style.fontSize = '10px';
l.style.padding = '5px';
document.body.appendChild(l);
}
return l;
}
 
 
function Timer() {}
Timer.start = function() {}
Timer.end = function() {}
dot-two-refactor/windmill/js/lib/xpath/dom.js New file
0,0 → 1,428
// Copyright 2005 Google Inc.
// All Rights Reserved
//
// An XML parse and a minimal DOM implementation that just supportes
// the subset of the W3C DOM that is used in the XSLT implementation.
//
// References:
//
// [DOM] W3C DOM Level 3 Core Specification
// <http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/>.
//
//
// Author: Steffen Meschkat <mesch@google.com>
 
// NOTE: The split() method in IE omits empty result strings. This is
// utterly annoying. So we don't use it here.
 
// Resolve entities in XML text fragments. According to the DOM
// specification, the DOM is supposed to resolve entity references at
// the API level. I.e. no entity references are passed through the
// API. See "Entities and the DOM core", p.12, DOM 2 Core
// Spec. However, different browsers actually pass very different
// values at the API.
//
function xmlResolveEntities(s) {
 
var parts = stringSplit(s, '&');
 
var ret = parts[0];
for (var i = 1; i < parts.length; ++i) {
var rp = stringSplit(parts[i], ';');
if (rp.length == 1) {
// no entity reference: just a & but no ;
ret += parts[i];
continue;
}
 
var ch;
switch (rp[0]) {
case 'lt':
ch = '<';
break;
case 'gt':
ch = '>';
break;
case 'amp':
ch = '&';
break;
case 'quot':
ch = '"';
break;
case 'apos':
ch = '\'';
break;
case 'nbsp':
ch = String.fromCharCode(160);
break;
default:
// Cool trick: let the DOM do the entity decoding. We assign
// the entity text through non-W3C DOM properties and read it
// through the W3C DOM. W3C DOM access is specified to resolve
// entities.
var span = window.document.createElement('span');
span.innerHTML = '&' + rp[0] + '; ';
ch = span.childNodes[0].nodeValue.charAt(0);
}
ret += ch + rp[1];
}
 
return ret;
}
 
 
// Parses the given XML string with our custom, JavaScript XML parser. Written
// by Steffen Meschkat (mesch@google.com).
function xmlParse(xml) {
Timer.start('xmlparse');
var regex_empty = /\/$/;
 
// See also <http://www.w3.org/TR/REC-xml/#sec-common-syn> for
// allowed chars in a tag and attribute name. TODO(mesch): the
// following is still not completely correct.
 
var regex_tagname = /^([\w:-]*)/;
var regex_attribute = /([\w:-]+)\s?=\s?('([^\']*)'|"([^\"]*)")/g;
 
var xmldoc = new XDocument();
var root = xmldoc;
 
// For the record: in Safari, we would create native DOM nodes, but
// in Opera that is not possible, because the DOM only allows HTML
// element nodes to be created, so we have to do our own DOM nodes.
 
// xmldoc = document.implementation.createDocument('','',null);
// root = xmldoc; // .createDocumentFragment();
// NOTE(mesch): using the DocumentFragment instead of the Document
// crashes my Safari 1.2.4 (v125.12).
var stack = [];
 
var parent = root;
stack.push(parent);
 
var x = stringSplit(xml, '<');
for (var i = 1; i < x.length; ++i) {
var xx = stringSplit(x[i], '>');
var tag = xx[0];
var text = xmlResolveEntities(xx[1] || '');
 
if (tag.charAt(0) == '/') {
stack.pop();
parent = stack[stack.length-1];
 
} else if (tag.charAt(0) == '?') {
// Ignore XML declaration and processing instructions
} else if (tag.charAt(0) == '!') {
// Ignore notation and comments
} else {
var empty = tag.match(regex_empty);
var tagname = regex_tagname.exec(tag)[1];
var node = xmldoc.createElement(tagname);
 
var att;
while (att = regex_attribute.exec(tag)) {
var val = xmlResolveEntities(att[3] || att[4] || '');
node.setAttribute(att[1], val);
}
 
if (empty) {
parent.appendChild(node);
} else {
parent.appendChild(node);
parent = node;
stack.push(node);
}
}
 
if (text && parent != root) {
parent.appendChild(xmldoc.createTextNode(text));
}
}
 
Timer.end('xmlparse');
return root;
}
 
 
// Our W3C DOM Node implementation. Note we call it XNode because we
// can't define the identifier Node. We do this mostly for Opera,
// where we can't reuse the HTML DOM for parsing our own XML, and for
// Safari, where it is too expensive to have the template processor
// operate on native DOM nodes.
function XNode(type, name, value, owner) {
this.attributes = [];
this.childNodes = [];
 
XNode.init.call(this, type, name, value, owner);
}
 
// Don't call as method, use apply() or call().
XNode.init = function(type, name, value, owner) {
this.nodeType = type - 0;
this.nodeName = '' + name;
this.nodeValue = '' + value;
this.ownerDocument = owner;
 
this.firstChild = null;
this.lastChild = null;
this.nextSibling = null;
this.previousSibling = null;
this.parentNode = null;
}
 
XNode.unused_ = [];
 
XNode.recycle = function(node) {
if (!node) {
return;
}
 
if (node.constructor == XDocument) {
XNode.recycle(node.documentElement);
return;
}
 
if (node.constructor != this) {
return;
}
 
XNode.unused_.push(node);
for (var a = 0; a < node.attributes.length; ++a) {
XNode.recycle(node.attributes[a]);
}
for (var c = 0; c < node.childNodes.length; ++c) {
XNode.recycle(node.childNodes[c]);
}
node.attributes.length = 0;
node.childNodes.length = 0;
XNode.init.call(node, 0, '', '', null);
}
 
XNode.create = function(type, name, value, owner) {
if (XNode.unused_.length > 0) {
var node = XNode.unused_.pop();
XNode.init.call(node, type, name, value, owner);
return node;
} else {
return new XNode(type, name, value, owner);
}
}
 
XNode.prototype.appendChild = function(node) {
// firstChild
if (this.childNodes.length == 0) {
this.firstChild = node;
}
 
// previousSibling
node.previousSibling = this.lastChild;
 
// nextSibling
node.nextSibling = null;
if (this.lastChild) {
this.lastChild.nextSibling = node;
}
 
// parentNode
node.parentNode = this;
 
// lastChild
this.lastChild = node;
 
// childNodes
this.childNodes.push(node);
}
 
 
XNode.prototype.replaceChild = function(newNode, oldNode) {
if (oldNode == newNode) {
return;
}
 
for (var i = 0; i < this.childNodes.length; ++i) {
if (this.childNodes[i] == oldNode) {
this.childNodes[i] = newNode;
 
var p = oldNode.parentNode;
oldNode.parentNode = null;
newNode.parentNode = p;
 
p = oldNode.previousSibling;
oldNode.previousSibling = null;
newNode.previousSibling = p;
if (newNode.previousSibling) {
newNode.previousSibling.nextSibling = newNode;
}
 
p = oldNode.nextSibling;
oldNode.nextSibling = null;
newNode.nextSibling = p;
if (newNode.nextSibling) {
newNode.nextSibling.previousSibling = newNode;
}
 
if (this.firstChild == oldNode) {
this.firstChild = newNode;
}
 
if (this.lastChild == oldNode) {
this.lastChild = newNode;
}
 
break;
}
}
}
 
XNode.prototype.insertBefore = function(newNode, oldNode) {
if (oldNode == newNode) {
return;
}
 
if (oldNode.parentNode != this) {
return;
}
 
if (newNode.parentNode) {
newNode.parentNode.removeChild(newNode);
}
 
var newChildren = [];
for (var i = 0; i < this.childNodes.length; ++i) {
var c = this.childNodes[i];
if (c == oldNode) {
newChildren.push(newNode);
 
newNode.parentNode = this;
 
newNode.previousSibling = oldNode.previousSibling;
oldNode.previousSibling = newNode;
if (newNode.previousSibling) {
newNode.previousSibling.nextSibling = newNode;
}
 
newNode.nextSibling = oldNode;
 
if (this.firstChild == oldNode) {
this.firstChild = newNode;
}
}
newChildren.push(c);
}
this.childNodes = newChildren;
}
 
XNode.prototype.removeChild = function(node) {
var newChildren = [];
for (var i = 0; i < this.childNodes.length; ++i) {
var c = this.childNodes[i];
if (c != node) {
newChildren.push(c);
} else {
if (c.previousSibling) {
c.previousSibling.nextSibling = c.nextSibling;
}
if (c.nextSibling) {
c.nextSibling.previousSibling = c.previousSibling;
}
if (this.firstChild == c) {
this.firstChild = c.nextSibling;
}
if (this.lastChild == c) {
this.lastChild = c.previousSibling;
}
}
}
this.childNodes = newChildren;
}
 
 
XNode.prototype.hasAttributes = function() {
return this.attributes.length > 0;
}
 
 
XNode.prototype.setAttribute = function(name, value) {
for (var i = 0; i < this.attributes.length; ++i) {
if (this.attributes[i].nodeName == name) {
this.attributes[i].nodeValue = '' + value;
return;
}
}
this.attributes.push(new XNode(DOM_ATTRIBUTE_NODE, name, value));
}
 
 
XNode.prototype.getAttribute = function(name) {
for (var i = 0; i < this.attributes.length; ++i) {
if (this.attributes[i].nodeName == name) {
return this.attributes[i].nodeValue;
}
}
return null;
}
 
XNode.prototype.removeAttribute = function(name) {
var a = [];
for (var i = 0; i < this.attributes.length; ++i) {
if (this.attributes[i].nodeName != name) {
a.push(this.attributes[i]);
}
}
this.attributes = a;
}
 
 
function XDocument() {
XNode.call(this, DOM_DOCUMENT_NODE, '#document', null, this);
this.documentElement = null;
}
 
XDocument.prototype = new XNode(DOM_DOCUMENT_NODE, '#document');
 
XDocument.prototype.clear = function() {
XNode.recycle(this.documentElement);
this.documentElement = null;
}
 
XDocument.prototype.appendChild = function(node) {
XNode.prototype.appendChild.call(this, node);
this.documentElement = this.childNodes[0];
}
 
XDocument.prototype.createElement = function(name) {
return XNode.create(DOM_ELEMENT_NODE, name, null, this);
}
 
XDocument.prototype.createDocumentFragment = function() {
return XNode.create(DOM_DOCUMENT_FRAGMENT_NODE, '#document-fragment',
null, this);
}
 
XDocument.prototype.createTextNode = function(value) {
return XNode.create(DOM_TEXT_NODE, '#text', value, this);
}
 
XDocument.prototype.createAttribute = function(name) {
return XNode.create(DOM_ATTRIBUTE_NODE, name, null, this);
}
 
XDocument.prototype.createComment = function(data) {
return XNode.create(DOM_COMMENT_NODE, '#comment', data, this);
}
 
XNode.prototype.getElementsByTagName = function(name, list) {
if (!list) {
list = [];
}
 
if (this.nodeName == name) {
list.push(this);
}
 
for (var i = 0; i < this.childNodes.length; ++i) {
this.childNodes[i].getElementsByTagName(name, list);
}
 
return list;
}
dot-two-refactor/windmill/js/lib/mozController.js New file
0,0 → 1,111
/*
Copyright 2006, Open Source Applications Foundation
 
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
 
http://www.apache.org/licenses/LICENSE-2.0
 
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
 
//Mozilla specific controller functions
//more
windmill.controller.what = function() {
alert('Mozilla');
}
 
//Click function for Mozilla with Chrome
windmill.controller.click = function(param_object){
 
var element = this._lookupDispatch(param_object);
if (!element){
return false;
}
 
element.addEventListener('click', function(evt) {
preventDefault = evt.getPreventDefault();
}, false);
 
// Trigger the event.
// And since the DOM order that these actually happen is as follows when a user clicks, we replicate.
windmill.events.triggerMouseEvent(element, 'mousedown', true);
windmill.events.triggerMouseEvent(element, 'mouseup', true);
windmill.events.triggerMouseEvent(element, 'click', true);
 
//Apparently there is some annoying issue with chrome..and this fixes it. Concept from selenium browerbot.
if (!browser.isChrome && !preventDefault) {
if (element.href) {
document.getElementById('webapp').src = element.href;
 
//if the url is calling JS then its ajax and we don't need to wait for any full page load.. hopefully.
if (element.href.indexOf('javascript:', 0) == -1){
windmill.xhr.loopState = 0;
return true;
}
}
/* else if (element.parentNode.href){
document.getElementById('webapp').src = element.parentNode.href;
 
if (element.href.indexOf('javascript:', 0) == -1){
windmill.xhr.loopState = 0;
//element = "true";
return true;
}
}*/
}
 
return true;
 
};
 
//there is a problem with checking via click in safari
windmill.controller.check = function(param_object){
 
return windmill.controller.click(param_object);
}
 
//Radio buttons are even WIERDER in safari, not breaking in FF
windmill.controller.radio = function(param_object){
 
return windmill.controller.click(param_object);
 
}
 
//Double click for Mozilla
windmill.controller.doubleClick = function(param_object) {
 
//Look up the dom element, return false if its not there so we can report failure
var element = this._lookupDispatch(param_object);
if (!element){
return false;
}
 
windmill.events.triggerEvent(element, 'focus', false);
 
// Trigger the mouse event.
windmill.events.triggerMouseEvent(element, 'dblclick', true);
 
/*if (this._windowClosed()) {
return;
}*/
 
windmill.events.triggerEvent(element, 'blur', false);
 
return true;
};
 
/**
* In non-IE browsers, getElementById() does not search by name. Instead, we
* we search separately by id and name.
*/
windmill.controller.locateElementByIdentifier = function(identifier, inDocument, inWindow) {
return windmill.controller.locateElementById(identifier, inDocument, inWindow)
|| windmill.controller.locateElementByName(identifier, inDocument, inWindow)
|| null;
};
Property changes : Added: svn:eol-style + native
dot-two-refactor/windmill/js/lib/browserdetect.js New file
0,0 → 1,136
/*
Copyright 2006, Open Source Applications Foundation
 
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
 
http://www.apache.org/licenses/LICENSE-2.0
 
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
 
function BrowserDetect() {
var ua = navigator.userAgent.toLowerCase();
this.current_ua = ua;
// browser name
this.isGecko = (ua.indexOf('gecko') != -1 && ua.indexOf('safari') == -1);
this.isMozilla = (this.isGecko && ua.indexOf('gecko/') + 14 == ua.length);
this.isNS = ( (this.isGecko) ? (ua.indexOf('netscape') != -1) : ( (ua.indexOf('mozilla') != -1) && (ua.indexOf('spoofer') == -1) && (ua.indexOf('compatible') == -1) && (ua.indexOf('opera') == -1) && (ua.indexOf('webtv') == -1) && (ua.indexOf('hotjava') == -1) ) );
this.isIE = ( (ua.indexOf('msie') != -1) && (ua.indexOf('opera') == -1) && (ua.indexOf('webtv') == -1) );
this.isSafari = (ua.indexOf('safari') != - 1);
this.isOpera = (ua.indexOf('opera') != -1);
this.isKonqueror = (ua.indexOf('konqueror') != -1 && !this.isSafari);
this.isIcab = (ua.indexOf('icab') != -1);
this.isAol = (ua.indexOf('aol') != -1);
this.altKeyDown = false;
this.controlKeyDown = false;
this.shiftKeyDown = false;
 
//Chrome
var checkChrome = function() {
var loc = window.document.location.href;
try {
loc = window.top.document.location.href;
} catch (e) {
// can't see the top (that means we might be chrome, but it's impossible to be sure)
this.isChromeDetectable = "no, top location couldn't be read in this window";
}
 
if (/^chrome:\/\//.test(loc)) {
this.isChrome = true;
} else {
this.isChrome = false;
}
}
 
// spoofing and compatible browsers
this.isIECompatible = ( (ua.indexOf('msie') != -1) && !this.isIE);
this.isNSCompatible = ( (ua.indexOf('mozilla') != -1) && !this.isNS && !this.isMozilla);
 
// browser version
this.versionMinor = parseFloat(navigator.appVersion);
 
// correct version number
if (this.isNS && this.isGecko) {
this.versionMinor = parseFloat( ua.substring( ua.lastIndexOf('/') + 1 ) );
}
else if (this.isIE && this.versionMinor >= 4) {
this.versionMinor = parseFloat( ua.substring( ua.indexOf('msie ') + 5 ) );
}
else if (this.isMozilla) {
this.versionMinor = parseFloat( ua.substring( ua.indexOf('rv:') + 3 ) );
}
else if (this.isSafari) {
this.versionMinor = parseFloat( ua.substring( ua.lastIndexOf('/') + 1 ) );
}
else if (this.isOpera) {
if (ua.indexOf('opera/') != -1) {
this.versionMinor = parseFloat( ua.substring( ua.indexOf('opera/') + 6 ) );
}
else {
this.versionMinor = parseFloat( ua.substring( ua.indexOf('opera ') + 6 ) );
}
}
else if (this.isKonqueror) {
this.versionMinor = parseFloat( ua.substring( ua.indexOf('konqueror/') + 10 ) );
}
else if (this.isIcab) {
if (ua.indexOf('icab/') != -1) {
this.versionMinor = parseFloat( ua.substring( ua.indexOf('icab/') + 6 ) );
}
else {
this.versionMinor = parseFloat( ua.substring( ua.indexOf('icab ') + 6 ) );
}
}
 
this.versionMajor = parseInt(this.versionMinor);
this.geckoVersion = ( (this.isGecko) ? ua.substring( (ua.lastIndexOf('gecko/') + 6), (ua.lastIndexOf('gecko/') + 14) ) : -1 );
// GECKO REVISION
this.geckoRevision = -1;
if (this.isGecko) {
temp = ua.split("rv:");
this.geckoRevision = parseFloat(temp[1]);
}
 
// dom support
this.isDOM1 = (document.getElementById);
this.isDOM2Event = (document.addEventListener && document.removeEventListener);
 
// css compatibility mode
this.mode = document.compatMode ? document.compatMode : 'BackCompat';
 
// platform
this.isWin = (ua.indexOf('win') != -1);
this.isWin32 = (this.isWin && ( ua.indexOf('95') != -1 || ua.indexOf('98') != -1 || ua.indexOf('nt') != -1 || ua.indexOf('win32') != -1 || ua.indexOf('32bit') != -1 || ua.indexOf('xp') != -1) );
this.isMac = (ua.indexOf('mac') != -1);
this.isUnix = (ua.indexOf('unix') != -1 || ua.indexOf('sunos') != -1 || ua.indexOf('bsd') != -1 || ua.indexOf('x11') != -1)
this.isLinux = (ua.indexOf('linux') != -1);
 
// specific browser shortcuts
this.isNS4x = (this.isNS && this.versionMajor == 4);
this.isNS40x = (this.isNS4x && this.versionMinor < 4.5);
this.isNS47x = (this.isNS4x && this.versionMinor >= 4.7);
this.isNS4up = (this.isNS && this.versionMinor >= 4);
this.isNS6x = (this.isNS && this.versionMajor == 6);
this.isNS6up = (this.isNS && this.versionMajor >= 6);
this.isNS7x = (this.isNS && this.versionMajor == 7);
this.isNS7up = (this.isNS && this.versionMajor >= 7);
 
this.isIE4x = (this.isIE && this.versionMajor == 4);
this.isIE4up = (this.isIE && this.versionMajor >= 4);
this.isIE5x = (this.isIE && this.versionMajor == 5);
this.isIE55 = (this.isIE && this.versionMinor == 5.5);
this.isIE55up = (this.isIE && this.versionMinor >= 5.5);
this.isIE5up = (this.isIE && this.versionMajor >= 5);
this.isIE6x = (this.isIE && this.versionMajor == 6);
this.isIE6up = (this.isIE && this.versionMajor >= 6);
 
this.isIE4xMac = (this.isIE4x && this.isMac);
}
 
var browser = new BrowserDetect();
\ No newline at end of file Property changes : Added: svn:executable + * Added: svn:eol-style + native
dot-two-refactor/windmill/js/lib/safController.js New file
0,0 → 1,97
/*
Copyright 2006, Open Source Applications Foundation
 
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
 
http://www.apache.org/licenses/LICENSE-2.0
 
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
 
//Safari specific controller functions
 
windmill.controller.what = function() {
alert('Safari');
}
 
/**
* In non-IE browsers, getElementById() does not search by name. Instead, we
* we search separately by id and name.
*/
windmill.controller.locateElementByIdentifier = function(identifier, inDocument, inWindow) {
return PageBot.prototype._locateElementById(identifier, inDocument, inWindow)
|| PageBot.prototype._locateElementByName(identifier, inDocument, inWindow)
|| null;
};
 
//there is a problem with checking via click in safari
windmill.controller.check = function(param_object){
var element = this._lookupDispatch(param_object);
 
if (element.checked == true){
element.checked = false;
}
else {
element.checked = true;
}
 
return true;
}
 
//Radio buttons are even WIERDER in safari
windmill.controller.radio = function(param_object){
var element = this._lookupDispatch(param_object);
 
element.checked = true;
 
return true;
}
 
//Safari Click function
windmill.controller.click = function(param_object){
 
var element = this._lookupDispatch(param_object);
if (!element){
return false;
}
// Trigger the event.
// And since the DOM order that these actually happen is as follows when a user clicks, we replicate.
windmill.events.triggerMouseEvent(element, 'mousedown', true);
windmill.events.triggerMouseEvent(element, 'mouseup', true);
windmill.events.triggerMouseEvent(element, 'click', true);
 
if (element.href && (element.href.indexOf('javascript:', 0) == -1)){
windmill.xhr.loopState = 0;
}
 
return true;
};
 
//Double click for Safari
windmill.controller.doubleClick = function(param_object) {
 
var element = this._lookupDispatch(param_object);
if (!element){
return false;
}
 
windmill.events.triggerEvent(element, 'focus', false);
 
// Trigger the mouse event.
windmill.events.triggerMouseEvent(element, 'dblclick', true);
 
/* if (this._windowClosed()) {
return;
}
*/
windmill.events.triggerEvent(element, 'blur', false);
 
return true;
};
 
Property changes : Added: svn:eol-style + native
dot-two-refactor/windmill/js/lib/ui.js New file
0,0 → 1,411
/*
Copyright 2006, Open Source Applications Foundation
 
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
 
http://www.apache.org/licenses/LICENSE-2.0
 
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
 
//Functionality that works for every browser
//Mozilla specific functionality abstracted to mozcontroller.js
//Safari specific functionality abstracted to safcontroller.js
//IE specific functionality abstracted to iecontroller.js
 
//The reason for this is that the start page only includes the one corresponding
//to the current browser, this means that the functionality in the controller
//object is only for the current browser, and there is only one copy of the code being
//loaded into the browser for performance.
 
 
windmill.ui = new function() {
 
//Needed to keep track of the old border for the dom explorer
var domExplorerBorder = null;
//keeping track of the recorder state when a new page is loaded and wipes the document
var recordState = false;
 
this.donothing = function(){
return;
}
 
//Run code and manage its result
this.Run = function(){
 
var jstext = windmill.remote.document.getElementById("jsrunner");
 
var command_string = jstext.value;
var array_commands = command_string.split("\n")
//alert(array_commands[0]);
 
//This loop works if there are multiple actions on a page
//But if involves page reloads since these are instantly
//dispatched it probably wont wait long enough to get UI elements
//Even with a wait command
for (var i=0;i<array_commands.length;i++)
{
if (array_commands[i]){
result = true;
var run_obj = eval('(' + array_commands[i] + ')');
try{
if (run_obj.method.indexOf('.') != -1){
var mArray = run_obj.method.split(".");
result = windmill.controller[mArray[0]][mArray[1]](run_obj.params);
}
else{ result = windmill.controller[run_obj.method](run_obj.params); }
}
catch (e) {
result = false;
windmill.ui.writeResult(run_obj.method + ' <font color="#FF0000">Method failed, '+ e +'</font>' );
}
if (result == true){ windmill.ui.writeResult(run_obj.method + '<font color="#69d91f"><b> Succeeded.</b></font>' ); }
}
}
 
}
 
//Clearing runner box
this.clearJs = function(){
var jstext = windmill.remote.document.getElementById("jsrunner");
jstext.value = "";
}
 
//Toggle Pause
this.toggleLoopButtonText = function(){
var loopButton = windmill.remote.document.getElementById("loopButton");
if (loopButton.value == "Loop Stopped"){
loopButton.value = "Loop Running";
 
}
else{
loopButton.value = "Loop Stopped";
}
 
}
 
//Writing to the performance tab
this.writePerformance = function(str){
var resultsDiv = windmill.remote.document.getElementById("tab3");
resultsDiv.innerHTML = str + "<br>" + resultsDiv.innerHTML
//resultsDiv.scrollTop = resultsDiv.scrollHeight;
}
 
this.writeStatus = function(str){
windmill.remote.document.getElementById("runningStatus").innerHTML = str;
}
 
//Writing to the results tab
this.writeResult = function(str){
var resultsDiv = windmill.remote.document.getElementById("tab4");
resultsDiv.innerHTML = str + "<br>" + resultsDiv.innerHTML;
//resultsDiv.scrollTop = resultsDiv.scrollHeight;
}
 
//Allowing the stopOnFailure switch to be controlled from the UI
this.toggleBreak = function(){
var breakCheckBox = windmill.remote.document.getElementById('toggleBreak');
if (breakCheckBox.checked){
windmill.stopOnFailure = true;
}
else{
windmill.stopOnFailure = false;
}
}
 
//Display the id in the remote
this.setIdInRemote = function(e){
//console.log (e);
if(e.target.id != ""){
windmill.remote.document.getElementById("domExp").innerHTML = "ID: "+ e.target.id;
}
else{
windmill.remote.document.getElementById("domExp").innerHTML = "Name: "+ e.target.nodeName;
}
windmill.ui.domExplorerBorder = e.target.style.border;
e.target.style.border = "1px solid yellow";
}
 
//Reset the border to what it was before the mouse over
this.resetBorder = function(e){
e.target.style.border = windmill.ui.domExplorerBorder;
}
 
//Set the listeners for the dom explorer
this.domExplorerOn = function(){
//fleegix.event.listen(windmill.testingApp.document, 'onmouseover', windmill.ui, 'setIdInRemote');
fleegix.event.listen(windmill.testingApp.document, 'onmouseover', windmill.ui, 'setIdInRemote');
fleegix.event.listen(windmill.testingApp.document, 'onmouseout', windmill.ui, 'resetBorder');
 
}
 
//Remove the listeners for the dom explorer
this.domExplorerOff = function(){
fleegix.event.unlisten(windmill.testingApp.document, 'onmouseover', windmill.ui, 'setIdInRemote');
fleegix.event.unlisten(windmill.testingApp.document, 'onmouseout', windmill.ui, 'resetBorder');
}
 
this.scrollRecorderTextArea = function() {
var obj=windmill.remote.document.getElementById("wmTest");
obj.scrollTop=obj.scrollHeight;
}
 
//write json to the remote from the click events
this.writeJsonClicks = function(e){
//console.log(e);
//alert('now');
var locator = '';
var locValue = '';
if (e.target.id != ""){
locator = 'id';
locValue = e.target.id;
}
else if (e.target.name != ""){
locator = 'name';
locValue = e.target.name;
}
else if (e.target.innerHTML.match('href') != 'null'){
locator = 'link';
locValue = e.target.innerHTML.replace(/(<([^>]+)>)/ig,"");
locValue = locValue.replace("\n", "");
}
else{
locator = 'Couldnt Detect';
locValue = 'Couldnt Detect';
}
if (locValue != ""){
if(e.type == 'dblclick'){
windmill.remote.document.getElementById("wmTest").value = windmill.remote.document.getElementById("wmTest").value + '{"method": "doubleClick", "params":{"'+locator+'": "'+locValue+'"}}\n';
}
else{
//console.log(e.target.parentNode);
if (windmill.remote.document.getElementById("clickOn").checked == true){
windmill.remote.document.getElementById("wmTest").value = windmill.remote.document.getElementById("wmTest").value + '{"method": "'+e.type+'", "params":{"'+locator+'": "'+locValue+'"}}\n';
 
}
else if ((e.target.onclick != null) || (locator == 'link') || (e.target.type == 'image')){
//alert(typeof(e.target.onclick));
windmill.remote.document.getElementById("wmTest").value = windmill.remote.document.getElementById("wmTest").value + '{"method": "'+e.type+'", "params":{"'+locator+'": "'+locValue+'"}}\n';
}
}
}
windmill.ui.scrollRecorderTextArea();
 
}
 
//Writing json to the remote for the change events
this.writeJsonChange = function(e){
//console.log(e);
 
var locator = '';
var locValue = '';
if (e.target.id != ""){
locator = 'id';
locValue = e.target.id;
}
else if (e.target.name != ""){
locator = 'name';
locValue = e.target.nodeName;
}
else{
locator = 'Couldnt Detect';
locValue = 'Couldnt Detect';
}
 
if (e.target.type == 'textarea'){
windmill.remote.document.getElementById("wmTest").value = windmill.remote.document.getElementById("wmTest").value + '{"method": "type", "params":{"'+locator+'": "'+locValue+'","text": "'+e.target.value+'"}}\n';
}
else if (e.target.type == 'text'){
windmill.remote.document.getElementById("wmTest").value = windmill.remote.document.getElementById("wmTest").value + '{"method": "type", "params":{"'+locator+'": "'+locValue+'","text": "'+e.target.value+'"}}\n';
}
else if (e.target.type == 'password'){
windmill.remote.document.getElementById("wmTest").value = windmill.remote.document.getElementById("wmTest").value + '{"method": "type", "params":{"'+locator+'": "'+locValue+'","text": "'+e.target.value+'"}}\n';
}
else if(e.target.type == 'select-one'){
windmill.remote.document.getElementById("wmTest").value = windmill.remote.document.getElementById("wmTest").value + '{"method": "select", "params":{"'+locator+'": "'+locValue+'","option": "'+e.target.value+'"}}\n';
}
else if(e.target.type == 'radio'){
windmill.remote.document.getElementById("wmTest").value = windmill.remote.document.getElementById("wmTest").value + '{"method": "radio", "params":{"'+locator+'": "'+locValue+'"}}\n';
}
else if(e.target.type == "checkbox"){
windmill.remote.document.getElementById("wmTest").value = windmill.remote.document.getElementById("wmTest").value + '{"method": "check", "params":{"'+locator+'": "'+locValue+'"}}\n';
}
 
windmill.ui.scrollRecorderTextArea();
 
}
 
this.writeJsonDragDown = function(e){
//console.log(e)
 
windmill.remote.document.getElementById("wmTest").value = windmill.remote.document.getElementById("wmTest").value + '{"method": "dragDropXY", "params": {"source" : ['+e.clientX+','+e.clientY+'],';
}
 
this.writeJsonDragUp = function(e){
//console.log(e);
windmill.remote.document.getElementById("wmTest").value = windmill.remote.document.getElementById("wmTest").value + '"destination": ['+e.clientX+','+e.clientY+']}}\n';
 
}
 
//Turn on the recorder
//Since the click event does things like firing twice when a double click goes also
//and can be obnoxious im enabling it to be turned off and on with a toggle check box
this.recordOn = function(){
//Turn off the listeners so that we don't have multiple attached listeners for the same event
windmill.ui.recordOff();
 
//keep track of the recorder state, for page refreshes
windmill.ui.recordState = true;
 
fleegix.event.listen(windmill.testingApp.document, 'ondblclick', windmill.ui, 'writeJsonClicks');
fleegix.event.listen(windmill.testingApp.document, 'onchange', windmill.ui, 'writeJsonChange');
fleegix.event.listen(windmill.testingApp.document, 'onblur', windmill.ui, 'writeJsonChange');
fleegix.event.listen(windmill.testingApp.document, 'onclick', windmill.ui, 'writeJsonClicks');
 
//We need to set these listeners on all iframes inside the testing app, per bug 32
var iframeCount = windmill.testingApp.window.frames.length;
var iframeArray = windmill.testingApp.window.frames;
 
for (var i=0;i<iframeCount;i++)
{
try{
fleegix.event.listen(iframeArray[i], 'ondblclick', windmill.ui, 'writeJsonClicks');
fleegix.event.listen(iframeArray[i], 'onchange', windmill.ui, 'writeJsonChange');
fleegix.event.listen(iframeArray[i], 'onclick', windmill.ui, 'writeJsonClicks');
fleegix.event.listen(iframeArray[i], 'onblur', windmill.ui, 'writeJsonChange');
 
}
catch(error){
windmill.ui.writeResult('There was a problem binding to one of your iframes, is it cross domain? Binding to all others.' + error);
}
 
}
 
}
 
this.recordOff = function(){
 
windmill.ui.recordState = false;
fleegix.event.unlisten(windmill.testingApp.document, 'ondblclick', windmill.ui, 'writeJsonClicks');
fleegix.event.unlisten(windmill.testingApp.document, 'onchange', windmill.ui, 'writeJsonChange');
fleegix.event.unlisten(windmill.testingApp.document, 'onclick', windmill.ui, 'writeJsonClicks');
fleegix.event.unlisten(windmill.testingApp.document, 'onblur', windmill.ui, 'writeJsonChange');
 
//fleegix.event.unlisten(windmill.testingApp.document, 'onmousedown', windmill.ui, 'writeJsonDragDown');
//fleegix.event.unlisten(windmill.testingApp.document, 'onmouseup', windmill.ui, 'writeJsonDragUp');
 
//We need to disable these listeners on all iframes inside the testing app, per bug 32
var iframeCount = windmill.testingApp.window.frames.length;
var iframeArray = windmill.testingApp.window.frames;
 
for (var i=0;i<iframeCount;i++)
{
try{
fleegix.event.unlisten(iframeArray[i], 'ondblclick', windmill.ui, 'writeJsonClicks');
fleegix.event.unlisten(iframeArray[i], 'onchange', windmill.ui, 'writeJsonChange');
fleegix.event.unlisten(iframeArray[i], 'onclick', windmill.ui, 'writeJsonClicks');
fleegix.event.unlisten(iframeArray[i], 'onblur', windmill.ui, 'writeJsonClicks');
 
}
catch(error){
windmill.ui.writeResult('There was a problem binding to one of your iframes, is it cross domain? Binding to all others.' + error);
}
 
}
 
}
 
this.setRecState = function(){
if (windmill.ui.recordState == true){
windmill.ui.recordOn();
}
}
 
//Handle key listeners for windmill remote shortcuts
this.remoteKeyDown = function(e){
var tabNum = parseInt(windmill.remote.tabs.aid.replace('tab',''));
 
//If they are pressing ctrl and left arrow
if ((e.keyCode == 37) && (e.ctrlKey == true)){
tabNum = tabNum - 1;
if (tabNum == 0){
tabNum = 8;
}
var focusTab = 'tab'+ tabNum;
windmill.remote.tabs.focus(focusTab);
}
//if they are pressing ctrl and right arrow
if ((e.keyCode == 39) && (e.ctrlKey == true)){
tabNum = tabNum + 1;
if (tabNum == 9){
tabNum = 1;
}
var focusTab = 'tab'+ tabNum;
windmill.remote.tabs.focus(focusTab);
}
 
//keyboard shortcut to run your recorded test
if ((e.keyCode == 82) && (e.ctrlKey == true)){
if (windmill.remote.document.getElementById('wmTest').value != ""){
windmill.ui.sendPlayBack();
}
}
 
//ctrl b, gets the action from the builder and adds it to the recorder
if ((e.keyCode == 66) && (e.ctrlKey == true)){
if (windmill.remote.document.getElementById('methodDD').value != ""){
windmill.builder.addToRecorder();
}
}
 
}
 
//Quickly bring the remote to focus
this.getRemote = function(e){
 
if ((e.charCode == 96) && (e.ctrlKey == true)){
windmill.remote.alert('Here I am!');
//windmill.remote.close();
}
}
 
//Send the tests to be played back
this.sendPlayBack = function (){
var testArray = windmill.remote.document.getElementById('wmTest').value.split("\n");
if (testArray[testArray.length-1] == ""){
testArray.pop();
}
 
windmill.ui.recordOff();
 
var resp = function(str){
 
var respRun = function(str){
return true;
}
 
var json_object = new windmill.xhr.json_call('1.1', 'run_json_tests');
var params_obj = {};
params_obj.tests = testArray;
json_object.params = params_obj;
var json_string = fleegix.json.serialize(json_object)
fleegix.xhr.doPost(respRun, '/windmill-jsonrpc/', json_string);
 
}
 
var json_object = new windmill.xhr.json_call('1.1', 'clear_queue');
var params_obj = {};
json_object.params = params_obj;
var json_string = fleegix.json.serialize(json_object)
json_string = json_string.replace('\\', '');
fleegix.xhr.doPost(resp, '/windmill-jsonrpc/', json_string);
 
}
 
}
\ No newline at end of file
dot-two-refactor/windmill/js/lib/windmill.js New file
0,0 → 1,62
/*
Copyright 2006, Open Source Applications Foundation
 
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
 
http://www.apache.org/licenses/LICENSE-2.0
 
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
 
var windmill = new function () {
var browser = null;
 
this.init = function (b){
browser = b;
}
 
//More namespacing
this.builder={};
this.helpers={};
 
//So that users can generate a user for the current test randomly
//Usually you want one username for the test so you can set it once and leave it for the test
//a user can write a method to set overWrite to true if they want to replace it each time %random% is used in a json test
this.randomRegistry = new function (){
var string = null;
var overWrite = false;
}
 
//The app your testing
this.testingApp = parent.frames['webapp'];
 
this.Start = function(){
//Index page load report
load_timer.endTime();
 
windmill.ui.writeResult("<br>Start UI output session.<br> <b>User Environment: " + browser.current_ua + ".</b><br>");
windmill.ui.writePerformance("<br>Starting UI performance session.<br> <b>User Environment: " + browser.current_ua + ".</b><br>");
load_timer.write();
 
fleegix.event.listen(windmill.remote.document, 'onkeydown', windmill.ui, 'remoteKeyDown');
//fleegix.event.listen(windmill.remote.document, 'onkeypress', windmill.ui, 'remoteKeyPress');
fleegix.event.listen(windmill.testingApp.document, 'onkeydown', windmill.ui, 'getRemote');
 
}
 
//windmill Options to be set
this.stopOnFailure = false;
this.showRemote = true;
 
};
 
//Set the browser
windmill.init(browser);
 
 
Property changes : Added: svn:eol-style + native
dot-two-refactor/windmill/js/lib/controller.js New file
0,0 → 1,836
/*
Copyright 2006, Open Source Applications Foundation
 
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
 
http://www.apache.org/licenses/LICENSE-2.0
 
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
 
/*
* Copyright 2004 ThoughtWorks, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
 
/*
Functionality that works for every browser
Mozilla specific functionality abstracted to mozcontroller.js
Safari specific functionality abstracted to safcontroller.js
IE specific functionality abstracted to iecontroller.js
 
The reason for this is that the start page only includes the one corresponding
to the current browser, this means that the functionality in the controller
object is only for the current browser, and there is only one copy of the code being
loaded into the browser for performance.
*/
 
windmill.controller = new function () {
 
this.extensions = {};
this.commands = {};
this.optionLocatorFactory = new OptionLocatorFactory();
 
 
/*******************************
/* Helper functions, non user facing
/* Note: the getControllerMethods command above returns a list of all the user facing functions to the user
/* And the ones that start with an underscore are ignored in that list
/* So if you are adding functionality for internal use and doesnt map from json please start with _
/*******************************/
this._getDocument = function() { return windmill.testingApp.document; }
this._getCurrentWindow = function() { return parent; }
this._getTitle = function() {
var t = this._getDocument().title;
if (typeof(t) == "string") {
t = t.trim();
}
return t;
}
 
//Translates from the way we are passing objects to functions to the lookups
this._lookupDispatch = function(param_object){
 
var element = null;
//If a link was passed, lookup as link
if(typeof param_object.link != "undefined") {
element = this.findElement("link=" + param_object.link)
}
 
//if xpath was passed, lookup as xpath
if(typeof param_object.xpath != "undefined") {
element = this.findElement("xpath=" + param_object.xpath)
}
 
//if id was passed, do as such
if(typeof param_object.id != "undefined") {
element = this.findElement("id=" + param_object.id)
}
 
//if jsid was passed
if(typeof param_object.jsid != "undefined") {
var jsid;
eval ("jsid=" + param_object.jsid + ";");
element = this.findElement("id=" + jsid);
}
 
//if name was passed
if(typeof param_object.name != "undefined") {
element = this.findElement("name=" + param_object.name)
}
 
 
return element;
};
 
this._randomString = function(){
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
var string_length = 8;
var randomstring = '';
for (var i=0; i<string_length; i++) {
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum,rnum+1);
}
return randomstring;
}
 
//Function to handle the random keyword scenario
this._handleRandom = function(actualValue){
if (actualValue.indexOf("%random%") != -1){
 
if((windmill.randomRegistry.string == null)){
windmill.randomRegistry.string = this._randomString();
}
if((windmill.randomRegistry.string != null) && (windmill.randomRegistry.overWrite == true)){
windmill.randomRegistry.string = this._randomString();
}
 
actualValue = actualValue.replace("%random%", windmill.randomRegistry.string);
}
 
return actualValue;
}
 
/************************************
/* User facing windmill functionality
/************************************/
this.defer = function(){
//We may want to somehow display that the loop is being deferred but right now it was too messy in output.
//windmill.ui.writeResult('Deferring..')
};
 
//After a page is done loading, continue the loop
this.continueLoop = function(){
windmill.xhr.loopState = 1;
windmill.xhr.startJsonLoop();
};
 
//open an url in the webapp iframe
this.open = function(param_object) {
webappframe = document.getElementById('webapp');
url = this._handleRandom(param_object.url);
 
webappframe.src = url;
 
//Turn off loop until the onload for the iframe restarts it
windmill.xhr.loopState = 0;
return true;
};
 
 
//Currently only does one level below the provided div
//To make it more thorough it needs recursion to be implemented later
this.verify = function(param_object) {
 
var n = this._lookupDispatch(param_object);
var validator = param_object.validator;
 
try{
if (n.nodeType == 3) {
 
//If the validator string was found we return true
if (n.nodeValue.indexOf(validator, 0) != -1){
return true;
}
//If the validator string was found we return true
if (n.innerHTML.indexOf(validator) != -1){
return true;
}
}
 
 
else{
for(var m = n.firstChild; m != null; m = m.nextSibling) {
 
//alert(m.nodeValue);
//If the validator string was found we return true
if (m.nodeValue.indexOf(validator, 0) != -1){
return true;
}
}
}
}
catch(error){return false;}
return true;
};
 
 
//Type Function
this.type = function(param_object){
 
var element = this._lookupDispatch(param_object);
if (!element){
return false;
}
//Get the focus on to the item to be typed in, or selected
windmill.events.triggerEvent(element, 'focus', false);
windmill.events.triggerEvent(element, 'select', true);
 
//Make sure text fits in the textbox
var maxLengthAttr = element.getAttribute("maxLength");
var actualValue = param_object.text;
var stringValue = param_object.text;
 
if (maxLengthAttr != null) {
var maxLength = parseInt(maxLengthAttr);
if (stringValue.length > maxLength) {
//truncate it to fit
actualValue = stringValue.substr(0, maxLength);
}
}
 
actualValue = this._handleRandom(actualValue);
 
//Set the value
element.value = actualValue;
 
// DGF this used to be skipped in chrome URLs, but no longer. Is xpcnativewrappers to blame?
//Another wierd chrome thing?
windmill.events.triggerEvent(element, 'change', true);
 
return true;
};
 
//Wait function
this.wait = function(param_object){
done = function(){
return true;
}
setTimeout("done()", param_object.milliseconds);
 
return true;
};
 
//Initial stab at selector functionality, taken from selenium-browserbot.js
/*
* Select the specified option and trigger the relevant events of the element.
*/
this.select = function(param_object) {
var element = this._lookupDispatch(param_object);
 
if (!element){
return false;
}
 
/*if (!("options" in element)) {
//throw new SeleniumError("Specified element is not a Select (has no options)");
 
}*/
 
var locator = this.optionLocatorFactory.fromLocatorString('label=' + param_object.option);
 
var optionToSelect = locator.findOption(element);
 
windmill.events.triggerEvent(element, 'focus', false);
var changed = false;
for (var i = 0; i < element.options.length; i++) {
var option = element.options[i];
if (option.selected && option != optionToSelect) {
option.selected = false;
changed = true;
}
else if (!option.selected && option == optionToSelect) {
option.selected = true;
changed = true;
}
}
 
if (changed) {
windmill.events.triggerEvent(element, 'change', true);
}
 
return true;
};
 
//Drag Drop functionality allowing functions passed to calculate cursor offsets
this.dragDrop = function(param_object){
 
 
var p = param_object;
var hash_key;
 
eval ("hash_key=" + p.dragged.jsid + ";");
p.dragged.id = hash_key;
delete p.dragged.jsid;
 
function getPos(elem, evType) {
// param_object.mouseDownPos or param_obj.mouseUpPos
var t = evType + 'Pos';
var res = [];
// Explicit function for getting XY of both
// start and end position for drag start
// to be calculated from the initial pos of the
// dragged, and end to be calculated from the
// position of the destination
if (p[t]) {
var f = eval(p[t]);
res = f(elem);
}
// Otherwise naively assume top/left XY for both
// start (dragged) and end (destination)
else {
res = [elem.offsetLeft, elem.offsetTop];
}
 
return res;
}
 
 
var dragged = this._lookupDispatch(p.dragged);
var dest = this._lookupDispatch(p.destination);
var mouseDownPos = getPos(dragged, 'mouseDown');
var mouseUpPos = getPos(dest, 'mouseUp');
 
var webApp = parent.frames['webapp'];
windmill.events.triggerMouseEvent(webApp.document.body, 'mousemove', true, mouseDownPos[0], mouseDownPos[1]);
windmill.events.triggerMouseEvent(dragged, 'mousedown', true);
windmill.events.triggerMouseEvent(webApp.document.body, 'mousemove', true, mouseUpPos[0], mouseUpPos[1]);
windmill.events.triggerMouseEvent(dragged, 'mouseup', true);
windmill.events.triggerMouseEvent(dragged, 'click', true);
 
return true;
};
 
//Drag Drop functionality allowing functions passed to calculate cursor offsets
this.dragDropXY = function(param_object){
 
var p = param_object;
var webApp = parent.frames['webapp'];
windmill.events.triggerMouseEvent(webApp.document.body, 'mousemove', true, p.source[0], p.source[1]);
windmill.events.triggerMouseEvent(webApp.document.body, 'mousedown', true);
windmill.events.triggerMouseEvent(webApp.document.body, 'mousemove', true, p.destination[0], p.destination[1]);
windmill.events.triggerMouseEvent(webApp.document.body, 'mouseup', true);
windmill.events.triggerMouseEvent(webApp.document.body, 'click', true);
 
return true;
};
 
//Directly access mouse events
this.mousedown = function(param_object){
var mupElement = this._lookupDispatch(param_object);
windmill.events.triggerMouseEvent(mupElement, 'mousedown', true);
 
return true;
};
 
this.mouseup = function(param_object){
var mdnElement = this._lookupDispatch(param_object);
windmill.events.triggerMouseEvent(mdnElement, 'mouseup', true);
 
return true;
};
 
//After the app reloads you have to re overwrite the alert function for the TestingApp
this.reWriteAlert = function(param_object){
windmill.testingApp.window.alert = function(s){
windmill.ui.writeResult("<br>Alert: <b><font color=\"#fff32c\">" + s + "</font>.</b>");
};
 
return true;
};
 
/*******************************************************************************************************
/* Commands namespace functions, mostly system specific for the server to inderact with the client
/******************************************************************************************************/
 
//Give the backend a list of available controller methods
this.commands.getControllerMethods = function(param_object){
 
var str = '';
for (var i in windmill.controller) { if (i.indexOf('_') == -1){ str += "," + i; } }
for (var i in windmill.controller.extensions) {
if (str) { str += ',' }
str += 'extensions.'+i;
}
for (var i in windmill.controller.commands) {
if (str) { str += ',' }
str += 'commands.'+i;
}
 
//Clean up
var ca = new Array();
ca = str.split(",");
ca = ca.reverse();
ca.pop();
ca.pop();
ca.pop();
ca.pop();
ca = ca.sort();
 
//Send to the server
var json_object = new windmill.xhr.json_call('1.1', 'command_result');
var params_obj = {};
params_obj.status = true;
params_obj.uuid = '8y9234yywds8733gwfdbsbdf';
params_obj.result = ca;
json_object.params = params_obj;
var json_string = fleegix.json.serialize(json_object)
 
var resp = function(str){
return true;
}
 
fleegix.xhr.doPost(resp, '/windmill-jsonrpc/', json_string);
return true;
};
 
//Keeping the suites running
this.commands.setOptions = function(param_object){
 
if(typeof param_object.stopOnFailure != "undefined") {
windmill.stopOnFailure = param_object.stopOnFailure;
}
if(typeof param_object.showRemote != "undefined") {
windmill.showRemote = param_object.showRemote;
}
 
return true;
};
 
/********************************************************************************
/* DOM location functionality, all used for various types of lookups in the DOM
/*********************************************************************************/
 
//A big part of the following is adapted from the selenium project browserbot
//Registers all the ways to do a lookup
this._registerAllLocatorFunctions = function() {
// TODO - don't do this in the constructor - only needed once ever
this.locationStrategies = {};
for (var functionName in this) {
var result = /^locateElementBy([A-Z].+)$/.exec(functionName);
if (result != null) {
var locatorFunction = this[functionName];
if (typeof(locatorFunction) != 'function') {
continue;
}
// Use a specified prefix in preference to one generated from
// the function name
var locatorPrefix = locatorFunction.prefix || result[1].toLowerCase();
this.locationStrategies[locatorPrefix] = locatorFunction;
}
};
 
 
//Find a locator based on a prefix.
this.findElementBy = function(locatorType, locator, inDocument, inWindow) {
var locatorFunction = this.locationStrategies[locatorType];
if (! locatorFunction) {
//windmill.Log.debug("Unrecognised locator type: '" + locatorType + "'");
}
 
return locatorFunction.call(this, locator, inDocument, inWindow);
};
 
 
// The implicit locator, that is used when no prefix is supplied.
this.locationStrategies['implicit'] = function(locator, inDocument, inWindow) {
if (locator.startsWith('//')) {
return this.locateElementByXPath(locator, inDocument, inWindow);
}
if (locator.startsWith('document.')) {
return this.locateElementByDomTraversal(locator, inDocument, inWindow);
}
 
return this.locateElementByIdentifier(locator, inDocument, inWindow);
 
};
}
 
//All Element Lookup functionality, based on selenium browserbot code
this.findElement = function(locator) {
var locatorType = 'implicit';
var locatorString = locator;
 
// If there is a locator prefix, use the specified strategy
var result = locator.match(/^([A-Za-z]+)=(.+)/);
if (result) {
locatorType = result[1].toLowerCase();
locatorString = result[2];
}
 
var element = this.findElementBy(locatorType, locatorString, this._getDocument(), parent.frames[1]);
if (element != null) {
return element;
}
 
for (var i = 0; i < parent.frames.length; i++) {
element = this.findElementBy(locatorType, locatorString, parent.frames[i].document, parent.frames[i]);
if (element != null) {
return element;
}
};
 
// Element was not found by any locator function.
////windmill.Log.debug("Element " + locator + " not found");
};
 
 
 
//Find the element with id - can't rely on getElementById, coz it returns by name as well in IE..
this.locateElementById = function(identifier, inDocument, inWindow) {
 
var element = inDocument.getElementById(identifier);
if (element && element.id === identifier) {
return element;
}
else {
return null;
}
};
 
 
//Find an element by name, refined by (optional) element-filter expressions.
this.locateElementByName = function(locator, document, inWindow) {
var elements = document.getElementsByTagName("*");
 
var filters = locator.split(' ');
filters[0] = 'name=' + filters[0];
 
while (filters.length) {
var filter = filters.shift();
elements = this.selectElements(filter, elements, 'value');
}
 
if (elements.length > 0) {
return elements[0];
}
return null;
};
 
 
// Finds an element using by evaluating the specified string.
this.locateElementByDomTraversal = function(domTraversal, document, window) {
 
var browserbot = this.browserbot;
var element = null;
try {
element = eval(domTraversal);
} catch (e) {
//windmill.Log.debug("dom Traversal, element not found.");
}
 
if (!element) {
return null;
}
 
return element;
};
this.locateElementByDomTraversal.prefix = "dom";
 
 
//Finds an element identified by the xpath expression. Expressions _must_
//begin with "//".
this.locateElementByXPath = function(xpath, inDocument, inWindow) {
 
// Trim any trailing "/": not valid xpath, and remains from attribute
// locator.
if (xpath.charAt(xpath.length - 1) == '/') {
xpath = xpath.slice(0, -1);
}
 
// Handle //tag
var match = xpath.match(/^\/\/(\w+|\*)$/);
if (match) {
var elements = inDocument.getElementsByTagName(match[1].toUpperCase());
if (elements == null) return null;
return elements[0];
}
 
// Handle //tag[@attr='value']
var match = xpath.match(/^\/\/(\w+|\*)\[@(\w+)=('([^\']+)'|"([^\"]+)")\]$/);
if (match) {
// We don't return the value without checking if it is null first.
// This is beacuse in some rare cases, this shortcut actually WONT work
// but that the full XPath WILL. A known case, for example, is in IE
// when the attribute is onclick/onblur/onsubmit/etc. Due to a bug in IE
// this shortcut won't work because the actual function is returned
// by getAttribute() rather than the text of the attribute.
var val = this._findElementByTagNameAndAttributeValue(
inDocument,
match[1].toUpperCase(),
match[2].toLowerCase(),
match[3].slice(1, -1)
);
if (val) {
return val;
}
}
 
// Handle //tag[text()='value']
var match = xpath.match(/^\/\/(\w+|\*)\[text\(\)=('([^\']+)'|"([^\"]+)")\]$/);
if (match) {
return this._findElementByTagNameAndText(
inDocument,
match[1].toUpperCase(),
match[2].slice(1, -1)
);
}
 
return this._findElementUsingFullXPath(xpath, inDocument);
};
 
this._findElementByTagNameAndAttributeValue = function(
inDocument, tagName, attributeName, attributeValue
) {
if (browser.isIE && attributeName == "class") {
attributeName = "className";
}
var elements = inDocument.getElementsByTagName(tagName);
for (var i = 0; i < elements.length; i++) {
var elementAttr = elements[i].getAttribute(attributeName);
if (elementAttr == attributeValue) {
return elements[i];
}
}
return null;
};
 
this._findElementByTagNameAndText = function(
inDocument, tagName, text
) {
var elements = inDocument.getElementsByTagName(tagName);
for (var i = 0; i < elements.length; i++) {
if (windmill.events.getText((elements[i]) == text)) {
return elements[i];
}
}
return null;
};
 
this._namespaceResolver = function(prefix) {
if (prefix == 'html' || prefix == 'xhtml' || prefix == 'x') {
return 'http://www.w3.org/1999/xhtml';
} else if (prefix == 'mathml') {
return 'http://www.w3.org/1998/Math/MathML';
} else {
throw new Error("Unknown namespace: " + prefix + ".");
}
}
 
this._findElementUsingFullXPath = function(xpath, inDocument, inWindow) {
// HUGE hack - remove namespace from xpath for IE
if (browser.isIE) {
xpath = xpath.replace(/x:/g, '')
}
 
// Use document.evaluate() if it's available
if (inDocument.evaluate) {
return inDocument.evaluate(xpath, inDocument, this._namespaceResolver, 0, null).iterateNext();
}
 
// If not, fall back to slower JavaScript implementation
var context = new ExprContext(inDocument);
var xpathObj = xpathParse(xpath);
var xpathResult = xpathObj.evaluate(context);
if (xpathResult && xpathResult.value) {
return xpathResult.value[0];
}
return null;
};
 
/**
* Finds a link element with text matching the expression supplied. Expressions must
* begin with "link:".
*/
this.locateElementByLinkText = function(linkText, inDocument, inWindow) {
 
var links = inDocument.getElementsByTagName('a');
 
for (var i = 0; i < links.length; i++) {
var element = links[i];
if (PatternMatcher.matches(linkText, windmill.events.getText(element))) {
return element;
}
}
return null;
};
this.locateElementByLinkText.prefix = "link";
 
//Register all the ways to lookup an element in a list to access dynamically
this._registerAllLocatorFunctions();
 
};
 
/**
* Factory for creating "Option Locators".
* An OptionLocator is an object for dealing with Select options (e.g. for
* finding a specified option, or asserting that the selected option of
* Select element matches some condition.
* The type of locator returned by the factory depends on the locator string:
* label=<exp> (OptionLocatorByLabel)
* value=<exp> (OptionLocatorByValue)
* index=<exp> (OptionLocatorByIndex)
* id=<exp> (OptionLocatorById)
* <exp> (default is OptionLocatorByLabel).
*/
function OptionLocatorFactory() {
}
 
OptionLocatorFactory.prototype.fromLocatorString = function(locatorString) {
var locatorType = 'label';
var locatorValue = locatorString;
// If there is a locator prefix, use the specified strategy
var result = locatorString.match(/^([a-zA-Z]+)=(.*)/);
if (result) {
locatorType = result[1];
locatorValue = result[2];
}
if (this.optionLocators == undefined) {
this.registerOptionLocators();
}
if (this.optionLocators[locatorType]) {
return new this.optionLocators[locatorType](locatorValue);
}
 
//windmill.Log.debug("Unrecognised locator type: '" + locatorType + "'");
 
};
 
/**
* To allow for easy extension, all of the option locators are found by
* searching for all methods of OptionLocatorFactory.prototype that start
* with "OptionLocatorBy".
* TODO: Consider using the term "Option Specifier" instead of "Option Locator".
*/
OptionLocatorFactory.prototype.registerOptionLocators = function() {
this.optionLocators={};
for (var functionName in this) {
var result = /OptionLocatorBy([A-Z].+)$/.exec(functionName);
if (result != null) {
var locatorName = result[1].lcfirst();
this.optionLocators[locatorName] = this[functionName];
}
}
};
 
/**
* OptionLocator for options identified by their labels.
*/
OptionLocatorFactory.prototype.OptionLocatorByLabel = function(label) {
this.label = label;
this.labelMatcher = new PatternMatcher(this.label);
this.findOption = function(element) {
for (var i = 0; i < element.options.length; i++) {
if (this.labelMatcher.matches(element.options[i].text)) {
return element.options[i];
}
}
//windmill.Log.debug("Option with label '" + this.label + "' not found");
 
 
};
 
this.assertSelected = function(element) {
var selectedLabel = element.options[element.selectedIndex].text;
Assert.matches(this.label, selectedLabel)
};
};
 
/**
* OptionLocator for options identified by their values.
*/
OptionLocatorFactory.prototype.OptionLocatorByValue = function(value) {
this.value = value;
this.valueMatcher = new PatternMatcher(this.value);
this.findOption = function(element) {
for (var i = 0; i < element.options.length; i++) {
if (this.valueMatcher.matches(element.options[i].value)) {
return element.options[i];
}
}
 
//windmill.Log.debug("Option with value '" + this.value + "' not found");
 
};
 
this.assertSelected = function(element) {
var selectedValue = element.options[element.selectedIndex].value;
Assert.matches(this.value, selectedValue)
};
};
 
/**
* OptionLocator for options identified by their index.
*/
OptionLocatorFactory.prototype.OptionLocatorByIndex = function(index) {
this.index = Number(index);
if (isNaN(this.index) || this.index < 0) {
 
//windmill.Log.debug("Illegal Index: " + index);
 
}
 
this.findOption = function(element) {
if (element.options.length <= this.index) {
 
//windmill.Log.debug("Index out of range. Only " + element.options.length + " options available");
 
}
return element.options[this.index];
};
 
this.assertSelected = function(element) {
Assert.equals(this.index, element.selectedIndex);
};
};
 
/**
* OptionLocator for options identified by their id.
*/
OptionLocatorFactory.prototype.OptionLocatorById = function(id) {
this.id = id;
this.idMatcher = new PatternMatcher(this.id);
this.findOption = function(element) {
for (var i = 0; i < element.options.length; i++) {
if (this.idMatcher.matches(element.options[i].id)) {
return element.options[i];
}
}
//windmill.Log.debug("Option with id '" + this.id + "' not found");
 
};
 
this.assertSelected = function(element) {
var selectedId = element.options[element.selectedIndex].id;
Assert.matches(this.id, selectedId)
};
};
 
Property changes : Added: svn:eol-style + native
dot-two-refactor/windmill/js/lib/fleegix.js New file
0,0 → 1,1145
if(typeof fleegix=="undefined"){
var fleegix={};
}
fleegix.popup=new function(){
var _1=this;
this.win=null;
this.open=function(_2,_3){
var _4=_3||{};
var _5="";
var _6={"width":"","height":"","location":0,"menubar":0,"resizable":1,"scrollbars":0,"status":0,"titlebar":1,"toolbar":0};
for(var _7 in _6){
_5+=_7+"=";
_5+=_4[_7]?_4[_7]:_6[_7];
_5+=",";
}
var _8=_5.length;
if(_8){
_5=_5.substr(0,_8-1);
}
if(!_1.win||_1.win.closed){
_1.win=window.open(_2,"thePopupWin",_5);
}else{
_1.win.focus();
_1.win.document.location=_2;
}
};
this.close=function(){
if(_1.win){
_1.win.window.close();
_1.win=null;
}
};
this.goURLMainWin=function(_9){
location=_9;
_1.close();
};
};
fleegix.popup.constructor=null;
fleegix.form={};
fleegix.form.serialize=function(f,o){
var h=fleegix.form.toHash(f);
var _d=o||{};
var _e="";
var _f=null;
if(_d.stripTags){
_f=/<[^>]*>/g;
}
for(var n in h){
var s="";
var v=h[n];
if(v){
if(typeof v=="string"){
s=_d.stripTags?v.replace(_f,""):v;
_e+=n+"="+encodeURIComponent(s);
}else{
var sep="";
if(_d.collapseMulti){
sep=",";
_e+=n+"=";
}else{
sep="&";
}
for(var j=0;j<v.length;j++){
s=_d.stripTags?v[j].replace(_f,""):v[j];
s=(!_d.collapseMulti)?n+"="+encodeURIComponent(s):encodeURIComponent(s);
_e+=s+sep;
}
_e=_e.substr(0,_e.length-1);
}
_e+="&";
}else{
if(_d.includeEmpty){
_e+=n+"=&";
}
}
}
_e=_e.substr(0,_e.length-1);
return _e;
};
fleegix.form.toHash=function(f){
var h={};
function expandToArr(_17,val){
if(_17){
var r=null;
if(typeof _17=="string"){
r=[];
r.push(_17);
}else{
r=_17;
}
r.push(val);
return r;
}else{
return val;
}
}
for(i=0;i<f.elements.length;i++){
elem=f.elements[i];
switch(elem.type){
case "text":
case "hidden":
case "password":
case "textarea":
case "select-one":
h[elem.name]=elem.value||null;
break;
case "select-multiple":
h[elem.name]=null;
for(var j=0;j<elem.options.length;j++){
var o=elem.options[j];
if(o.selected){
h[elem.name]=expandToArr(h[elem.name],o.value);
}
}
break;
case "radio":
if(typeof h[elem.name]=="undefined"){
h[elem.name]=null;
}
if(elem.checked){
h[elem.name]=elem.value;
}
break;
case "checkbox":
if(typeof h[elem.name]=="undefined"){
h[elem.name]=null;
}
if(elem.checked){
h[elem.name]=expandToArr(h[elem.name],elem.value);
}
break;
}
}
return h;
};
fleegix.form.restore=function(_1c,str){
var arr=str.split("&");
var d={};
for(var i=0;i<arr.length;i++){
var _21=arr[i].split("=");
var _22=_21[0];
var val=_21[1];
if(typeof d[_22]=="undefined"){
d[_22]=val;
}else{
if(!(d[_22] instanceof Array)){
var t=d[_22];
d[_22]=[];
d[_22].push(t);
}
d[_22].push(val);
}
}
for(var i=0;i<_1c.elements.length;i++){
elem=_1c.elements[i];
if(typeof d[elem.name]!="undefined"){
val=d[elem.name];
switch(elem.type){
case "text":
case "hidden":
case "password":
case "textarea":
case "select-one":
elem.value=decodeURIComponent(val);
break;
case "radio":
if(encodeURIComponent(elem.value)==val){
elem.checked=true;
}
break;
case "checkbox":
for(var j=0;j<val.length;j++){
if(encodeURIComponent(elem.value)==val[j]){
elem.checked=true;
}
}
break;
case "select-multiple":
for(var h=0;h<elem.options.length;h++){
var opt=elem.options[h];
for(var j=0;j<val.length;j++){
if(encodeURIComponent(opt.value)==val[j]){
opt.selected=true;
}
}
}
break;
}
}
}
return _1c;
};
fleegix.form.diff=function(_28,_29,_2a){
var o=_2a||{};
var _2c=_28.toString()=="[object HTMLFormElement]"?fleegix.form.toHash(_28):_28;
var _2d=_29.toString()=="[object HTMLFormElement]"?fleegix.form.toHash(_29):_29;
var _2e=[];
var _2f=0;
function addDiff(n,hA,hB,_33){
if(!_2e[n]){
_2f++;
_2e[n]=_33?[hB[n],hA[n]]:[hA[n],hB[n]];
}
}
function diffSweep(hA,hB,_36){
for(n in hA){
if(typeof hB[n]=="undefined"){
if(o.intersectionOnly){
continue;
}
addDiff(n,hA,hB,_36);
}else{
v=hA[n];
if(v instanceof Array){
if(!hB[n]||(hB[n].toString()!=v.toString())){
addDiff(n,hA,hB,_36);
}
}else{
if(hB[n]!=v){
addDiff(n,hA,hB,_36);
}
}
}
}
}
diffSweep(_2c,_2d,false);
diffSweep(_2d,_2c,true);
return {count:_2f,diffs:_2e};
};
fleegix.xhr=new function(){
var _37=null;
function spawnTransporter(_38){
var i=0;
var t=["Msxml2.XMLHTTP.6.0","MSXML2.XMLHTTP.3.0","Microsoft.XMLHTTP"];
var _3b=null;
if(window.XMLHttpRequest!=null){
_3b=new XMLHttpRequest();
}else{
if(window.ActiveXObject!=null){
if(_37){
_3b=new ActiveXObject(_37);
}else{
for(var i=0;i<t.length;i++){
try{
_3b=new ActiveXObject(t[i]);
_37=t[i];
break;
}
catch(e){
}
}
}
}
}
if(_3b){
if(_38){
return _3b;
}else{
fleegix.xhr.transporters.push(_3b);
var _3c=fleegix.xhr.transporters.length-1;
return _3c;
}
}else{
throw ("Could not create XMLHttpRequest object.");
}
}
this.transporters=[];
this.maxTransporters=5;
this.lastReqId=0;
this.requestQueue=[];
this.idleTransporters=[];
this.processingMap={};
this.processingArray=[];
this.syncTransporter=spawnTransporter(true);
this.syncRequest=null;
this.debug=false;
this.processingWatcherId=null;
this.doGet=function(){
var o={};
var _3e=null;
var _3f=Array.prototype.slice.apply(arguments);
if(typeof _3f[0]=="function"){
o.async=true;
_3e=_3f.shift();
}else{
o.async=false;
}
var url=_3f.shift();
if(typeof _3f[0]=="object"){
var _41=_3f.shift();
for(var p in _41){
o[p]=_41[p];
}
}else{
o.responseFormat=_3f.shift()||"text";
}
o.handleSuccess=_3e;
o.url=url;
return this.doReq(o);
};
this.doPost=function(){
var o={};
var _44=null;
var _45=Array.prototype.slice.apply(arguments);
if(typeof _45[0]=="function"){
o.async=true;
_44=_45.shift();
}else{
o.async=false;
}
var url=_45.shift();
var _47=_45.shift();
if(typeof _45[0]=="object"){
var _48=_45.shift();
for(var p in _48){
o[p]=_48[p];
}
}else{
o.responseFormat=_45.shift()||"text";
}
o.handleSuccess=_44;
o.url=url;
o.dataPayload=_47;
o.method="POST";
return this.doReq(o);
};
this.doReq=function(o){
var _4b=o||{};
var req=new fleegix.xhr.Request();
var _4d=null;
for(var p in _4b){
req[p]=_4b[p];
}
req.id=this.lastReqId;
this.lastReqId++;
if(req.async){
if(this.idleTransporters.length){
_4d=this.idleTransporters.shift();
}else{
if(this.transporters.length<this.maxTransporters){
_4d=spawnTransporter();
}
}
if(_4d!=null){
this.processReq(req,_4d);
}else{
if(req.uber){
this.requestQueue.unshift(req);
}else{
this.requestQueue.push(req);
}
}
return req.id;
}else{
return this.processReq(req);
}
};
this.processReq=function(req,t){
var _51=this;
var _52=null;
var _53=null;
var url="";
var _55=null;
if(req.async){
_52=t;
_53=this.transporters[_52];
this.processingMap[req.id]=req;
this.processingArray.unshift(req);
req.transporterId=_52;
}else{
_53=this.syncTransporter;
this.syncRequest=req;
}
if(req.preventCache){
var dt=new Date().getTime();
url=req.url.indexOf("?")>-1?req.url+"&preventCache="+dt:req.url+"?preventCache="+dt;
}else{
url=req.url;
}
if(document.all){
_53.abort();
}
if(req.username&&req.password){
_53.open(req.method,url,req.async,req.username,req.password);
}else{
_53.open(req.method,url,req.async);
}
if(req.mimeType&&navigator.userAgent.indexOf("MSIE")==-1){
_53.overrideMimeType(req.mimeType);
}
if(req.headers.length){
for(var i=0;i<req.headers.length;i++){
var _58=req.headers[i].split(": ");
_53.setRequestHeader(_58[i],_58[1]);
}
}else{
if(req.method=="POST"){
_53.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
}
}
_53.send(req.dataPayload);
if(this.processingWatcherId==null){
this.processingWatcherId=setTimeout(fleegix.xhr.watchProcessing,10);
}
if(!req.async){
var ret=this.handleResponse(_53,req);
this.syncRequest=null;
if(_51.processingArray.length){
_51.processingWatcherId=setTimeout(fleegix.xhr.watchProcessing,10);
}
return ret;
}
};
this.getResponseByType=function(_5a,req){
switch(req.responseFormat){
case "text":
r=_5a.responseText;
break;
case "xml":
r=_5a.responseXML;
break;
case "object":
r=_5a;
break;
}
return r;
};
this.watchProcessing=function(){
var _5c=fleegix.xhr;
var _5d=_5c.processingArray;
var d=new Date().getTime();
if(_5c.syncRequest!=null){
return;
}else{
for(var i=0;i<_5d.length;i++){
var req=_5d[i];
var _61=_5c.transporters[req.transporterId];
var _62=((d-req.startTime)>(req.timeoutSeconds*1000));
switch(true){
case (req.aborted||!_61.readyState):
_5c.processingArray.splice(i,1);
case _62:
_5c.processingArray.splice(i,1);
_5c.timeout(req);
break;
case (_61.readyState==4):
_5c.processingArray.splice(i,1);
_5c.handleResponse.apply(_5c,[_61,req]);
break;
}
}
}
clearTimeout(_5c.processingWatcherId);
if(_5c.processingArray.length){
_5c.processingWatcherId=setTimeout(fleegix.xhr.watchProcessing,10);
}else{
_5c.processingWatcherId=null;
}
};
this.abort=function(_63){
var r=this.processingMap[_63];
var t=this.transporters[r.transporterId];
if(t){
t.onreadystatechange=function(){
};
t.abort();
r.aborted=true;
this.cleanupAfterReq(r);
return true;
}else{
return false;
}
};
this.timeout=function(req){
if(fleegix.xhr.abort.apply(fleegix.xhr,[req.id])){
if(typeof req.handleTimeout=="function"){
req.handleTimeout();
}else{
alert("XMLHttpRequest to "+req.url+" timed out.");
}
}
};
this.handleResponse=function(_67,req){
var _69=this.getResponseByType(_67,req);
if(req.handleAll){
req.handleAll(_69,req.id);
}else{
try{
if((_67.status>199&&_67.status<300)||_67.status==304){
if(req.async){
if(!req.handleSuccess){
throw ("No response handler defined "+"for this request");
return;
}else{
req.handleSuccess(_69,req.id);
}
}else{
return _69;
}
}else{
if(!_67.status){
if(this.debug){
throw ("XMLHttpRequest HTTP status either zero or not set.");
}
}else{
if(req.handleErr){
req.handleErr(_69,req.id);
}else{
this.handleErrDefault(_67);
}
}
}
}
catch(e){
if(this.debug){
throw (e);
}
}
}
if(req.async){
this.cleanupAfterReq(req);
}
return true;
};
this.cleanupAfterReq=function(req){
delete this.processingMap[req.id];
if(this.requestQueue.length){
var _6b=this.requestQueue.shift();
_6b.startTime=new Date().getTime();
this.processReq(_6b,req.transporterId);
}else{
this.idleTransporters.push(req.transporterId);
}
};
this.handleErrDefault=function(r){
console.log(r);
var _6d;
try{
_6d=window.open("","errorWin");
_6d.document.body.innerHTML=r.responseText;
}
catch(e){
alert("An error occurred, but the error message cannot be"+" displayed because of your browser's pop-up blocker.\n"+"Please allow pop-ups from this Web site.");
}
};
};
fleegix.xhr.constructor=null;
fleegix.xhr.Request=function(){
this.id=0;
this.transporterId=null;
this.url=null;
this.status=null;
this.statusText="";
this.method="GET";
this.async=true;
this.dataPayload=null;
this.readyState=null;
this.responseText=null;
this.responseXML=null;
this.handleSuccess=null;
this.handleErr=null;
this.handleAll=null;
this.handleTimeout=null;
this.responseFormat="text",this.mimeType=null;
this.username="";
this.password="";
this.headers=[];
this.preventCache=false;
this.startTime=new Date().getTime();
this.timeoutSeconds=30;
this.uber=false;
this.aborted=false;
};
fleegix.xhr.Request.prototype.setRequestHeader=function(_6e,_6f){
this.headers.push(_6e+": "+_6f);
};
fleegix.event=new function(){
var _70=[];
var _71={};
this.listen=function(){
var _72=arguments[0];
var _73=arguments[1];
var _74=_72[_73]?_72[_73].listenReg:null;
if(!_74){
_74={};
_74.orig={};
_74.orig.obj=_72,_74.orig.methName=_73;
if(_72[_73]){
_74.orig.methCode=_72[_73];
}
_74.after=[];
_72[_73]=function(){
var reg=_72[_73].listenReg;
var _76=[];
for(var i=0;i<arguments.length;i++){
_76.push(arguments[i]);
}
if(reg.orig.methCode){
reg.orig.methCode.apply(reg.orig.obj,_76);
}
if(_72.attachEvent||_72.nodeType||_72.addEventListener){
var ev=null;
if(!_76.length){
try{
switch(true){
case !!(_72.ownerDocument):
ev=_72.ownerDocument.parentWindow.event;
break;
case !!(_72.documentElement):
ev=_72.documentElement.ownerDocument.parentWindow.event;
break;
case !!(_72.event):
ev=_72.event;
break;
default:
ev=window.event;
break;
}
}
catch(e){
ev=window.event;
}
}
if(ev){
if(typeof ev.target=="undefined"){
ev.target=ev.srcElement;
}
if(typeof ev.srcElement=="undefined"){
ev.srcElement=ev.target;
}
_76[0]=ev;
}
}
for(var i=0;i<reg.after.length;i++){
var ex=reg.after[i];
if(typeof ex=="function"){
var _7a=ex;
_7a.apply(window,_76);
}else{
execObj=ex[0];
execMethod=ex[1];
execObj[execMethod].apply(execObj,_76);
}
}
};
_72[_73].listenReg=_74;
_70.push(_72[_73].listenReg);
}
if(typeof arguments[2]=="function"){
_74.after.push(arguments[2]);
}else{
_74.after.push([arguments[2],arguments[3]]);
}
_72[_73].listenReg=_74;
};
this.unlisten=function(){
var _7b=arguments[0];
var _7c=arguments[1];
var _7d=_7b[_7c]?_7b[_7c].listenReg:null;
var _7e=null;
if(!_7d){
return false;
}
for(var i=0;i<_7d.after.length;i++){
var ex=_7d.after[i];
if(typeof arguments[2]=="function"){
if(ex==arguments[2]){
_7d.after.splice(i,1);
}
}else{
if(ex[0]==arguments[2]&&ex[1]==arguments[3]){
_7d.after.splice(i,1);
}
}
}
_7b[_7c].listenReg=_7d;
};
this.flush=function(){
for(var i=0;i<_70.length;i++){
var reg=_70[i];
removeObj=reg.orig.obj;
removeMethod=reg.orig.methName;
removeObj[removeMethod]=null;
}
};
this.subscribe=function(_83,obj,_85){
if(!obj){
return;
}
if(!_71[_83]){
_71[_83]={};
_71[_83].audience=[];
}else{
this.unsubscribe(_83,obj);
}
_71[_83].audience.push([obj,_85]);
};
this.unsubscribe=function(_86,obj){
if(!obj){
_71[_86]=null;
}else{
if(_71[_86]){
var aud=_71[_86].audience;
for(var i=0;i<aud.length;i++){
if(aud[i][0]==obj){
aud.splice(i,1);
}
}
}
}
};
this.publish=function(pub,_8b){
if(_71[pub]){
aud=_71[pub].audience;
for(var i=0;i<aud.length;i++){
var _8d=aud[i][0];
var _8e=aud[i][1];
_8d[_8e](_8b);
}
}
};
this.getSrcElementId=function(e){
var ret=null;
if(e.srcElement){
ret=e.srcElement;
}else{
if(e.target){
ret=e.target;
}
}
if(typeof ret.id=="undefined"){
return null;
}else{
while(!ret.id||ret.nodeType==3){
if(ret.parentNode){
ret=ret.parentNode;
}else{
return null;
}
}
}
return ret.id;
};
};
fleegix.event.constructor=null;
fleegix.event.listen(window,"onunload",fleegix.event,"flush");
fleegix.xml=new function(){
var _91=this;
this.parse=function(_92,_93){
var _94=new Array;
var _95;
var _96=[];
if(_92.hasChildNodes()){
_94=_92.getElementsByTagName(_93);
_95=_94[0];
for(var j=0;j<_94.length;j++){
_95=_94[j];
_96[j]=_91.xmlElem2Obj(_94[j]);
}
}
return _96;
};
this.xmlElem2Obj=function(_98){
var ret=new Object();
_91.setPropertiesRecursive(ret,_98);
return ret;
};
this.setPropertiesRecursive=function(obj,_9b){
if(_9b.childNodes.length>0){
for(var i=0;i<_9b.childNodes.length;i++){
if(_9b.childNodes[i].nodeType==1&&_9b.childNodes[i].firstChild){
if(_9b.childNodes[i].childNodes.length==1){
obj[_9b.childNodes[i].tagName]=_9b.childNodes[i].firstChild.nodeValue;
}else{
obj[_9b.childNodes[i].tagName]=[];
_91.setPropertiesRecursive(obj[_9b.childNodes[i].tagName],_9b.childNodes[i]);
}
}
}
}
};
this.cleanXMLObjText=function(_9d){
var _9e=_9d;
for(var _9f in _9e){
_9e[_9f]=cleanText(_9e[_9f]);
}
return _9e;
};
this.cleanText=function(str){
var ret=str;
ret=ret.replace(/\n/g,"");
ret=ret.replace(/\r/g,"");
ret=ret.replace(/\'/g,"\\'");
ret=ret.replace(/\[CDATA\[/g,"");
ret=ret.replace(/\]]/g,"");
return ret;
};
this.rendered2Source=function(str){
var _a3=str;
_a3=_a3.replace(/</g,"&lt;");
_a3=_a3.replace(/>/g,"&gt;");
return "<pre>"+_a3+"</pre>";
};
this.getXMLDocElem=function(_a4,_a5){
var _a6=[];
var _a7=null;
if(document.all){
var _a8=document.getElementById(_a4).innerHTML;
var _a9=new ActiveXObject("Microsoft.XMLDOM");
_a9.loadXML(_a8);
_a7=_a9.documentElement;
}else{
_a6=window.document.body.getElementsByTagName(_a5);
_a7=_a6[0];
}
return _a7;
};
};
fleegix.xml.constructor=null;
fleegix.uri=new function(){
var _aa=this;
this.params={};
this.getParamHash=function(str){
var q=str||_aa.getQuery();
var d={};
if(q){
var arr=q.split("&");
for(var i=0;i<arr.length;i++){
var _b0=arr[i].split("=");
var _b1=_b0[0];
var val=_b0[1];
if(typeof d[_b1]=="undefined"){
d[_b1]=val;
}else{
if(!(d[_b1] instanceof Array)){
var t=d[_b1];
d[_b1]=[];
d[_b1].push(t);
}
d[_b1].push(val);
}
}
}
return d;
};
this.getParam=function(_b4,str){
var p=null;
if(str){
var h=this.getParamHash(str);
p=h[_b4];
}else{
p=this.params[_b4];
}
return p;
};
this.setParam=function(_b8,val,str){
var ret=null;
if(str){
var pat=new RegExp("(^|&)("+_b8+"=[^&]*)(&|$)");
var arr=str.match(pat);
if(arr){
ret=str.replace(arr[0],arr[1]+_b8+"="+val+arr[3]);
}else{
ret=str+"&"+_b8+"="+val;
}
}else{
ret=_b8+"="+val;
}
return ret;
};
this.getQuery=function(s){
var l=s?s:location.href;
return l.split("?")[1];
};
this.getBase=function(s){
var l=s?s:location.href;
return l.split("?")[0];
};
this.params=this.getParamHash();
};
fleegix.uri.constructor=null;
fleegix.fx=new function(){
function doFade(_c2,_c3,dir){
var s=dir=="in"?0:100;
var e=dir=="in"?100:0;
var o={startVal:s,endVal:e,props:{opacity:[s,e]},trans:"lightEaseIn"};
for(p in _c3){
o[p]=_c3[p];
}
return new fleegix.fx.Effecter(_c2,o);
}
this.fadeOut=function(_c8,_c9){
return doFade(_c8,_c9,"out");
};
this.fadeIn=function(_ca,_cb){
return doFade(_ca,_cb,"in");
};
this.setCSSProp=function(_cc,p,v){
if(p=="opacity"){
if(document.all){
_cc.style.filter="alpha(opacity="+v+")";
}else{
var d=v/100;
_cc.style.opacity=d;
}
}else{
if(p.toLowerCase().indexOf("color")>-1){
_cc.style[p]=v;
}else{
_cc.style[p]=document.all?parseInt(v)+"px":v+"px";
}
}
return true;
};
this.hexPat=/^[#]{0,1}([\w]{1,2})([\w]{1,2})([\w]{1,2})$/;
this.hexToRGB=function(str,_d1){
var rgb=[];
var h=str.match(this.hexPat);
if(h){
for(var i=1;i<h.length;i++){
var s=h[i];
s=s.length==1?s+s:s;
rgb.push(parseInt(s,16));
}
s="rgb("+rgb.join()+")";
return _d1?rgb:s;
}else{
throw ("\""+str+"\" not a valid hex value.");
}
};
};
fleegix.fx.Effecter=function(_d6,_d7){
var _d8=this;
this.props=_d7.props;
this.trans=_d7.trans||"lightEaseIn";
this.duration=_d7.duration||500;
this.fps=30;
this.startTime=new Date().getTime();
this.timeSpent=0;
this.doOnStart=_d7.doOnStart||null;
this.doAfterFinished=_d7.doAfterFinished||null;
this.autoStart=_d7.autoStart==false?false:true;
if(typeof this.transitions[this.trans]!="function"){
throw ("\""+this.trans+"\" is not a valid transition.");
}
this.start=function(){
_d8.id=setInterval(function(){
_d8.doStep.apply(_d8,[_d6]);
},Math.round(1000/_d8.fps));
if(typeof _d7.doOnStart=="function"){
_d8.doOnStart();
}
};
if(this.autoStart){
this.start();
}
return this;
};
fleegix.fx.Effecter.prototype.doStep=function(_d9){
var t=new Date().getTime();
var p=this.props;
if(t<(this.startTime+this.duration)){
this.timeSpent=t-this.startTime;
for(var i in p){
fleegix.fx.setCSSProp(_d9,i,this.calcCurrVal(i));
}
}else{
for(var i in p){
fleegix.fx.setCSSProp(_d9,i,p[i][1]);
}
clearInterval(this.id);
if(typeof this.doAfterFinished=="function"){
this.doAfterFinished();
}
}
};
fleegix.fx.Effecter.prototype.calcCurrVal=function(key){
var _de=this.props[key][0];
var _df=this.props[key][1];
var _e0=this.transitions[this.trans];
if(key.toLowerCase().indexOf("color")>-1){
var _e1=fleegix.fx.hexToRGB(_de,true);
var _e2=fleegix.fx.hexToRGB(_df,true);
var _e3=[];
for(var i=0;i<_e1.length;i++){
var s=_e1[i];
var e=_e2[i];
_e3.push(parseInt(_e0(this.timeSpent,s,(e-s),this.duration)));
}
return "rgb("+_e3.join()+")";
}else{
return _e0(this.timeSpent,_de,(_df-_de),this.duration);
}
};
fleegix.fx.Effecter.prototype.transitions={linear:function(t,b,c,d){
return c*(t/d)+b;
},lightEaseIn:function(t,b,c,d){
return c*(t/=d)*t+b;
},lightEaseOut:function(t,b,c,d){
return -c*(t/=d)*(t-2)+b;
},lightEaseInOut:function(t,b,c,d){
if((t/=d/2)<1){
return c/2*t*t+b;
}
return -c/2*((--t)*(t-2)-1)+b;
},heavyEaseIn:function(t,b,c,d){
return c*(t/=d)*t*t+b;
},heavyEaseOut:function(t,b,c,d){
return c*((t=t/d-1)*t*t+1)+b;
},heavyEaseInOut:function(t,b,c,d){
if((t/=d/2)<1){
return c/2*t*t*t+b;
}
return c/2*((t-=2)*t*t+2)+b;
}};
fleegix.json=new function(){
this.serialize=function(obj){
var str="";
switch(typeof obj){
case "object":
if(obj==null){
return "null";
}else{
if(obj instanceof Array){
for(var i=0;i<obj.length;i++){
if(str){
str+=",";
}
str+=fleegix.json.serialize(obj[i]);
}
return "["+str+"]";
}else{
if(typeof obj.toString!="undefined"){
for(var i in obj){
if(str){
str+=",";
}
str+="\""+i+"\":";
if(typeof obj[i]=="undefined"){
str+="\"undefined\"";
}else{
str+=fleegix.json.serialize(obj[i]);
}
}
return "{"+str+"}";
}
}
}
return str;
break;
case "unknown":
case "undefined":
case "function":
return "\"undefined\"";
break;
case "string":
str+="\""+obj.replace(/(["\\])/g,"\\$1").replace(/\r/g,"").replace(/\n/g,"\\n")+"\"";
return str;
break;
default:
return String(obj);
break;
}
};
};
fleegix.json.constructor=null;
fleegix.cookie=new function(){
this.set=function(name,_107,_108){
var opts=_108||{};
var exp="";
var t=0;
if(typeof _108=="object"){
var path=opts.path||"/";
var days=opts.days||0;
var _10e=opts.hours||0;
var _10f=opts.minutes||0;
}else{
var path=optsParam||"/";
}
t+=days?days*24*60*60*1000:0;
t+=_10e?_10e*60*60*1000:0;
t+=_10f?_10f*60*1000:0;
if(t){
var dt=new Date();
dt.setTime(dt.getTime()+t);
exp="; expires="+dt.toGMTString();
}else{
exp="";
}
document.cookie=name+"="+_107+exp+"; path="+path;
};
this.get=function(name){
var _112=name+"=";
var arr=document.cookie.split(";");
for(var i=0;i<arr.length;i++){
var c=arr[i];
while(c.charAt(0)==" "){
c=c.substring(1,c.length);
}
if(c.indexOf(_112)==0){
return c.substring(_112.length,c.length);
}
}
return null;
};
this.create=this.set;
this.destroy=function(name,path){
var opts={};
opts.minutes=-1;
if(path){
opts.path=path;
}
this.set(name,"",opts);
};
};
fleegix.cookie.constructor=null;
fleegix.ui=new function(){
this.getViewportWidth=function(){
return fleegix.ui.getViewportMeasure("Width");
};
this.getViewportHeight=function(){
return fleegix.ui.getViewportMeasure("Height");
};
this.getViewportMeasure=function(s){
if(document.all){
if(document.documentElement&&document.documentElement["client"+s]){
return document.documentElement["client"+s];
}else{
return document.body["client"+s];
}
}else{
return window["inner"+s];
}
};
this.center=function(node){
var nW=node.offsetWidth;
var nH=node.offsetHeight;
var vW=fleegix.ui.getViewportWidth();
var vH=fleegix.ui.getViewportHeight();
node.style.left=parseInt((vW/2)-(nW/2))+"px";
node.style.top=parseInt((vH/2)-(nH/2))+"px";
};
};
fleegix.ui.constructor=null;
 
dot-two-refactor/windmill/js/lib/htmlutils.js New file
0,0 → 1,750
/*
* Copyright 2004 ThoughtWorks, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
 
// This script contains a badly-organised collection of miscellaneous
// functions that really better homes.
 
function classCreate() {
return function() {
this.initialize.apply(this, arguments);
}
}
 
function objectExtend(destination, source) {
for (var property in source) {
destination[property] = source[property];
}
return destination;
}
 
function $() {
var results = [], element;
for (var i = 0; i < arguments.length; i++) {
element = arguments[i];
if (typeof element == 'string')
element = document.getElementById(element);
results[results.length] = element;
}
return results.length < 2 ? results[0] : results;
}
 
function $A(iterable) {
if (!iterable) return [];
if (iterable.toArray) {
return iterable.toArray();
} else {
var results = [];
for (var i = 0; i < iterable.length; i++)
results.push(iterable[i]);
return results;
}
}
 
function fnBind() {
var args = $A(arguments), __method = args.shift(), object = args.shift();
return function() {
return __method.apply(object, args.concat($A(arguments)));
}
}
 
function fnBindAsEventListener(fn, object) {
var __method = fn;
return function(event) {
return __method.call(object, event || window.event);
}
}
 
function removeClassName(element, name) {
var re = new RegExp("\\b" + name + "\\b", "g");
element.className = element.className.replace(re, "");
}
 
function addClassName(element, name) {
element.className = element.className + ' ' + name;
}
 
function elementSetStyle(element, style) {
for (var name in style) {
element.style[name] = style[name];
}
}
 
function elementGetStyle(element, style) {
var value = element.style[style];
if (!value) {
if (document.defaultView && document.defaultView.getComputedStyle) {
var css = document.defaultView.getComputedStyle(element, null);
value = css ? css.getPropertyValue(style) : null;
} else if (element.currentStyle) {
value = element.currentStyle[style];
}
}
 
/** DGF necessary?
if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
if (Element.getStyle(element, 'position') == 'static') value = 'auto'; */
 
return value == 'auto' ? null : value;
}
 
String.prototype.trim = function() {
var result = this.replace(/^\s+/g, "");
// strip leading
return result.replace(/\s+$/g, "");
// strip trailing
};
String.prototype.lcfirst = function() {
return this.charAt(0).toLowerCase() + this.substr(1);
};
String.prototype.ucfirst = function() {
return this.charAt(0).toUpperCase() + this.substr(1);
};
String.prototype.startsWith = function(str) {
return this.indexOf(str) == 0;
};
 
// Returns the text in this element
function getText(element) {
var text = "";
 
var isRecentFirefox = (browser.isMozilla);
if (isRecentFirefox || browser.isKonqueror || browser.isSafari || browser.isOpera) {
text = getTextContent(element);
} else if (element.textContent) {
text = element.textContent;
} else if (element.innerText) {
text = element.innerText;
}
 
text = normalizeNewlines(text);
text = normalizeSpaces(text);
 
return text.trim();
}
 
function getTextContent(element, preformatted) {
if (element.nodeType == 3 /*Node.TEXT_NODE*/) {
var text = element.data;
if (!preformatted) {
text = text.replace(/\n|\r|\t/g, " ");
}
return text;
}
if (element.nodeType == 1 /*Node.ELEMENT_NODE*/) {
var childrenPreformatted = preformatted || (element.tagName == "PRE");
var text = "";
for (var i = 0; i < element.childNodes.length; i++) {
var child = element.childNodes.item(i);
text += getTextContent(child, childrenPreformatted);
}
// Handle block elements that introduce newlines
// -- From HTML spec:
//<!ENTITY % block
// "P | %heading; | %list; | %preformatted; | DL | DIV | NOSCRIPT |
// BLOCKQUOTE | F:wORM | HR | TABLE | FIELDSET | ADDRESS">
//
// TODO: should potentially introduce multiple newlines to separate blocks
if (element.tagName == "P" || element.tagName == "BR" || element.tagName == "HR" || element.tagName == "DIV") {
text += "\n";
}
return text;
}
return '';
}
 
/**
* Convert all newlines to \m
*/
function normalizeNewlines(text)
{
return text.replace(/\r\n|\r/g, "\n");
}
 
/**
* Replace multiple sequential spaces with a single space, and then convert &nbsp; to space.
*/
function normalizeSpaces(text)
{
// IE has already done this conversion, so doing it again will remove multiple nbsp
if (browser.isIE)
{
return text;
}
 
// Replace multiple spaces with a single space
// TODO - this shouldn't occur inside PRE elements
text = text.replace(/\ +/g, " ");
 
// Replace &nbsp; with a space
var nbspPattern = new RegExp(String.fromCharCode(160), "g");
if (browser.isSafari) {
return replaceAll(text, String.fromCharCode(160), " ");
} else {
return text.replace(nbspPattern, " ");
}
}
 
function replaceAll(text, oldText, newText) {
while (text.indexOf(oldText) != -1) {
text = text.replace(oldText, newText);
}
return text;
}
 
 
function xmlDecode(text) {
text = text.replace(/&quot;/g, '"');
text = text.replace(/&apos;/g, "'");
text = text.replace(/&lt;/g, "<");
text = text.replace(/&gt;/g, ">");
text = text.replace(/&amp;/g, "&");
return text;
}
 
// Sets the text in this element
function setText(element, text) {
if (element.textContent) {
element.textContent = text;
} else if (element.innerText) {
element.innerText = text;
}
}
 
// Get the value of an <input> element
function getInputValue(inputElement) {
if (inputElement.type.toUpperCase() == 'CHECKBOX' ||
inputElement.type.toUpperCase() == 'RADIO')
{
return (inputElement.checked ? 'on' : 'off');
}
return inputElement.value;
}
 
/* Fire an event in a browser-compatible manner */
function triggerEvent(element, eventType, canBubble, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
 
canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
if (element.fireEvent) {
//alert(eventType)
var evt = createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown);
element.fireEvent('on' + eventType, evt);
//Fix for IE6-- this does work but isn't needed the bug was in the type function
//eval("element." + eventType + "();");
}
else {
var evt = document.createEvent('HTMLEvents');
 
evt.shiftKey = shiftKeyDown;
evt.metaKey = metaKeyDown;
evt.altKey = altKeyDown;
evt.ctrlKey = controlKeyDown;
 
evt.initEvent(eventType, canBubble, true);
element.dispatchEvent(evt);
}
}
 
function getKeyCodeFromKeySequence(keySequence) {
var match = /^\\(\d{1,3})$/.exec(keySequence);
if (match != null) {
return match[1];
}
match = /^.$/.exec(keySequence);
if (match != null) {
return match[0].charCodeAt(0);
}
// this is for backward compatibility with existing tests
// 1 digit ascii codes will break however because they are used for the digit chars
match = /^\d{2,3}$/.exec(keySequence);
if (match != null) {
return match[0];
}
//throw SeleniumError("invalid keySequence");
}
 
function createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
var evt = element.ownerDocument.createEventObject();
evt.shiftKey = shiftKeyDown;
evt.metaKey = metaKeyDown;
evt.altKey = altKeyDown;
evt.ctrlKey = controlKeyDown;
return evt;
}
 
function triggerKeyEvent(element, eventType, keySequence, canBubble, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
var keycode = getKeyCodeFromKeySequence(keySequence);
canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
if (element.fireEvent) {
 
var keyEvent = createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown);
keyEvent.keyCode = keycode;
element.fireEvent('on' + eventType, keyEvent);
}
else {
var evt;
if (window.KeyEvent) {
evt = document.createEvent('KeyEvents');
evt.initKeyEvent(eventType, true, true, window, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown, keycode, keycode);
} else {
evt = document.createEvent('UIEvents');
 
evt.shiftKey = shiftKeyDown;
evt.metaKey = metaKeyDown;
evt.altKey = altKeyDown;
evt.ctrlKey = controlKeyDown;
 
evt.initUIEvent(eventType, true, true, window, 1);
evt.keyCode = keycode;
}
 
element.dispatchEvent(evt);
}
}
 
/* Fire a mouse event in a browser-compatible manner */
function triggerMouseEvent(element, eventType, canBubble, clientX, clientY, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
clientX = clientX ? clientX : 0;
clientY = clientY ? clientY : 0;
 
//LOG.warn("windmill.events.triggerMouseEvent assumes setting screenX and screenY to 0 is ok");
var screenX = 0;
var screenY = 0;
 
canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
 
if (element.fireEvent) {
//LOG.info("element has fireEvent");
 
var evt = createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown);
evt.detail = 0;
evt.button = 1;
evt.relatedTarget = null;
if (!screenX && !screenY && !clientX && !clientY) {
//element.click();
if(eventType == "click"){
element.click();
}
element.fireEvent('on' + eventType);
//eval("element." + eventType + "();");
}
else {
evt.screenX = screenX;
evt.screenY = screenY;
evt.clientX = clientX;
evt.clientY = clientY;
 
// when we go this route, window.event is never set to contain the event we have just created.
// ideally we could just slide it in as follows in the try-block below, but this normally
// doesn't work. This is why I try to avoid this code path, which is only required if we need to
// set attributes on the event (e.g., clientX).
try {
window.event = evt;
}
catch(e) {
// getting an "Object does not support this action or property" error. Save the event away
// for future reference.
// TODO: is there a way to update window.event?
 
// work around for http://jira.openqa.org/browse/SEL-280 -- make the event available somewhere:
//selenium.browserbot.getCurrentWindow().selenium_event = evt;
}
 
element.fireEvent('on' + eventType, evt);
}
}
else {
 
//LOG.info("element doesn't have fireEvent");
var evt = document.createEvent('MouseEvents');
if (evt.initMouseEvent)
{
//LOG.info("element has initMouseEvent");
//Safari
evt.initMouseEvent(eventType, canBubble, true, document.defaultView, 1, screenX, screenY, clientX, clientY, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown, 0, null)
}
else {
//LOG.warn("element doesn't have initMouseEvent; firing an event which should -- but doesn't -- have other mouse-event related attributes here, as well as controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown");
evt.initEvent(eventType, canBubble, true);
 
evt.shiftKey = shiftKeyDown;
evt.metaKey = metaKeyDown;
evt.altKey = altKeyDown;
evt.ctrlKey = controlKeyDown;
 
}
//Used by safari
element.dispatchEvent(evt);
}
}
 
function removeLoadListener(element, command) {
LOG.info('Removing loadListenter for ' + element + ', ' + command);
if (window.removeEventListener)
element.removeEventListener("load", command, true);
else if (window.detachEvent)
element.detachEvent("onload", command);
}
 
function addLoadListener(element, command) {
LOG.info('Adding loadListenter for ' + element + ', ' + command);
if (window.addEventListener && !browserVersion.isOpera)
element.addEventListener("load", command, true);
else if (window.attachEvent)
element.attachEvent("onload", command);
}
 
/**
* Override the broken getFunctionName() method from JsUnit
* This file must be loaded _after_ the jsunitCore.js
*/
function getFunctionName(aFunction) {
var regexpResult = aFunction.toString().match(/function (\w*)/);
if (regexpResult && regexpResult[1]) {
return regexpResult[1];
}
return 'anonymous';
}
 
function getDocumentBase(doc) {
var bases = document.getElementsByTagName("base");
if (bases && bases.length && bases[0].href) {
return bases[0].href;
}
return "";
}
 
function describe(object, delimiter) {
var props = new Array();
for (var prop in object) {
props.push(prop + " -> " + object[prop]);
}
return props.join(delimiter || '\n');
}
 
var PatternMatcher = function(pattern) {
this.selectStrategy(pattern);
};
PatternMatcher.prototype = {
 
selectStrategy: function(pattern) {
this.pattern = pattern;
var strategyName = 'glob';
// by default
if (/^([a-z-]+):(.*)/.test(pattern)) {
var possibleNewStrategyName = RegExp.$1;
var possibleNewPattern = RegExp.$2;
if (PatternMatcher.strategies[possibleNewStrategyName]) {
strategyName = possibleNewStrategyName;
pattern = possibleNewPattern;
}
}
var matchStrategy = PatternMatcher.strategies[strategyName];
if (!matchStrategy) {
throw new SeleniumError("cannot find PatternMatcher.strategies." + strategyName);
}
this.strategy = matchStrategy;
this.matcher = new matchStrategy(pattern);
},
 
matches: function(actual) {
return this.matcher.matches(actual + '');
// Note: appending an empty string avoids a Konqueror bug
}
 
};
 
/**
* A "static" convenience method for easy matching
*/
PatternMatcher.matches = function(pattern, actual) {
return new PatternMatcher(pattern).matches(actual);
};
 
PatternMatcher.strategies = {
 
/**
* Exact matching, e.g. "exact:***"
*/
exact: function(expected) {
this.expected = expected;
this.matches = function(actual) {
return actual == this.expected;
};
},
 
/**
* Match by regular expression, e.g. "regexp:^[0-9]+$"
*/
regexp: function(regexpString) {
this.regexp = new RegExp(regexpString);
this.matches = function(actual) {
return this.regexp.test(actual);
};
},
 
regex: function(regexpString) {
this.regexp = new RegExp(regexpString);
this.matches = function(actual) {
return this.regexp.test(actual);
};
},
 
/**
* "globContains" (aka "wildmat") patterns, e.g. "glob:one,two,*",
* but don't require a perfect match; instead succeed if actual
* contains something that matches globString.
* Making this distinction is motivated by a bug in IE6 which
* leads to the browser hanging if we implement *TextPresent tests
* by just matching against a regular expression beginning and
* ending with ".*". The globcontains strategy allows us to satisfy
* the functional needs of the *TextPresent ops more efficiently
* and so avoid running into this IE6 freeze.
*/
globContains: function(globString) {
this.regexp = new RegExp(PatternMatcher.regexpFromGlobContains(globString));
this.matches = function(actual) {
return this.regexp.test(actual);
};
},
 
 
/**
* "glob" (aka "wildmat") patterns, e.g. "glob:one,two,*"
*/
glob: function(globString) {
this.regexp = new RegExp(PatternMatcher.regexpFromGlob(globString));
this.matches = function(actual) {
return this.regexp.test(actual);
};
}
 
};
 
PatternMatcher.convertGlobMetaCharsToRegexpMetaChars = function(glob) {
var re = glob;
re = re.replace(/([.^$+(){}\[\]\\|])/g, "\\$1");
re = re.replace(/\?/g, "(.|[\r\n])");
re = re.replace(/\*/g, "(.|[\r\n])*");
return re;
};
 
PatternMatcher.regexpFromGlobContains = function(globContains) {
return PatternMatcher.convertGlobMetaCharsToRegexpMetaChars(globContains);
};
 
PatternMatcher.regexpFromGlob = function(glob) {
return "^" + PatternMatcher.convertGlobMetaCharsToRegexpMetaChars(glob) + "$";
};
 
var Assert = {
 
fail: function(message) {
throw new AssertionFailedError(message);
},
 
/*
* Assert.equals(comment?, expected, actual)
*/
equals: function() {
var args = new AssertionArguments(arguments);
if (args.expected === args.actual) {
return;
}
Assert.fail(args.comment +
"Expected '" + args.expected +
"' but was '" + args.actual + "'");
},
 
/*
* Assert.matches(comment?, pattern, actual)
*/
matches: function() {
var args = new AssertionArguments(arguments);
if (PatternMatcher.matches(args.expected, args.actual)) {
return;
}
Assert.fail(args.comment +
"Actual value '" + args.actual +
"' did not match '" + args.expected + "'");
},
 
/*
* Assert.notMtches(comment?, pattern, actual)
*/
notMatches: function() {
var args = new AssertionArguments(arguments);
if (!PatternMatcher.matches(args.expected, args.actual)) {
return;
}
Assert.fail(args.comment +
"Actual value '" + args.actual +
"' did match '" + args.expected + "'");
}
 
};
 
// Preprocess the arguments to allow for an optional comment.
function AssertionArguments(args) {
if (args.length == 2) {
this.comment = "";
this.expected = args[0];
this.actual = args[1];
} else {
this.comment = args[0] + "; ";
this.expected = args[1];
this.actual = args[2];
}
}
 
function AssertionFailedError(message) {
this.isAssertionFailedError = true;
this.isSeleniumError = true;
this.message = message;
this.failureMessage = message;
}
 
function SeleniumError(message) {
var error = new Error(message);
error.isSeleniumError = true;
return error;
}
 
var Effect = new Object();
 
objectExtend(Effect, {
highlight : function(element) {
var highLightColor = "yellow";
if (element.originalColor == undefined) { // avoid picking up highlight
element.originalColor = elementGetStyle(element, "background-color");
}
elementSetStyle(element, {"background-color" : highLightColor});
window.setTimeout(function() {
//if element is orphan, probably page of it has already gone, so ignore
if (!element.parentNode) {
return;
}
elementSetStyle(element, {"background-color" : element.originalColor});
}, 200);
}
});
 
 
// for use from vs.2003 debugger
function o2s(obj) {
var s = "";
for (key in obj) {
var line = key + "->" + obj[key];
line.replace("\n", " ");
s += line + "\n";
}
return s;
}
 
var seenReadyStateWarning = false;
 
function openSeparateApplicationWindow(url) {
// resize the Selenium window itself
window.resizeTo(1200, 500);
window.moveTo(window.screenX, 0);
 
var appWindow = window.open(url + '?start=true', 'main');
try {
var windowHeight = 500;
if (window.outerHeight) {
windowHeight = window.outerHeight;
} else if (document.documentElement && document.documentElement.offsetHeight) {
windowHeight = document.documentElement.offsetHeight;
}
 
if (window.screenLeft && !window.screenX) window.screenX = window.screenLeft;
if (window.screenTop && !window.screenY) window.screenY = window.screenTop;
 
appWindow.resizeTo(1200, screen.availHeight - windowHeight - 60);
appWindow.moveTo(window.screenX, window.screenY + windowHeight + 25);
} catch (e) {
LOG.error("Couldn't resize app window");
LOG.exception(e);
}
 
 
if (window.document.readyState == null && !seenReadyStateWarning) {
alert("Beware! Mozilla bug 300992 means that we can't always reliably detect when a new page has loaded. Install the Selenium IDE extension or the readyState extension available from selenium.openqa.org to make page load detection more reliable.");
seenReadyStateWarning = true;
}
 
return appWindow;
}
 
var URLConfiguration = classCreate();
objectExtend(URLConfiguration.prototype, {
initialize: function() {
},
_isQueryParameterTrue: function (name) {
var parameterValue = this._getQueryParameter(name);
if (parameterValue == null) return false;
if (parameterValue.toLowerCase() == "true") return true;
if (parameterValue.toLowerCase() == "on") return true;
return false;
},
 
_getQueryParameter: function(searchKey) {
var str = this.queryString
if (str == null) return null;
var clauses = str.split('&');
for (var i = 0; i < clauses.length; i++) {
var keyValuePair = clauses[i].split('=', 2);
var key = unescape(keyValuePair[0]);
if (key == searchKey) {
return unescape(keyValuePair[1]);
}
}
return null;
},
 
_extractArgs: function() {
var str = SeleniumHTARunner.commandLine;
if (str == null || str == "") return new Array();
var matches = str.match(/(?:\"([^\"]+)\"|(?!\"([^\"]+)\")(\S+))/g);
// We either want non quote stuff ([^"]+) surrounded by quotes
// or we want to look-ahead, see that the next character isn't
// a quoted argument, and then grab all the non-space stuff
// this will return for the line: "foo" bar
// the results "\"foo\"" and "bar"
 
// So, let's unquote the quoted arguments:
var args = new Array;
for (var i = 0; i < matches.length; i++) {
args[i] = matches[i];
args[i] = args[i].replace(/^"(.*)"$/, "$1");
}
return args;
},
 
isMultiWindowMode:function() {
return this._isQueryParameterTrue('multiWindow');
}
});
 
 
function safeScrollIntoView(element) {
if (element.scrollIntoView) {
element.scrollIntoView(false);
return;
}
// TODO: work out how to scroll browsers that don't support
// scrollIntoView (like Konqueror)
}
Property changes : Added: svn:eol-style + native
dot-two-refactor/windmill/js/lib/builder.js New file
0,0 → 1,130
var $ = function(id) {
return document.getElementById(id);
};
 
 
opener.windmill.builder.methodSelected =function() {
var method = $('methodDD').value;