##// END OF EJS Templates
This is a manual merge of certain things in the ipython1-dev branch, revision 46, into the main ...
Brian E Granger -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,14 b''
1 # encoding: utf-8
2
3 __docformat__ = "restructuredtext en"
4
5 #-------------------------------------------------------------------------------
6 # Copyright (C) 2008 The IPython Development Team
7 #
8 # Distributed under the terms of the BSD License. The full license is in
9 # the file COPYING, distributed as part of this software.
10 #-------------------------------------------------------------------------------
11
12 #-------------------------------------------------------------------------------
13 # Imports
14 #------------------------------------------------------------------------------- No newline at end of file
@@ -0,0 +1,99 b''
1 # encoding: utf-8
2
3 """This is the official entry point to IPython's configuration system. """
4
5 __docformat__ = "restructuredtext en"
6
7 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
13
14 #-------------------------------------------------------------------------------
15 # Imports
16 #-------------------------------------------------------------------------------
17
18 import os
19 from IPython.config.cutils import get_home_dir, get_ipython_dir
20 from IPython.external.configobj import ConfigObj
21
22 class ConfigObjManager(object):
23
24 def __init__(self, configObj, filename):
25 self.current = configObj
26 self.current.indent_type = ' '
27 self.filename = filename
28 # self.write_default_config_file()
29
30 def get_config_obj(self):
31 return self.current
32
33 def update_config_obj(self, newConfig):
34 self.current.merge(newConfig)
35
36 def update_config_obj_from_file(self, filename):
37 newConfig = ConfigObj(filename, file_error=False)
38 self.current.merge(newConfig)
39
40 def update_config_obj_from_default_file(self, ipythondir=None):
41 fname = self.resolve_file_path(self.filename, ipythondir)
42 self.update_config_obj_from_file(fname)
43
44 def write_config_obj_to_file(self, filename):
45 f = open(filename, 'w')
46 self.current.write(f)
47 f.close()
48
49 def write_default_config_file(self):
50 ipdir = get_ipython_dir()
51 fname = ipdir + '/' + self.filename
52 if not os.path.isfile(fname):
53 print "Writing the configuration file to: " + fname
54 self.write_config_obj_to_file(fname)
55
56 def _import(self, key):
57 package = '.'.join(key.split('.')[0:-1])
58 obj = key.split('.')[-1]
59 execString = 'from %s import %s' % (package, obj)
60 exec execString
61 exec 'temp = %s' % obj
62 return temp
63
64 def resolve_file_path(self, filename, ipythondir = None):
65 """Resolve filenames into absolute paths.
66
67 This function looks in the following directories in order:
68
69 1. In the current working directory or by absolute path with ~ expanded
70 2. In ipythondir if that is set
71 3. In the IPYTHONDIR environment variable if it exists
72 4. In the ~/.ipython directory
73
74 Note: The IPYTHONDIR is also used by the trunk version of IPython so
75 changing it will also affect it was well.
76 """
77
78 # In cwd or by absolute path with ~ expanded
79 trythis = os.path.expanduser(filename)
80 if os.path.isfile(trythis):
81 return trythis
82
83 # In ipythondir if it is set
84 if ipythondir is not None:
85 trythis = ipythondir + '/' + filename
86 if os.path.isfile(trythis):
87 return trythis
88
89 trythis = get_ipython_dir() + '/' + filename
90 if os.path.isfile(trythis):
91 return trythis
92
93 return None
94
95
96
97
98
99
@@ -0,0 +1,99 b''
1 # encoding: utf-8
2
3 """Configuration-related utilities for all IPython."""
4
5 __docformat__ = "restructuredtext en"
6
7 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
13
14 #-------------------------------------------------------------------------------
15 # Imports
16 #-------------------------------------------------------------------------------
17
18 import os
19 import sys
20
21 #---------------------------------------------------------------------------
22 # Normal code begins
23 #---------------------------------------------------------------------------
24
25 class HomeDirError(Exception):
26 pass
27
28 def get_home_dir():
29 """Return the closest possible equivalent to a 'home' directory.
30
31 We first try $HOME. Absent that, on NT it's $HOMEDRIVE\$HOMEPATH.
32
33 Currently only Posix and NT are implemented, a HomeDirError exception is
34 raised for all other OSes. """
35
36 isdir = os.path.isdir
37 env = os.environ
38 try:
39 homedir = env['HOME']
40 if not isdir(homedir):
41 # in case a user stuck some string which does NOT resolve to a
42 # valid path, it's as good as if we hadn't foud it
43 raise KeyError
44 return homedir
45 except KeyError:
46 if os.name == 'posix':
47 raise HomeDirError,'undefined $HOME, IPython can not proceed.'
48 elif os.name == 'nt':
49 # For some strange reason, win9x returns 'nt' for os.name.
50 try:
51 homedir = os.path.join(env['HOMEDRIVE'],env['HOMEPATH'])
52 if not isdir(homedir):
53 homedir = os.path.join(env['USERPROFILE'])
54 if not isdir(homedir):
55 raise HomeDirError
56 return homedir
57 except:
58 try:
59 # Use the registry to get the 'My Documents' folder.
60 import _winreg as wreg
61 key = wreg.OpenKey(wreg.HKEY_CURRENT_USER,
62 "Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
63 homedir = wreg.QueryValueEx(key,'Personal')[0]
64 key.Close()
65 if not isdir(homedir):
66 e = ('Invalid "Personal" folder registry key '
67 'typically "My Documents".\n'
68 'Value: %s\n'
69 'This is not a valid directory on your system.' %
70 homedir)
71 raise HomeDirError(e)
72 return homedir
73 except HomeDirError:
74 raise
75 except:
76 return 'C:\\'
77 elif os.name == 'dos':
78 # Desperate, may do absurd things in classic MacOS. May work under DOS.
79 return 'C:\\'
80 else:
81 raise HomeDirError,'support for your operating system not implemented.'
82
83 def get_ipython_dir():
84 ipdir_def = '.ipython'
85 home_dir = get_home_dir()
86 ipdir = os.path.abspath(os.environ.get('IPYTHONDIR',
87 os.path.join(home_dir,ipdir_def)))
88 return ipdir
89
90 def import_item(key):
91 """
92 Import and return bar given the string foo.bar.
93 """
94 package = '.'.join(key.split('.')[0:-1])
95 obj = key.split('.')[-1]
96 execString = 'from %s import %s' % (package, obj)
97 exec execString
98 exec 'temp = %s' % obj
99 return temp
This diff has been collapsed as it changes many lines, (622 lines changed) Show them Hide them
@@ -0,0 +1,622 b''
1 # encoding: utf-8
2
3 """Mix of ConfigObj and Struct-like access.
4
5 Provides:
6
7 - Coupling a Struct object to a ConfigObj one, so that changes to the Traited
8 instance propagate back into the ConfigObj.
9
10 - A declarative interface for describing configurations that automatically maps
11 to valid ConfigObj representations.
12
13 - From these descriptions, valid .conf files can be auto-generated, with class
14 docstrings and traits information used for initial auto-documentation.
15
16 - Hierarchical inclusion of files, so that a base config can be overridden only
17 in specific spots.
18
19
20 Notes:
21
22 The file creation policy is:
23
24 1. Creating a SConfigManager(FooConfig,'missingfile.conf') will work
25 fine, and 'missingfile.conf' will be created empty.
26
27 2. Creating SConfigManager(FooConfig,'OKfile.conf') where OKfile.conf has
28
29 include = 'missingfile.conf'
30
31 conks out with IOError.
32
33 My rationale is that creating top-level empty files is a common and
34 reasonable need, but that having invalid include statements should
35 raise an error right away, so people know immediately that their files
36 have gone stale.
37
38
39 TODO:
40
41 - Turn the currently interactive tests into proper doc/unit tests. Complete
42 docstrings.
43
44 - Write the real ipython1 config system using this. That one is more
45 complicated than either the MPL one or the fake 'ipythontest' that I wrote
46 here, and it requires solving the issue of declaring references to other
47 objects inside the config files.
48
49 - [Low priority] Write a custom TraitsUI view so that hierarchical
50 configurations provide nicer interactive editing. The automatic system is
51 remarkably good, but for very complex configurations having a nicely
52 organized view would be nice.
53 """
54
55 __docformat__ = "restructuredtext en"
56 __license__ = 'BSD'
57
58 #-------------------------------------------------------------------------------
59 # Copyright (C) 2008 The IPython Development Team
60 #
61 # Distributed under the terms of the BSD License. The full license is in
62 # the file COPYING, distributed as part of this software.
63 #-------------------------------------------------------------------------------
64
65 #-------------------------------------------------------------------------------
66 # Imports
67 #-------------------------------------------------------------------------------
68
69 ############################################################################
70 # Stdlib imports
71 ############################################################################
72 from cStringIO import StringIO
73 from inspect import isclass
74
75 import os
76 import textwrap
77
78 ############################################################################
79 # External imports
80 ############################################################################
81
82 from IPython.external import configobj
83
84 ############################################################################
85 # Utility functions
86 ############################################################################
87
88 def get_split_ind(seq, N):
89 """seq is a list of words. Return the index into seq such that
90 len(' '.join(seq[:ind])<=N
91 """
92
93 sLen = 0
94 # todo: use Alex's xrange pattern from the cbook for efficiency
95 for (word, ind) in zip(seq, range(len(seq))):
96 sLen += len(word) + 1 # +1 to account for the len(' ')
97 if sLen>=N: return ind
98 return len(seq)
99
100 def wrap(prefix, text, cols, max_lines=6):
101 """'wrap text with prefix at length cols"""
102 pad = ' '*len(prefix.expandtabs())
103 available = cols - len(pad)
104
105 seq = text.split(' ')
106 Nseq = len(seq)
107 ind = 0
108 lines = []
109 while ind<Nseq:
110 lastInd = ind
111 ind += get_split_ind(seq[ind:], available)
112 lines.append(seq[lastInd:ind])
113
114 num_lines = len(lines)
115 abbr_end = max_lines // 2
116 abbr_start = max_lines - abbr_end
117 lines_skipped = False
118 for i in range(num_lines):
119 if i == 0:
120 # add the prefix to the first line, pad with spaces otherwise
121 ret = prefix + ' '.join(lines[i]) + '\n'
122 elif i < abbr_start or i > num_lines-abbr_end-1:
123 ret += pad + ' '.join(lines[i]) + '\n'
124 else:
125 if not lines_skipped:
126 lines_skipped = True
127 ret += ' <...snipped %d lines...> \n' % (num_lines-max_lines)
128 # for line in lines[1:]:
129 # ret += pad + ' '.join(line) + '\n'
130 return ret[:-1]
131
132 def dedent(txt):
133 """A modified version of textwrap.dedent, specialized for docstrings.
134
135 This version doesn't get confused by the first line of text having
136 inconsistent indentation from the rest, which happens a lot in docstrings.
137
138 :Examples:
139
140 >>> s = '''
141 ... First line.
142 ... More...
143 ... End'''
144
145 >>> print dedent(s)
146 First line.
147 More...
148 End
149
150 >>> s = '''First line
151 ... More...
152 ... End'''
153
154 >>> print dedent(s)
155 First line
156 More...
157 End
158 """
159 out = [textwrap.dedent(t) for t in txt.split('\n',1)
160 if t and not t.isspace()]
161 return '\n'.join(out)
162
163
164 def comment(strng,indent=''):
165 """return an input string, commented out"""
166 template = indent + '# %s'
167 lines = [template % s for s in strng.splitlines(True)]
168 return ''.join(lines)
169
170
171 def configobj2str(cobj):
172 """Dump a Configobj instance to a string."""
173 outstr = StringIO()
174 cobj.write(outstr)
175 return outstr.getvalue()
176
177 def get_config_filename(conf):
178 """Find the filename attribute of a ConfigObj given a sub-section object.
179 """
180 depth = conf.depth
181 for d in range(depth):
182 conf = conf.parent
183 return conf.filename
184
185 def sconf2File(sconf,fname,force=False):
186 """Write a SConfig instance to a given filename.
187
188 :Keywords:
189
190 force : bool (False)
191 If true, force writing even if the file exists.
192 """
193
194 if os.path.isfile(fname) and not force:
195 raise IOError("File %s already exists, use force=True to overwrite" %
196 fname)
197
198 txt = repr(sconf)
199
200 fobj = open(fname,'w')
201 fobj.write(txt)
202 fobj.close()
203
204 def filter_scalars(sc):
205 """ input sc MUST be sorted!!!"""
206 scalars = []
207 maxi = len(sc)-1
208 i = 0
209 while i<len(sc):
210 t = sc[i]
211 if t.startswith('_sconf_'):
212 # Skip altogether private _sconf_ attributes, so we actually issue
213 # a 'continue' call to avoid the append(t) below
214 i += 1
215 continue
216 scalars.append(t)
217 i += 1
218
219 return scalars
220
221
222 def get_scalars(obj):
223 """Return scalars for a Sconf class object"""
224
225 skip = set(['trait_added','trait_modified'])
226 sc = [k for k in obj.__dict__ if not k.startswith('_')]
227 sc.sort()
228 return filter_scalars(sc)
229
230
231 def get_sections(obj,sectionClass):
232 """Return sections for a Sconf class object"""
233 return [(n,v) for (n,v) in obj.__dict__.iteritems()
234 if isclass(v) and issubclass(v,sectionClass)]
235
236
237 def get_instance_sections(inst):
238 """Return sections for a Sconf instance"""
239 sections = [(k,v) for k,v in inst.__dict__.iteritems()
240 if isinstance(v,SConfig) and not k=='_sconf_parent']
241 # Sort the sections by name
242 sections.sort(key=lambda x:x[0])
243 return sections
244
245
246 def partition_instance(obj):
247 """Return scalars,sections for a given Sconf instance.
248 """
249 scnames = []
250 sections = []
251 for k,v in obj.__dict__.iteritems():
252 if isinstance(v,SConfig):
253 if not k=='_sconf_parent':
254 sections.append((k,v))
255 else:
256 scnames.append(k)
257
258 # Sort the sections by name
259 sections.sort(key=lambda x:x[0])
260
261 # Sort the scalar names, filter them and then extract the actual objects
262 scnames.sort()
263 scnames = filter_scalars(scnames)
264 scalars = [(s,obj.__dict__[s]) for s in scnames]
265
266 return scalars, sections
267
268
269 def mk_ConfigObj(filename,mk_missing_file=True):
270 """Return a ConfigObj instance with our hardcoded conventions.
271
272 Use a simple factory that wraps our option choices for using ConfigObj.
273 I'm hard-wiring certain choices here, so we'll always use instances with
274 THESE choices.
275
276 :Parameters:
277
278 filename : string
279 File to read from.
280
281 :Keywords:
282 makeMissingFile : bool (True)
283 If true, the file named by `filename` may not yet exist and it will be
284 automatically created (empty). Else, if `filename` doesn't exist, an
285 IOError will be raised.
286 """
287
288 if mk_missing_file:
289 create_empty = True
290 file_error = False
291 else:
292 create_empty = False
293 file_error = True
294
295 return configobj.ConfigObj(filename,
296 create_empty=create_empty,
297 file_error=file_error,
298 indent_type=' ',
299 interpolation='Template',
300 unrepr=True)
301
302 nullConf = mk_ConfigObj(None)
303
304
305 class RecursiveConfigObj(object):
306 """Object-oriented interface for recursive ConfigObj constructions."""
307
308 def __init__(self,filename):
309 """Return a ConfigObj instance with our hardcoded conventions.
310
311 Use a simple factory that wraps our option choices for using ConfigObj.
312 I'm hard-wiring certain choices here, so we'll always use instances with
313 THESE choices.
314
315 :Parameters:
316
317 filename : string
318 File to read from.
319 """
320
321 self.comp = []
322 self.conf = self._load(filename)
323
324 def _load(self,filename,mk_missing_file=True):
325 conf = mk_ConfigObj(filename,mk_missing_file)
326
327 # Do recursive loading. We only allow (or at least honor) the include
328 # tag at the top-level. For now, we drop the inclusion information so
329 # that there are no restrictions on which levels of the SConfig
330 # hierarchy can use include statements. But this means that
331
332 # if bookkeeping of each separate component of the recursive
333 # construction was requested, make a separate object for storage
334 # there, since we don't want that to be modified by the inclusion
335 # process.
336 self.comp.append(mk_ConfigObj(filename,mk_missing_file))
337
338 incfname = conf.pop('include',None)
339 if incfname is not None:
340 # Do recursive load. We don't want user includes that point to
341 # missing files to fail silently, so in the recursion we disable
342 # auto-creation of missing files.
343 confinc = self._load(incfname,mk_missing_file=False)
344
345 # Update with self to get proper ordering (included files provide
346 # base data, current one overwrites)
347 confinc.update(conf)
348 # And do swap to return the updated structure
349 conf = confinc
350 # Set the filename to be the original file instead of the included
351 # one
352 conf.filename = filename
353 return conf
354
355 ############################################################################
356 # Main SConfig class and supporting exceptions
357 ############################################################################
358
359 class SConfigError(Exception): pass
360
361 class SConfigInvalidKeyError(SConfigError): pass
362
363 class SConfig(object):
364 """A class representing configuration objects.
365
366 Note: this class should NOT have any traits itself, since the actual traits
367 will be declared by subclasses. This class is meant to ONLY declare the
368 necessary initialization/validation methods. """
369
370 # Any traits declared here are prefixed with _sconf_ so that our special
371 # formatting/analysis utilities can distinguish them from user traits and
372 # can avoid them.
373
374 # Once created, the tree's hierarchy can NOT be modified
375 _sconf_parent = None
376
377 def __init__(self,config=None,parent=None,monitor=None):
378 """Makes an SConfig object out of a ConfigObj instance
379 """
380
381 if config is None:
382 config = mk_ConfigObj(None)
383
384 # Validate the set of scalars ...
385 my_scalars = set(get_scalars(self))
386 cf_scalars = set(config.scalars)
387 invalid_scalars = cf_scalars - my_scalars
388 if invalid_scalars:
389 config_fname = get_config_filename(config)
390 m=("In config defined in file: %r\n"
391 "Error processing section: %s\n"
392 "These keys are invalid : %s\n"
393 "Valid key names : %s\n"
394 % (config_fname,self.__class__.__name__,
395 list(invalid_scalars),list(my_scalars)))
396 raise SConfigInvalidKeyError(m)
397
398 # ... and sections
399 section_items = get_sections(self.__class__,SConfig)
400 my_sections = set([n for n,v in section_items])
401 cf_sections = set(config.sections)
402 invalid_sections = cf_sections - my_sections
403 if invalid_sections:
404 config_fname = get_config_filename(config)
405 m = ("In config defined in file: %r\n"
406 "Error processing section: %s\n"
407 "These subsections are invalid : %s\n"
408 "Valid subsection names : %s\n"
409 % (config_fname,self.__class__.__name__,
410 list(invalid_sections),list(my_sections)))
411 raise SConfigInvalidKeyError(m)
412
413 self._sconf_parent = parent
414
415 # Now set the traits based on the config
416 for k in my_scalars:
417 setattr(self,k,config[k])
418
419 # And build subsections
420 for s,v in section_items:
421 sec_config = config.setdefault(s,{})
422 section = v(sec_config,self,monitor=monitor)
423
424 # We must use add_trait instead of setattr because we inherit from
425 # HasStrictTraits, but we need to then do a 'dummy' getattr call on
426 # self so the class trait propagates to the instance.
427 self.add_trait(s,section)
428 getattr(self,s)
429
430 def __repr__(self,depth=0):
431 """Dump a section to a string."""
432
433 indent = ' '*(depth)
434
435 top_name = self.__class__.__name__
436
437 if depth == 0:
438 label = '# %s - plaintext (in .conf format)\n' % top_name
439 else:
440 # Section titles are indented one level less than their contents in
441 # the ConfigObj write methods.
442 sec_indent = ' '*(depth-1)
443 label = '\n'+sec_indent+('[' * depth) + top_name + (']'*depth)
444
445 out = [label]
446
447 doc = self.__class__.__doc__
448 if doc is not None:
449 out.append(comment(dedent(doc),indent))
450
451 scalars, sections = partition_instance(self)
452
453 for s,v in scalars:
454 try:
455 info = self.__base_traits__[s].handler.info()
456 # Get a short version of info with lines of max. 78 chars, so
457 # that after commenting them out (with '# ') they are at most
458 # 80-chars long.
459 out.append(comment(wrap('',info.replace('\n', ' '),78-len(indent)),indent))
460 except (KeyError,AttributeError):
461 pass
462 out.append(indent+('%s = %r' % (s,v)))
463
464 for sname,sec in sections:
465 out.append(sec.__repr__(depth+1))
466
467 return '\n'.join(out)
468
469 def __str__(self):
470 return self.__class__.__name__
471
472
473 ##############################################################################
474 # High-level class(es) and utilities for handling a coupled pair of SConfig and
475 # ConfigObj instances.
476 ##############################################################################
477
478 def path_to_root(obj):
479 """Find the path to the root of a nested SConfig instance."""
480 ob = obj
481 path = []
482 while ob._sconf_parent is not None:
483 path.append(ob.__class__.__name__)
484 ob = ob._sconf_parent
485 path.reverse()
486 return path
487
488
489 def set_value(fconf,path,key,value):
490 """Set a value on a ConfigObj instance, arbitrarily deep."""
491 section = fconf
492 for sname in path:
493 section = section.setdefault(sname,{})
494 section[key] = value
495
496
497 def fmonitor(fconf):
498 """Make a monitor for coupling SConfig instances to ConfigObj ones.
499
500 We must use a closure because Traits makes assumptions about the functions
501 used with on_trait_change() that prevent the use of a callable instance.
502 """
503
504 def mon(obj,name,new):
505 #print 'OBJ:',obj # dbg
506 #print 'NAM:',name # dbg
507 #print 'NEW:',new # dbg
508 set_value(fconf,path_to_root(obj),name,new)
509
510 return mon
511
512
513 class SConfigManager(object):
514 """A simple object to manage and sync a SConfig and a ConfigObj pair.
515 """
516
517 def __init__(self,configClass,configFilename,filePriority=True):
518 """Make a new SConfigManager.
519
520 :Parameters:
521
522 configClass : class
523
524 configFilename : string
525 If the filename points to a non-existent file, it will be created
526 empty. This is useful when creating a file form from an existing
527 configClass with the class defaults.
528
529
530 :Keywords:
531
532 filePriority : bool (True)
533
534 If true, at construction time the file object takes priority and
535 overwrites the contents of the config object. Else, the data flow
536 is reversed and the file object will be overwritten with the
537 configClass defaults at write() time.
538 """
539
540 rconf = RecursiveConfigObj(configFilename)
541 # In a hierarchical object, the two following fconfs are *very*
542 # different. In self.fconf, we'll keep the outer-most fconf associated
543 # directly to the original filename. self.fconf_combined, instead,
544 # contains an object which has the combined effect of having merged all
545 # the called files in the recursive chain.
546 self.fconf = rconf.comp[0]
547 self.fconf_combined = rconf.conf
548
549 # Create a monitor to track and apply trait changes to the sconf
550 # instance over into the fconf one
551 monitor = fmonitor(self.fconf)
552
553 if filePriority:
554 self.sconf = configClass(self.fconf_combined,monitor=monitor)
555 else:
556 # Push defaults onto file object
557 self.sconf = configClass(mk_ConfigObj(None),monitor=monitor)
558 self.fconfUpdate(self.fconf,self.sconf)
559
560 def fconfUpdate(self,fconf,sconf):
561 """Update the fconf object with the data from sconf"""
562
563 scalars, sections = partition_instance(sconf)
564
565 for s,v in scalars:
566 fconf[s] = v
567
568 for secname,sec in sections:
569 self.fconfUpdate(fconf.setdefault(secname,{}),sec)
570
571 def write(self,filename=None):
572 """Write out to disk.
573
574 This method writes out only to the top file in a hierarchical
575 configuration, which means that the class defaults and other values not
576 explicitly set in the top level file are NOT written out.
577
578 :Keywords:
579
580 filename : string (None)
581 If given, the output is written to this file, otherwise the
582 .filename attribute of the top-level configuration object is used.
583 """
584 if filename is not None:
585 file_obj = open(filename,'w')
586 out = self.fconf.write(file_obj)
587 file_obj.close()
588 return out
589 else:
590 return self.fconf.write()
591
592 def writeAll(self,filename=None):
593 """Write out the entire configuration to disk.
594
595 This method, in contrast with write(), updates the .fconf_combined
596 object with the *entire* .sconf instance, and then writes it out to
597 disk. This method is thus useful for generating files that have a
598 self-contained, non-hierarchical file.
599
600 :Keywords:
601
602 filename : string (None)
603 If given, the output is written to this file, otherwise the
604 .filename attribute of the top-level configuration object is used.
605 """
606 if filename is not None:
607 file_obj = open(filename,'w')
608 self.fconfUpdate(self.fconf_combined,self.sconf)
609 out = self.fconf_combined.write(file_obj)
610 file_obj.close()
611 return out
612 else:
613 self.fconfUpdate(self.fconf_combined,self.sconf)
614 return self.fconf_combined.write()
615
616 def sconf_str(self):
617 return str(self.sconf)
618
619 def fconf_str(self):
620 return configobj2str(self.fconf)
621
622 __repr__ = __str__ = fconf_str
@@ -0,0 +1,37 b''
1 """Little utilities for testing tconfig.
2
3 This module is meant to be used via
4
5 import sctst; reload(sctst)
6 from sctst import *
7
8 at the top of the actual test scripts, so that they all get the entire set of
9 common test tools with minimal fuss.
10 """
11
12 # Standard library imports
13 import os
14 import sys
15 from pprint import pprint
16
17 # Our own imports.
18
19 from IPython.config import sconfig
20 reload(sconfig)
21
22 from sconfig import mkConfigObj, RecursiveConfigObj, SConfigManager, \
23 sconf2file
24
25 # Simple utilities/classes for testing
26
27 def cat(fname):
28 print '### FILENAME:',fname
29 print open(fname).read()
30
31
32 class App(object):
33 """A trivial 'application' class to be initialized.
34 """
35 def __init__(self,config_class,config_filename):
36 self.rcman = SConfigManager(config_class,config_filename)
37 self.rc = self.rcman.sconf
@@ -0,0 +1,14 b''
1 # Toy example of a TConfig-based configuration description
2
3 # This is the class declaration for the configuration:
4
5 # SimpleConfig
6 # Configuration for my application
7
8 solver = "Iterative2"
9
10 [Protocol]
11 # Specify the Protocol
12
13 ptype = "http2"
14 max_users = 4
@@ -0,0 +1,14 b''
1 # Toy example of a TConfig-based configuration description
2
3 # This is the class declaration for the configuration:
4
5 # SimpleConfig
6 # Configuration for my application
7
8 datafile = string(default='data.txt')
9 solver = option('Direct','Iterative')
10
11 [Protocol]
12 # Specify the Protocol
13 ptype = option('http','ftp','ssh')
14 max_users = integer(1,10)
@@ -0,0 +1,59 b''
1 """Toy example of reading an SConf object."""
2
3 from IPython.external.configobj import ConfigObj
4 from IPython.external import configobj, validate
5
6
7 from IPython.config import sconfig
8 reload(sconfig)
9
10 configspecfilename = 'simple.spec.conf'
11 filename = 'simple.conf'
12
13 print '*'*80
14 configspec = ConfigObj(configspecfilename, encoding='UTF8',
15 list_values=False)
16 print sconfig.configobj2str(configspec)
17
18 print '*'*80
19 config = ConfigObj(filename, configspec=configspec,
20 interpolation='Template',
21 unrepr=True)
22 print sconfig.configobj2str(config)
23 vdt = validate.Validator()
24 test = config.validate(vdt,preserve_errors=True)
25
26 ####
27 vdt = validate.Validator()
28 class Bunch: pass
29 vf = Bunch()
30 vf.__dict__.update(vdt.functions)
31 vf.pass_ = vdt.functions['pass']
32 vf.__dict__.pop('',None)
33 vf.__dict__.pop('pass',None)
34 ###
35
36
37 if test==True:
38 print 'All OK'
39 else:
40 err = configobj.flatten_errors(config,test)
41 print 'Flat errors:'
42 for secs,key,result in err:
43 if secs == []:
44 print 'DEFAULT:','key:',key,'err:',result
45 else:
46 print 'Secs:',secs,'key:',key,'err:',result
47
48
49 ##
50 print '*'*80
51
52 sc = sconfig.SConfig(configspecfilename)
53
54
55
56 ####
57
58
59
@@ -0,0 +1,278 b''
1 # -*- coding: utf-8 -*-
2 """String interpolation for Python (by Ka-Ping Yee, 14 Feb 2000).
3
4 This module lets you quickly and conveniently interpolate values into
5 strings (in the flavour of Perl or Tcl, but with less extraneous
6 punctuation). You get a bit more power than in the other languages,
7 because this module allows subscripting, slicing, function calls,
8 attribute lookup, or arbitrary expressions. Variables and expressions
9 are evaluated in the namespace of the caller.
10
11 The itpl() function returns the result of interpolating a string, and
12 printpl() prints out an interpolated string. Here are some examples:
13
14 from Itpl import printpl
15 printpl("Here is a $string.")
16 printpl("Here is a $module.member.")
17 printpl("Here is an $object.member.")
18 printpl("Here is a $functioncall(with, arguments).")
19 printpl("Here is an ${arbitrary + expression}.")
20 printpl("Here is an $array[3] member.")
21 printpl("Here is a $dictionary['member'].")
22
23 The filter() function filters a file object so that output through it
24 is interpolated. This lets you produce the illusion that Python knows
25 how to do interpolation:
26
27 import Itpl
28 sys.stdout = Itpl.filter()
29 f = "fancy"
30 print "Isn't this $f?"
31 print "Standard output has been replaced with a $sys.stdout object."
32 sys.stdout = Itpl.unfilter()
33 print "Okay, back $to $normal."
34
35 Under the hood, the Itpl class represents a string that knows how to
36 interpolate values. An instance of the class parses the string once
37 upon initialization; the evaluation and substitution can then be done
38 each time the instance is evaluated with str(instance). For example:
39
40 from Itpl import Itpl
41 s = Itpl("Here is $foo.")
42 foo = 5
43 print str(s)
44 foo = "bar"
45 print str(s)
46
47 $Id: Itpl.py 2305 2007-05-04 05:34:42Z bgranger $
48 """ # ' -> close an open quote for stupid emacs
49
50 #*****************************************************************************
51 #
52 # Copyright (c) 2001 Ka-Ping Yee <ping@lfw.org>
53 #
54 #
55 # Published under the terms of the MIT license, hereby reproduced:
56 #
57 # Permission is hereby granted, free of charge, to any person obtaining a copy
58 # of this software and associated documentation files (the "Software"), to
59 # deal in the Software without restriction, including without limitation the
60 # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
61 # sell copies of the Software, and to permit persons to whom the Software is
62 # furnished to do so, subject to the following conditions:
63 #
64 # The above copyright notice and this permission notice shall be included in
65 # all copies or substantial portions of the Software.
66 #
67 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
68 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
69 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
70 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
71 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
72 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
73 # IN THE SOFTWARE.
74 #
75 #*****************************************************************************
76
77 __author__ = 'Ka-Ping Yee <ping@lfw.org>'
78 __license__ = 'MIT'
79
80 import string
81 import sys
82 from tokenize import tokenprog
83 from types import StringType
84
85 class ItplError(ValueError):
86 def __init__(self, text, pos):
87 self.text = text
88 self.pos = pos
89 def __str__(self):
90 return "unfinished expression in %s at char %d" % (
91 repr(self.text), self.pos)
92
93 def matchorfail(text, pos):
94 match = tokenprog.match(text, pos)
95 if match is None:
96 raise ItplError(text, pos)
97 return match, match.end()
98
99 class Itpl:
100 """Class representing a string with interpolation abilities.
101
102 Upon creation, an instance works out what parts of the format
103 string are literal and what parts need to be evaluated. The
104 evaluation and substitution happens in the namespace of the
105 caller when str(instance) is called."""
106
107 def __init__(self, format,codec='utf_8',encoding_errors='backslashreplace'):
108 """The single mandatory argument to this constructor is a format
109 string.
110
111 The format string is parsed according to the following rules:
112
113 1. A dollar sign and a name, possibly followed by any of:
114 - an open-paren, and anything up to the matching paren
115 - an open-bracket, and anything up to the matching bracket
116 - a period and a name
117 any number of times, is evaluated as a Python expression.
118
119 2. A dollar sign immediately followed by an open-brace, and
120 anything up to the matching close-brace, is evaluated as
121 a Python expression.
122
123 3. Outside of the expressions described in the above two rules,
124 two dollar signs in a row give you one literal dollar sign.
125
126 Optional arguments:
127
128 - codec('utf_8'): a string containing the name of a valid Python
129 codec.
130
131 - encoding_errors('backslashreplace'): a string with a valid error handling
132 policy. See the codecs module documentation for details.
133
134 These are used to encode the format string if a call to str() fails on
135 the expanded result."""
136
137 if not isinstance(format,basestring):
138 raise TypeError, "needs string initializer"
139 self.format = format
140 self.codec = codec
141 self.encoding_errors = encoding_errors
142
143 namechars = "abcdefghijklmnopqrstuvwxyz" \
144 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
145 chunks = []
146 pos = 0
147
148 while 1:
149 dollar = string.find(format, "$", pos)
150 if dollar < 0: break
151 nextchar = format[dollar+1]
152
153 if nextchar == "{":
154 chunks.append((0, format[pos:dollar]))
155 pos, level = dollar+2, 1
156 while level:
157 match, pos = matchorfail(format, pos)
158 tstart, tend = match.regs[3]
159 token = format[tstart:tend]
160 if token == "{": level = level+1
161 elif token == "}": level = level-1
162 chunks.append((1, format[dollar+2:pos-1]))
163
164 elif nextchar in namechars:
165 chunks.append((0, format[pos:dollar]))
166 match, pos = matchorfail(format, dollar+1)
167 while pos < len(format):
168 if format[pos] == "." and \
169 pos+1 < len(format) and format[pos+1] in namechars:
170 match, pos = matchorfail(format, pos+1)
171 elif format[pos] in "([":
172 pos, level = pos+1, 1
173 while level:
174 match, pos = matchorfail(format, pos)
175 tstart, tend = match.regs[3]
176 token = format[tstart:tend]
177 if token[0] in "([": level = level+1
178 elif token[0] in ")]": level = level-1
179 else: break
180 chunks.append((1, format[dollar+1:pos]))
181
182 else:
183 chunks.append((0, format[pos:dollar+1]))
184 pos = dollar + 1 + (nextchar == "$")
185
186 if pos < len(format): chunks.append((0, format[pos:]))
187 self.chunks = chunks
188
189 def __repr__(self):
190 return "<Itpl %s >" % repr(self.format)
191
192 def _str(self,glob,loc):
193 """Evaluate to a string in the given globals/locals.
194
195 The final output is built by calling str(), but if this fails, the
196 result is encoded with the instance's codec and error handling policy,
197 via a call to out.encode(self.codec,self.encoding_errors)"""
198 result = []
199 app = result.append
200 for live, chunk in self.chunks:
201 if live: app(str(eval(chunk,glob,loc)))
202 else: app(chunk)
203 out = ''.join(result)
204 try:
205 return str(out)
206 except UnicodeError:
207 return out.encode(self.codec,self.encoding_errors)
208
209 def __str__(self):
210 """Evaluate and substitute the appropriate parts of the string."""
211
212 # We need to skip enough frames to get to the actual caller outside of
213 # Itpl.
214 frame = sys._getframe(1)
215 while frame.f_globals["__name__"] == __name__: frame = frame.f_back
216 loc, glob = frame.f_locals, frame.f_globals
217
218 return self._str(glob,loc)
219
220 class ItplNS(Itpl):
221 """Class representing a string with interpolation abilities.
222
223 This inherits from Itpl, but at creation time a namespace is provided
224 where the evaluation will occur. The interpolation becomes a bit more
225 efficient, as no traceback needs to be extracte. It also allows the
226 caller to supply a different namespace for the interpolation to occur than
227 its own."""
228
229 def __init__(self, format,globals,locals=None,
230 codec='utf_8',encoding_errors='backslashreplace'):
231 """ItplNS(format,globals[,locals]) -> interpolating string instance.
232
233 This constructor, besides a format string, takes a globals dictionary
234 and optionally a locals (which defaults to globals if not provided).
235
236 For further details, see the Itpl constructor."""
237
238 if locals is None:
239 locals = globals
240 self.globals = globals
241 self.locals = locals
242 Itpl.__init__(self,format,codec,encoding_errors)
243
244 def __str__(self):
245 """Evaluate and substitute the appropriate parts of the string."""
246 return self._str(self.globals,self.locals)
247
248 def __repr__(self):
249 return "<ItplNS %s >" % repr(self.format)
250
251 # utilities for fast printing
252 def itpl(text): return str(Itpl(text))
253 def printpl(text): print itpl(text)
254 # versions with namespace
255 def itplns(text,globals,locals=None): return str(ItplNS(text,globals,locals))
256 def printplns(text,globals,locals=None): print itplns(text,globals,locals)
257
258 class ItplFile:
259 """A file object that filters each write() through an interpolator."""
260 def __init__(self, file): self.file = file
261 def __repr__(self): return "<interpolated " + repr(self.file) + ">"
262 def __getattr__(self, attr): return getattr(self.file, attr)
263 def write(self, text): self.file.write(str(Itpl(text)))
264
265 def filter(file=sys.stdout):
266 """Return an ItplFile that filters writes to the given file object.
267
268 'file = filter(file)' replaces 'file' with a filtered object that
269 has a write() method. When called with no argument, this creates
270 a filter to sys.stdout."""
271 return ItplFile(file)
272
273 def unfilter(ifile=None):
274 """Return the original file that corresponds to the given ItplFile.
275
276 'file = unfilter(file)' undoes the effect of 'file = filter(file)'.
277 'sys.stdout = unfilter()' undoes the effect of 'sys.stdout = filter()'."""
278 return ifile and ifile.file or sys.stdout.file
This diff has been collapsed as it changes many lines, (700 lines changed) Show them Hide them
@@ -0,0 +1,700 b''
1 /***
2
3 MochiKit.Async 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005 Bob Ippolito. All rights Reserved.
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide("MochiKit.Async");
13 dojo.require("MochiKit.Base");
14 }
15 if (typeof(JSAN) != 'undefined') {
16 JSAN.use("MochiKit.Base", []);
17 }
18
19 try {
20 if (typeof(MochiKit.Base) == 'undefined') {
21 throw "";
22 }
23 } catch (e) {
24 throw "MochiKit.Async depends on MochiKit.Base!";
25 }
26
27 if (typeof(MochiKit.Async) == 'undefined') {
28 MochiKit.Async = {};
29 }
30
31 MochiKit.Async.NAME = "MochiKit.Async";
32 MochiKit.Async.VERSION = "1.4";
33 MochiKit.Async.__repr__ = function () {
34 return "[" + this.NAME + " " + this.VERSION + "]";
35 };
36 MochiKit.Async.toString = function () {
37 return this.__repr__();
38 };
39
40 /** @id MochiKit.Async.Deferred */
41 MochiKit.Async.Deferred = function (/* optional */ canceller) {
42 this.chain = [];
43 this.id = this._nextId();
44 this.fired = -1;
45 this.paused = 0;
46 this.results = [null, null];
47 this.canceller = canceller;
48 this.silentlyCancelled = false;
49 this.chained = false;
50 };
51
52 MochiKit.Async.Deferred.prototype = {
53 /** @id MochiKit.Async.Deferred.prototype.repr */
54 repr: function () {
55 var state;
56 if (this.fired == -1) {
57 state = 'unfired';
58 } else if (this.fired === 0) {
59 state = 'success';
60 } else {
61 state = 'error';
62 }
63 return 'Deferred(' + this.id + ', ' + state + ')';
64 },
65
66 toString: MochiKit.Base.forwardCall("repr"),
67
68 _nextId: MochiKit.Base.counter(),
69
70 /** @id MochiKit.Async.Deferred.prototype.cancel */
71 cancel: function () {
72 var self = MochiKit.Async;
73 if (this.fired == -1) {
74 if (this.canceller) {
75 this.canceller(this);
76 } else {
77 this.silentlyCancelled = true;
78 }
79 if (this.fired == -1) {
80 this.errback(new self.CancelledError(this));
81 }
82 } else if ((this.fired === 0) && (this.results[0] instanceof self.Deferred)) {
83 this.results[0].cancel();
84 }
85 },
86
87 _resback: function (res) {
88 /***
89
90 The primitive that means either callback or errback
91
92 ***/
93 this.fired = ((res instanceof Error) ? 1 : 0);
94 this.results[this.fired] = res;
95 this._fire();
96 },
97
98 _check: function () {
99 if (this.fired != -1) {
100 if (!this.silentlyCancelled) {
101 throw new MochiKit.Async.AlreadyCalledError(this);
102 }
103 this.silentlyCancelled = false;
104 return;
105 }
106 },
107
108 /** @id MochiKit.Async.Deferred.prototype.callback */
109 callback: function (res) {
110 this._check();
111 if (res instanceof MochiKit.Async.Deferred) {
112 throw new Error("Deferred instances can only be chained if they are the result of a callback");
113 }
114 this._resback(res);
115 },
116
117 /** @id MochiKit.Async.Deferred.prototype.errback */
118 errback: function (res) {
119 this._check();
120 var self = MochiKit.Async;
121 if (res instanceof self.Deferred) {
122 throw new Error("Deferred instances can only be chained if they are the result of a callback");
123 }
124 if (!(res instanceof Error)) {
125 res = new self.GenericError(res);
126 }
127 this._resback(res);
128 },
129
130 /** @id MochiKit.Async.Deferred.prototype.addBoth */
131 addBoth: function (fn) {
132 if (arguments.length > 1) {
133 fn = MochiKit.Base.partial.apply(null, arguments);
134 }
135 return this.addCallbacks(fn, fn);
136 },
137
138 /** @id MochiKit.Async.Deferred.prototype.addCallback */
139 addCallback: function (fn) {
140 if (arguments.length > 1) {
141 fn = MochiKit.Base.partial.apply(null, arguments);
142 }
143 return this.addCallbacks(fn, null);
144 },
145
146 /** @id MochiKit.Async.Deferred.prototype.addErrback */
147 addErrback: function (fn) {
148 if (arguments.length > 1) {
149 fn = MochiKit.Base.partial.apply(null, arguments);
150 }
151 return this.addCallbacks(null, fn);
152 },
153
154 /** @id MochiKit.Async.Deferred.prototype.addCallbacks */
155 addCallbacks: function (cb, eb) {
156 if (this.chained) {
157 throw new Error("Chained Deferreds can not be re-used");
158 }
159 this.chain.push([cb, eb]);
160 if (this.fired >= 0) {
161 this._fire();
162 }
163 return this;
164 },
165
166 _fire: function () {
167 /***
168
169 Used internally to exhaust the callback sequence when a result
170 is available.
171
172 ***/
173 var chain = this.chain;
174 var fired = this.fired;
175 var res = this.results[fired];
176 var self = this;
177 var cb = null;
178 while (chain.length > 0 && this.paused === 0) {
179 // Array
180 var pair = chain.shift();
181 var f = pair[fired];
182 if (f === null) {
183 continue;
184 }
185 try {
186 res = f(res);
187 fired = ((res instanceof Error) ? 1 : 0);
188 if (res instanceof MochiKit.Async.Deferred) {
189 cb = function (res) {
190 self._resback(res);
191 self.paused--;
192 if ((self.paused === 0) && (self.fired >= 0)) {
193 self._fire();
194 }
195 };
196 this.paused++;
197 }
198 } catch (err) {
199 fired = 1;
200 if (!(err instanceof Error)) {
201 err = new MochiKit.Async.GenericError(err);
202 }
203 res = err;
204 }
205 }
206 this.fired = fired;
207 this.results[fired] = res;
208 if (cb && this.paused) {
209 // this is for "tail recursion" in case the dependent deferred
210 // is already fired
211 res.addBoth(cb);
212 res.chained = true;
213 }
214 }
215 };
216
217 MochiKit.Base.update(MochiKit.Async, {
218 /** @id MochiKit.Async.evalJSONRequest */
219 evalJSONRequest: function (req) {
220 return MochiKit.Base.evalJSON(req.responseText);
221 },
222
223 /** @id MochiKit.Async.succeed */
224 succeed: function (/* optional */result) {
225 var d = new MochiKit.Async.Deferred();
226 d.callback.apply(d, arguments);
227 return d;
228 },
229
230 /** @id MochiKit.Async.fail */
231 fail: function (/* optional */result) {
232 var d = new MochiKit.Async.Deferred();
233 d.errback.apply(d, arguments);
234 return d;
235 },
236
237 /** @id MochiKit.Async.getXMLHttpRequest */
238 getXMLHttpRequest: function () {
239 var self = arguments.callee;
240 if (!self.XMLHttpRequest) {
241 var tryThese = [
242 function () { return new XMLHttpRequest(); },
243 function () { return new ActiveXObject('Msxml2.XMLHTTP'); },
244 function () { return new ActiveXObject('Microsoft.XMLHTTP'); },
245 function () { return new ActiveXObject('Msxml2.XMLHTTP.4.0'); },
246 function () {
247 throw new MochiKit.Async.BrowserComplianceError("Browser does not support XMLHttpRequest");
248 }
249 ];
250 for (var i = 0; i < tryThese.length; i++) {
251 var func = tryThese[i];
252 try {
253 self.XMLHttpRequest = func;
254 return func();
255 } catch (e) {
256 // pass
257 }
258 }
259 }
260 return self.XMLHttpRequest();
261 },
262
263 _xhr_onreadystatechange: function (d) {
264 // MochiKit.Logging.logDebug('this.readyState', this.readyState);
265 var m = MochiKit.Base;
266 if (this.readyState == 4) {
267 // IE SUCKS
268 try {
269 this.onreadystatechange = null;
270 } catch (e) {
271 try {
272 this.onreadystatechange = m.noop;
273 } catch (e) {
274 }
275 }
276 var status = null;
277 try {
278 status = this.status;
279 if (!status && m.isNotEmpty(this.responseText)) {
280 // 0 or undefined seems to mean cached or local
281 status = 304;
282 }
283 } catch (e) {
284 // pass
285 // MochiKit.Logging.logDebug('error getting status?', repr(items(e)));
286 }
287 // 200 is OK, 201 is CREATED, 204 is NO CONTENT
288 // 304 is NOT MODIFIED, 1223 is apparently a bug in IE
289 if (status == 200 || status == 201 || status == 204 ||
290 status == 304 || status == 1223) {
291 d.callback(this);
292 } else {
293 var err = new MochiKit.Async.XMLHttpRequestError(this, "Request failed");
294 if (err.number) {
295 // XXX: This seems to happen on page change
296 d.errback(err);
297 } else {
298 // XXX: this seems to happen when the server is unreachable
299 d.errback(err);
300 }
301 }
302 }
303 },
304
305 _xhr_canceller: function (req) {
306 // IE SUCKS
307 try {
308 req.onreadystatechange = null;
309 } catch (e) {
310 try {
311 req.onreadystatechange = MochiKit.Base.noop;
312 } catch (e) {
313 }
314 }
315 req.abort();
316 },
317
318
319 /** @id MochiKit.Async.sendXMLHttpRequest */
320 sendXMLHttpRequest: function (req, /* optional */ sendContent) {
321 if (typeof(sendContent) == "undefined" || sendContent === null) {
322 sendContent = "";
323 }
324
325 var m = MochiKit.Base;
326 var self = MochiKit.Async;
327 var d = new self.Deferred(m.partial(self._xhr_canceller, req));
328
329 try {
330 req.onreadystatechange = m.bind(self._xhr_onreadystatechange,
331 req, d);
332 req.send(sendContent);
333 } catch (e) {
334 try {
335 req.onreadystatechange = null;
336 } catch (ignore) {
337 // pass
338 }
339 d.errback(e);
340 }
341
342 return d;
343
344 },
345
346 /** @id MochiKit.Async.doXHR */
347 doXHR: function (url, opts) {
348 /*
349 Work around a Firefox bug by dealing with XHR during
350 the next event loop iteration. Maybe it's this one:
351 https://bugzilla.mozilla.org/show_bug.cgi?id=249843
352 */
353 var self = MochiKit.Async;
354 return self.callLater(0, self._doXHR, url, opts);
355 },
356
357 _doXHR: function (url, opts) {
358 var m = MochiKit.Base;
359 opts = m.update({
360 method: 'GET',
361 sendContent: ''
362 /*
363 queryString: undefined,
364 username: undefined,
365 password: undefined,
366 headers: undefined,
367 mimeType: undefined
368 */
369 }, opts);
370 var self = MochiKit.Async;
371 var req = self.getXMLHttpRequest();
372 if (opts.queryString) {
373 var qs = m.queryString(opts.queryString);
374 if (qs) {
375 url += "?" + qs;
376 }
377 }
378 // Safari will send undefined:undefined, so we have to check.
379 // We can't use apply, since the function is native.
380 if ('username' in opts) {
381 req.open(opts.method, url, true, opts.username, opts.password);
382 } else {
383 req.open(opts.method, url, true);
384 }
385 if (req.overrideMimeType && opts.mimeType) {
386 req.overrideMimeType(opts.mimeType);
387 }
388 req.setRequestHeader("X-Requested-With", "XMLHttpRequest");
389 if (opts.headers) {
390 var headers = opts.headers;
391 if (!m.isArrayLike(headers)) {
392 headers = m.items(headers);
393 }
394 for (var i = 0; i < headers.length; i++) {
395 var header = headers[i];
396 var name = header[0];
397 var value = header[1];
398 req.setRequestHeader(name, value);
399 }
400 }
401 return self.sendXMLHttpRequest(req, opts.sendContent);
402 },
403
404 _buildURL: function (url/*, ...*/) {
405 if (arguments.length > 1) {
406 var m = MochiKit.Base;
407 var qs = m.queryString.apply(null, m.extend(null, arguments, 1));
408 if (qs) {
409 return url + "?" + qs;
410 }
411 }
412 return url;
413 },
414
415 /** @id MochiKit.Async.doSimpleXMLHttpRequest */
416 doSimpleXMLHttpRequest: function (url/*, ...*/) {
417 var self = MochiKit.Async;
418 url = self._buildURL.apply(self, arguments);
419 return self.doXHR(url);
420 },
421
422 /** @id MochiKit.Async.loadJSONDoc */
423 loadJSONDoc: function (url/*, ...*/) {
424 var self = MochiKit.Async;
425 url = self._buildURL.apply(self, arguments);
426 var d = self.doXHR(url, {
427 'mimeType': 'text/plain',
428 'headers': [['Accept', 'application/json']]
429 });
430 d = d.addCallback(self.evalJSONRequest);
431 return d;
432 },
433
434 /** @id MochiKit.Async.wait */
435 wait: function (seconds, /* optional */value) {
436 var d = new MochiKit.Async.Deferred();
437 var m = MochiKit.Base;
438 if (typeof(value) != 'undefined') {
439 d.addCallback(function () { return value; });
440 }
441 var timeout = setTimeout(
442 m.bind("callback", d),
443 Math.floor(seconds * 1000));
444 d.canceller = function () {
445 try {
446 clearTimeout(timeout);
447 } catch (e) {
448 // pass
449 }
450 };
451 return d;
452 },
453
454 /** @id MochiKit.Async.callLater */
455 callLater: function (seconds, func) {
456 var m = MochiKit.Base;
457 var pfunc = m.partial.apply(m, m.extend(null, arguments, 1));
458 return MochiKit.Async.wait(seconds).addCallback(
459 function (res) { return pfunc(); }
460 );
461 }
462 });
463
464
465 /** @id MochiKit.Async.DeferredLock */
466 MochiKit.Async.DeferredLock = function () {
467 this.waiting = [];
468 this.locked = false;
469 this.id = this._nextId();
470 };
471
472 MochiKit.Async.DeferredLock.prototype = {
473 __class__: MochiKit.Async.DeferredLock,
474 /** @id MochiKit.Async.DeferredLock.prototype.acquire */
475 acquire: function () {
476 var d = new MochiKit.Async.Deferred();
477 if (this.locked) {
478 this.waiting.push(d);
479 } else {
480 this.locked = true;
481 d.callback(this);
482 }
483 return d;
484 },
485 /** @id MochiKit.Async.DeferredLock.prototype.release */
486 release: function () {
487 if (!this.locked) {
488 throw TypeError("Tried to release an unlocked DeferredLock");
489 }
490 this.locked = false;
491 if (this.waiting.length > 0) {
492 this.locked = true;
493 this.waiting.shift().callback(this);
494 }
495 },
496 _nextId: MochiKit.Base.counter(),
497 repr: function () {
498 var state;
499 if (this.locked) {
500 state = 'locked, ' + this.waiting.length + ' waiting';
501 } else {
502 state = 'unlocked';
503 }
504 return 'DeferredLock(' + this.id + ', ' + state + ')';
505 },
506 toString: MochiKit.Base.forwardCall("repr")
507
508 };
509
510 /** @id MochiKit.Async.DeferredList */
511 MochiKit.Async.DeferredList = function (list, /* optional */fireOnOneCallback, fireOnOneErrback, consumeErrors, canceller) {
512
513 // call parent constructor
514 MochiKit.Async.Deferred.apply(this, [canceller]);
515
516 this.list = list;
517 var resultList = [];
518 this.resultList = resultList;
519
520 this.finishedCount = 0;
521 this.fireOnOneCallback = fireOnOneCallback;
522 this.fireOnOneErrback = fireOnOneErrback;
523 this.consumeErrors = consumeErrors;
524
525 var cb = MochiKit.Base.bind(this._cbDeferred, this);
526 for (var i = 0; i < list.length; i++) {
527 var d = list[i];
528 resultList.push(undefined);
529 d.addCallback(cb, i, true);
530 d.addErrback(cb, i, false);
531 }
532
533 if (list.length === 0 && !fireOnOneCallback) {
534 this.callback(this.resultList);
535 }
536
537 };
538
539 MochiKit.Async.DeferredList.prototype = new MochiKit.Async.Deferred();
540
541 MochiKit.Async.DeferredList.prototype._cbDeferred = function (index, succeeded, result) {
542 this.resultList[index] = [succeeded, result];
543 this.finishedCount += 1;
544 if (this.fired == -1) {
545 if (succeeded && this.fireOnOneCallback) {
546 this.callback([index, result]);
547 } else if (!succeeded && this.fireOnOneErrback) {
548 this.errback(result);
549 } else if (this.finishedCount == this.list.length) {
550 this.callback(this.resultList);
551 }
552 }
553 if (!succeeded && this.consumeErrors) {
554 result = null;
555 }
556 return result;
557 };
558
559 /** @id MochiKit.Async.gatherResults */
560 MochiKit.Async.gatherResults = function (deferredList) {
561 var d = new MochiKit.Async.DeferredList(deferredList, false, true, false);
562 d.addCallback(function (results) {
563 var ret = [];
564 for (var i = 0; i < results.length; i++) {
565 ret.push(results[i][1]);
566 }
567 return ret;
568 });
569 return d;
570 };
571
572 /** @id MochiKit.Async.maybeDeferred */
573 MochiKit.Async.maybeDeferred = function (func) {
574 var self = MochiKit.Async;
575 var result;
576 try {
577 var r = func.apply(null, MochiKit.Base.extend([], arguments, 1));
578 if (r instanceof self.Deferred) {
579 result = r;
580 } else if (r instanceof Error) {
581 result = self.fail(r);
582 } else {
583 result = self.succeed(r);
584 }
585 } catch (e) {
586 result = self.fail(e);
587 }
588 return result;
589 };
590
591
592 MochiKit.Async.EXPORT = [
593 "AlreadyCalledError",
594 "CancelledError",
595 "BrowserComplianceError",
596 "GenericError",
597 "XMLHttpRequestError",
598 "Deferred",
599 "succeed",
600 "fail",
601 "getXMLHttpRequest",
602 "doSimpleXMLHttpRequest",
603 "loadJSONDoc",
604 "wait",
605 "callLater",
606 "sendXMLHttpRequest",
607 "DeferredLock",
608 "DeferredList",
609 "gatherResults",
610 "maybeDeferred",
611 "doXHR"
612 ];
613
614 MochiKit.Async.EXPORT_OK = [
615 "evalJSONRequest"
616 ];
617
618 MochiKit.Async.__new__ = function () {
619 var m = MochiKit.Base;
620 var ne = m.partial(m._newNamedError, this);
621
622 ne("AlreadyCalledError",
623 /** @id MochiKit.Async.AlreadyCalledError */
624 function (deferred) {
625 /***
626
627 Raised by the Deferred if callback or errback happens
628 after it was already fired.
629
630 ***/
631 this.deferred = deferred;
632 }
633 );
634
635 ne("CancelledError",
636 /** @id MochiKit.Async.CancelledError */
637 function (deferred) {
638 /***
639
640 Raised by the Deferred cancellation mechanism.
641
642 ***/
643 this.deferred = deferred;
644 }
645 );
646
647 ne("BrowserComplianceError",
648 /** @id MochiKit.Async.BrowserComplianceError */
649 function (msg) {
650 /***
651
652 Raised when the JavaScript runtime is not capable of performing
653 the given function. Technically, this should really never be
654 raised because a non-conforming JavaScript runtime probably
655 isn't going to support exceptions in the first place.
656
657 ***/
658 this.message = msg;
659 }
660 );
661
662 ne("GenericError",
663 /** @id MochiKit.Async.GenericError */
664 function (msg) {
665 this.message = msg;
666 }
667 );
668
669 ne("XMLHttpRequestError",
670 /** @id MochiKit.Async.XMLHttpRequestError */
671 function (req, msg) {
672 /***
673
674 Raised when an XMLHttpRequest does not complete for any reason.
675
676 ***/
677 this.req = req;
678 this.message = msg;
679 try {
680 // Strange but true that this can raise in some cases.
681 this.number = req.status;
682 } catch (e) {
683 // pass
684 }
685 }
686 );
687
688
689 this.EXPORT_TAGS = {
690 ":common": this.EXPORT,
691 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
692 };
693
694 m.nameFunctions(this);
695
696 };
697
698 MochiKit.Async.__new__();
699
700 MochiKit.Base._exportSymbols(this, MochiKit.Async);
This diff has been collapsed as it changes many lines, (1413 lines changed) Show them Hide them
@@ -0,0 +1,1413 b''
1 /***
2
3 MochiKit.Base 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005 Bob Ippolito. All rights Reserved.
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide("MochiKit.Base");
13 }
14 if (typeof(MochiKit) == 'undefined') {
15 MochiKit = {};
16 }
17 if (typeof(MochiKit.Base) == 'undefined') {
18 MochiKit.Base = {};
19 }
20 if (typeof(MochiKit.__export__) == "undefined") {
21 MochiKit.__export__ = (MochiKit.__compat__ ||
22 (typeof(JSAN) == 'undefined' && typeof(dojo) == 'undefined')
23 );
24 }
25
26 MochiKit.Base.VERSION = "1.4";
27 MochiKit.Base.NAME = "MochiKit.Base";
28 /** @id MochiKit.Base.update */
29 MochiKit.Base.update = function (self, obj/*, ... */) {
30 if (self === null || self === undefined) {
31 self = {};
32 }
33 for (var i = 1; i < arguments.length; i++) {
34 var o = arguments[i];
35 if (typeof(o) != 'undefined' && o !== null) {
36 for (var k in o) {
37 self[k] = o[k];
38 }
39 }
40 }
41 return self;
42 };
43
44 MochiKit.Base.update(MochiKit.Base, {
45 __repr__: function () {
46 return "[" + this.NAME + " " + this.VERSION + "]";
47 },
48
49 toString: function () {
50 return this.__repr__();
51 },
52
53 /** @id MochiKit.Base.camelize */
54 camelize: function (selector) {
55 /* from dojo.style.toCamelCase */
56 var arr = selector.split('-');
57 var cc = arr[0];
58 for (var i = 1; i < arr.length; i++) {
59 cc += arr[i].charAt(0).toUpperCase() + arr[i].substring(1);
60 }
61 return cc;
62 },
63
64 /** @id MochiKit.Base.counter */
65 counter: function (n/* = 1 */) {
66 if (arguments.length === 0) {
67 n = 1;
68 }
69 return function () {
70 return n++;
71 };
72 },
73
74 /** @id MochiKit.Base.clone */
75 clone: function (obj) {
76 var me = arguments.callee;
77 if (arguments.length == 1) {
78 me.prototype = obj;
79 return new me();
80 }
81 },
82
83 _flattenArray: function (res, lst) {
84 for (var i = 0; i < lst.length; i++) {
85 var o = lst[i];
86 if (o instanceof Array) {
87 arguments.callee(res, o);
88 } else {
89 res.push(o);
90 }
91 }
92 return res;
93 },
94
95 /** @id MochiKit.Base.flattenArray */
96 flattenArray: function (lst) {
97 return MochiKit.Base._flattenArray([], lst);
98 },
99
100 /** @id MochiKit.Base.flattenArguments */
101 flattenArguments: function (lst/* ...*/) {
102 var res = [];
103 var m = MochiKit.Base;
104 var args = m.extend(null, arguments);
105 while (args.length) {
106 var o = args.shift();
107 if (o && typeof(o) == "object" && typeof(o.length) == "number") {
108 for (var i = o.length - 1; i >= 0; i--) {
109 args.unshift(o[i]);
110 }
111 } else {
112 res.push(o);
113 }
114 }
115 return res;
116 },
117
118 /** @id MochiKit.Base.extend */
119 extend: function (self, obj, /* optional */skip) {
120 // Extend an array with an array-like object starting
121 // from the skip index
122 if (!skip) {
123 skip = 0;
124 }
125 if (obj) {
126 // allow iterable fall-through, but skip the full isArrayLike
127 // check for speed, this is called often.
128 var l = obj.length;
129 if (typeof(l) != 'number' /* !isArrayLike(obj) */) {
130 if (typeof(MochiKit.Iter) != "undefined") {
131 obj = MochiKit.Iter.list(obj);
132 l = obj.length;
133 } else {
134 throw new TypeError("Argument not an array-like and MochiKit.Iter not present");
135 }
136 }
137 if (!self) {
138 self = [];
139 }
140 for (var i = skip; i < l; i++) {
141 self.push(obj[i]);
142 }
143 }
144 // This mutates, but it's convenient to return because
145 // it's often used like a constructor when turning some
146 // ghetto array-like to a real array
147 return self;
148 },
149
150
151 /** @id MochiKit.Base.updatetree */
152 updatetree: function (self, obj/*, ...*/) {
153 if (self === null || self === undefined) {
154 self = {};
155 }
156 for (var i = 1; i < arguments.length; i++) {
157 var o = arguments[i];
158 if (typeof(o) != 'undefined' && o !== null) {
159 for (var k in o) {
160 var v = o[k];
161 if (typeof(self[k]) == 'object' && typeof(v) == 'object') {
162 arguments.callee(self[k], v);
163 } else {
164 self[k] = v;
165 }
166 }
167 }
168 }
169 return self;
170 },
171
172 /** @id MochiKit.Base.setdefault */
173 setdefault: function (self, obj/*, ...*/) {
174 if (self === null || self === undefined) {
175 self = {};
176 }
177 for (var i = 1; i < arguments.length; i++) {
178 var o = arguments[i];
179 for (var k in o) {
180 if (!(k in self)) {
181 self[k] = o[k];
182 }
183 }
184 }
185 return self;
186 },
187
188 /** @id MochiKit.Base.keys */
189 keys: function (obj) {
190 var rval = [];
191 for (var prop in obj) {
192 rval.push(prop);
193 }
194 return rval;
195 },
196
197 /** @id MochiKit.Base.values */
198 values: function (obj) {
199 var rval = [];
200 for (var prop in obj) {
201 rval.push(obj[prop]);
202 }
203 return rval;
204 },
205
206 /** @id MochiKit.Base.items */
207 items: function (obj) {
208 var rval = [];
209 var e;
210 for (var prop in obj) {
211 var v;
212 try {
213 v = obj[prop];
214 } catch (e) {
215 continue;
216 }
217 rval.push([prop, v]);
218 }
219 return rval;
220 },
221
222
223 _newNamedError: function (module, name, func) {
224 func.prototype = new MochiKit.Base.NamedError(module.NAME + "." + name);
225 module[name] = func;
226 },
227
228
229 /** @id MochiKit.Base.operator */
230 operator: {
231 // unary logic operators
232 /** @id MochiKit.Base.truth */
233 truth: function (a) { return !!a; },
234 /** @id MochiKit.Base.lognot */
235 lognot: function (a) { return !a; },
236 /** @id MochiKit.Base.identity */
237 identity: function (a) { return a; },
238
239 // bitwise unary operators
240 /** @id MochiKit.Base.not */
241 not: function (a) { return ~a; },
242 /** @id MochiKit.Base.neg */
243 neg: function (a) { return -a; },
244
245 // binary operators
246 /** @id MochiKit.Base.add */
247 add: function (a, b) { return a + b; },
248 /** @id MochiKit.Base.sub */
249 sub: function (a, b) { return a - b; },
250 /** @id MochiKit.Base.div */
251 div: function (a, b) { return a / b; },
252 /** @id MochiKit.Base.mod */
253 mod: function (a, b) { return a % b; },
254 /** @id MochiKit.Base.mul */
255 mul: function (a, b) { return a * b; },
256
257 // bitwise binary operators
258 /** @id MochiKit.Base.and */
259 and: function (a, b) { return a & b; },
260 /** @id MochiKit.Base.or */
261 or: function (a, b) { return a | b; },
262 /** @id MochiKit.Base.xor */
263 xor: function (a, b) { return a ^ b; },
264 /** @id MochiKit.Base.lshift */
265 lshift: function (a, b) { return a << b; },
266 /** @id MochiKit.Base.rshift */
267 rshift: function (a, b) { return a >> b; },
268 /** @id MochiKit.Base.zrshift */
269 zrshift: function (a, b) { return a >>> b; },
270
271 // near-worthless built-in comparators
272 /** @id MochiKit.Base.eq */
273 eq: function (a, b) { return a == b; },
274 /** @id MochiKit.Base.ne */
275 ne: function (a, b) { return a != b; },
276 /** @id MochiKit.Base.gt */
277 gt: function (a, b) { return a > b; },
278 /** @id MochiKit.Base.ge */
279 ge: function (a, b) { return a >= b; },
280 /** @id MochiKit.Base.lt */
281 lt: function (a, b) { return a < b; },
282 /** @id MochiKit.Base.le */
283 le: function (a, b) { return a <= b; },
284
285 // strict built-in comparators
286 seq: function (a, b) { return a === b; },
287 sne: function (a, b) { return a !== b; },
288
289 // compare comparators
290 /** @id MochiKit.Base.ceq */
291 ceq: function (a, b) { return MochiKit.Base.compare(a, b) === 0; },
292 /** @id MochiKit.Base.cne */
293 cne: function (a, b) { return MochiKit.Base.compare(a, b) !== 0; },
294 /** @id MochiKit.Base.cgt */
295 cgt: function (a, b) { return MochiKit.Base.compare(a, b) == 1; },
296 /** @id MochiKit.Base.cge */
297 cge: function (a, b) { return MochiKit.Base.compare(a, b) != -1; },
298 /** @id MochiKit.Base.clt */
299 clt: function (a, b) { return MochiKit.Base.compare(a, b) == -1; },
300 /** @id MochiKit.Base.cle */
301 cle: function (a, b) { return MochiKit.Base.compare(a, b) != 1; },
302
303 // binary logical operators
304 /** @id MochiKit.Base.logand */
305 logand: function (a, b) { return a && b; },
306 /** @id MochiKit.Base.logor */
307 logor: function (a, b) { return a || b; },
308 /** @id MochiKit.Base.contains */
309 contains: function (a, b) { return b in a; }
310 },
311
312 /** @id MochiKit.Base.forwardCall */
313 forwardCall: function (func) {
314 return function () {
315 return this[func].apply(this, arguments);
316 };
317 },
318
319 /** @id MochiKit.Base.itemgetter */
320 itemgetter: function (func) {
321 return function (arg) {
322 return arg[func];
323 };
324 },
325
326 /** @id MochiKit.Base.typeMatcher */
327 typeMatcher: function (/* typ */) {
328 var types = {};
329 for (var i = 0; i < arguments.length; i++) {
330 var typ = arguments[i];
331 types[typ] = typ;
332 }
333 return function () {
334 for (var i = 0; i < arguments.length; i++) {
335 if (!(typeof(arguments[i]) in types)) {
336 return false;
337 }
338 }
339 return true;
340 };
341 },
342
343 /** @id MochiKit.Base.isNull */
344 isNull: function (/* ... */) {
345 for (var i = 0; i < arguments.length; i++) {
346 if (arguments[i] !== null) {
347 return false;
348 }
349 }
350 return true;
351 },
352
353 /** @id MochiKit.Base.isUndefinedOrNull */
354 isUndefinedOrNull: function (/* ... */) {
355 for (var i = 0; i < arguments.length; i++) {
356 var o = arguments[i];
357 if (!(typeof(o) == 'undefined' || o === null)) {
358 return false;
359 }
360 }
361 return true;
362 },
363
364 /** @id MochiKit.Base.isEmpty */
365 isEmpty: function (obj) {
366 return !MochiKit.Base.isNotEmpty.apply(this, arguments);
367 },
368
369 /** @id MochiKit.Base.isNotEmpty */
370 isNotEmpty: function (obj) {
371 for (var i = 0; i < arguments.length; i++) {
372 var o = arguments[i];
373 if (!(o && o.length)) {
374 return false;
375 }
376 }
377 return true;
378 },
379
380 /** @id MochiKit.Base.isArrayLike */
381 isArrayLike: function () {
382 for (var i = 0; i < arguments.length; i++) {
383 var o = arguments[i];
384 var typ = typeof(o);
385 if (
386 (typ != 'object' && !(typ == 'function' && typeof(o.item) == 'function')) ||
387 o === null ||
388 typeof(o.length) != 'number' ||
389 o.nodeType === 3
390 ) {
391 return false;
392 }
393 }
394 return true;
395 },
396
397 /** @id MochiKit.Base.isDateLike */
398 isDateLike: function () {
399 for (var i = 0; i < arguments.length; i++) {
400 var o = arguments[i];
401 if (typeof(o) != "object" || o === null
402 || typeof(o.getTime) != 'function') {
403 return false;
404 }
405 }
406 return true;
407 },
408
409
410 /** @id MochiKit.Base.xmap */
411 xmap: function (fn/*, obj... */) {
412 if (fn === null) {
413 return MochiKit.Base.extend(null, arguments, 1);
414 }
415 var rval = [];
416 for (var i = 1; i < arguments.length; i++) {
417 rval.push(fn(arguments[i]));
418 }
419 return rval;
420 },
421
422 /** @id MochiKit.Base.map */
423 map: function (fn, lst/*, lst... */) {
424 var m = MochiKit.Base;
425 var itr = MochiKit.Iter;
426 var isArrayLike = m.isArrayLike;
427 if (arguments.length <= 2) {
428 // allow an iterable to be passed
429 if (!isArrayLike(lst)) {
430 if (itr) {
431 // fast path for map(null, iterable)
432 lst = itr.list(lst);
433 if (fn === null) {
434 return lst;
435 }
436 } else {
437 throw new TypeError("Argument not an array-like and MochiKit.Iter not present");
438 }
439 }
440 // fast path for map(null, lst)
441 if (fn === null) {
442 return m.extend(null, lst);
443 }
444 // disabled fast path for map(fn, lst)
445 /*
446 if (false && typeof(Array.prototype.map) == 'function') {
447 // Mozilla fast-path
448 return Array.prototype.map.call(lst, fn);
449 }
450 */
451 var rval = [];
452 for (var i = 0; i < lst.length; i++) {
453 rval.push(fn(lst[i]));
454 }
455 return rval;
456 } else {
457 // default for map(null, ...) is zip(...)
458 if (fn === null) {
459 fn = Array;
460 }
461 var length = null;
462 for (i = 1; i < arguments.length; i++) {
463 // allow iterables to be passed
464 if (!isArrayLike(arguments[i])) {
465 if (itr) {
466 return itr.list(itr.imap.apply(null, arguments));
467 } else {
468 throw new TypeError("Argument not an array-like and MochiKit.Iter not present");
469 }
470 }
471 // find the minimum length
472 var l = arguments[i].length;
473 if (length === null || length > l) {
474 length = l;
475 }
476 }
477 rval = [];
478 for (i = 0; i < length; i++) {
479 var args = [];
480 for (var j = 1; j < arguments.length; j++) {
481 args.push(arguments[j][i]);
482 }
483 rval.push(fn.apply(this, args));
484 }
485 return rval;
486 }
487 },
488
489 /** @id MochiKit.Base.xfilter */
490 xfilter: function (fn/*, obj... */) {
491 var rval = [];
492 if (fn === null) {
493 fn = MochiKit.Base.operator.truth;
494 }
495 for (var i = 1; i < arguments.length; i++) {
496 var o = arguments[i];
497 if (fn(o)) {
498 rval.push(o);
499 }
500 }
501 return rval;
502 },
503
504 /** @id MochiKit.Base.filter */
505 filter: function (fn, lst, self) {
506 var rval = [];
507 // allow an iterable to be passed
508 var m = MochiKit.Base;
509 if (!m.isArrayLike(lst)) {
510 if (MochiKit.Iter) {
511 lst = MochiKit.Iter.list(lst);
512 } else {
513 throw new TypeError("Argument not an array-like and MochiKit.Iter not present");
514 }
515 }
516 if (fn === null) {
517 fn = m.operator.truth;
518 }
519 if (typeof(Array.prototype.filter) == 'function') {
520 // Mozilla fast-path
521 return Array.prototype.filter.call(lst, fn, self);
522 } else if (typeof(self) == 'undefined' || self === null) {
523 for (var i = 0; i < lst.length; i++) {
524 var o = lst[i];
525 if (fn(o)) {
526 rval.push(o);
527 }
528 }
529 } else {
530 for (i = 0; i < lst.length; i++) {
531 o = lst[i];
532 if (fn.call(self, o)) {
533 rval.push(o);
534 }
535 }
536 }
537 return rval;
538 },
539
540
541 _wrapDumbFunction: function (func) {
542 return function () {
543 // fast path!
544 switch (arguments.length) {
545 case 0: return func();
546 case 1: return func(arguments[0]);
547 case 2: return func(arguments[0], arguments[1]);
548 case 3: return func(arguments[0], arguments[1], arguments[2]);
549 }
550 var args = [];
551 for (var i = 0; i < arguments.length; i++) {
552 args.push("arguments[" + i + "]");
553 }
554 return eval("(func(" + args.join(",") + "))");
555 };
556 },
557
558 /** @id MochiKit.Base.methodcaller */
559 methodcaller: function (func/*, args... */) {
560 var args = MochiKit.Base.extend(null, arguments, 1);
561 if (typeof(func) == "function") {
562 return function (obj) {
563 return func.apply(obj, args);
564 };
565 } else {
566 return function (obj) {
567 return obj[func].apply(obj, args);
568 };
569 }
570 },
571
572 /** @id MochiKit.Base.method */
573 method: function (self, func) {
574 var m = MochiKit.Base;
575 return m.bind.apply(this, m.extend([func, self], arguments, 2));
576 },
577
578 /** @id MochiKit.Base.compose */
579 compose: function (f1, f2/*, f3, ... fN */) {
580 var fnlist = [];
581 var m = MochiKit.Base;
582 if (arguments.length === 0) {
583 throw new TypeError("compose() requires at least one argument");
584 }
585 for (var i = 0; i < arguments.length; i++) {
586 var fn = arguments[i];
587 if (typeof(fn) != "function") {
588 throw new TypeError(m.repr(fn) + " is not a function");
589 }
590 fnlist.push(fn);
591 }
592 return function () {
593 var args = arguments;
594 for (var i = fnlist.length - 1; i >= 0; i--) {
595 args = [fnlist[i].apply(this, args)];
596 }
597 return args[0];
598 };
599 },
600
601 /** @id MochiKit.Base.bind */
602 bind: function (func, self/* args... */) {
603 if (typeof(func) == "string") {
604 func = self[func];
605 }
606 var im_func = func.im_func;
607 var im_preargs = func.im_preargs;
608 var im_self = func.im_self;
609 var m = MochiKit.Base;
610 if (typeof(func) == "function" && typeof(func.apply) == "undefined") {
611 // this is for cases where JavaScript sucks ass and gives you a
612 // really dumb built-in function like alert() that doesn't have
613 // an apply
614 func = m._wrapDumbFunction(func);
615 }
616 if (typeof(im_func) != 'function') {
617 im_func = func;
618 }
619 if (typeof(self) != 'undefined') {
620 im_self = self;
621 }
622 if (typeof(im_preargs) == 'undefined') {
623 im_preargs = [];
624 } else {
625 im_preargs = im_preargs.slice();
626 }
627 m.extend(im_preargs, arguments, 2);
628 var newfunc = function () {
629 var args = arguments;
630 var me = arguments.callee;
631 if (me.im_preargs.length > 0) {
632 args = m.concat(me.im_preargs, args);
633 }
634 var self = me.im_self;
635 if (!self) {
636 self = this;
637 }
638 return me.im_func.apply(self, args);
639 };
640 newfunc.im_self = im_self;
641 newfunc.im_func = im_func;
642 newfunc.im_preargs = im_preargs;
643 return newfunc;
644 },
645
646 /** @id MochiKit.Base.bindMethods */
647 bindMethods: function (self) {
648 var bind = MochiKit.Base.bind;
649 for (var k in self) {
650 var func = self[k];
651 if (typeof(func) == 'function') {
652 self[k] = bind(func, self);
653 }
654 }
655 },
656
657 /** @id MochiKit.Base.registerComparator */
658 registerComparator: function (name, check, comparator, /* optional */ override) {
659 MochiKit.Base.comparatorRegistry.register(name, check, comparator, override);
660 },
661
662 _primitives: {'boolean': true, 'string': true, 'number': true},
663
664 /** @id MochiKit.Base.compare */
665 compare: function (a, b) {
666 if (a == b) {
667 return 0;
668 }
669 var aIsNull = (typeof(a) == 'undefined' || a === null);
670 var bIsNull = (typeof(b) == 'undefined' || b === null);
671 if (aIsNull && bIsNull) {
672 return 0;
673 } else if (aIsNull) {
674 return -1;
675 } else if (bIsNull) {
676 return 1;
677 }
678 var m = MochiKit.Base;
679 // bool, number, string have meaningful comparisons
680 var prim = m._primitives;
681 if (!(typeof(a) in prim && typeof(b) in prim)) {
682 try {
683 return m.comparatorRegistry.match(a, b);
684 } catch (e) {
685 if (e != m.NotFound) {
686 throw e;
687 }
688 }
689 }
690 if (a < b) {
691 return -1;
692 } else if (a > b) {
693 return 1;
694 }
695 // These types can't be compared
696 var repr = m.repr;
697 throw new TypeError(repr(a) + " and " + repr(b) + " can not be compared");
698 },
699
700 /** @id MochiKit.Base.compareDateLike */
701 compareDateLike: function (a, b) {
702 return MochiKit.Base.compare(a.getTime(), b.getTime());
703 },
704
705 /** @id MochiKit.Base.compareArrayLike */
706 compareArrayLike: function (a, b) {
707 var compare = MochiKit.Base.compare;
708 var count = a.length;
709 var rval = 0;
710 if (count > b.length) {
711 rval = 1;
712 count = b.length;
713 } else if (count < b.length) {
714 rval = -1;
715 }
716 for (var i = 0; i < count; i++) {
717 var cmp = compare(a[i], b[i]);
718 if (cmp) {
719 return cmp;
720 }
721 }
722 return rval;
723 },
724
725 /** @id MochiKit.Base.registerRepr */
726 registerRepr: function (name, check, wrap, /* optional */override) {
727 MochiKit.Base.reprRegistry.register(name, check, wrap, override);
728 },
729
730 /** @id MochiKit.Base.repr */
731 repr: function (o) {
732 if (typeof(o) == "undefined") {
733 return "undefined";
734 } else if (o === null) {
735 return "null";
736 }
737 try {
738 if (typeof(o.__repr__) == 'function') {
739 return o.__repr__();
740 } else if (typeof(o.repr) == 'function' && o.repr != arguments.callee) {
741 return o.repr();
742 }
743 return MochiKit.Base.reprRegistry.match(o);
744 } catch (e) {
745 if (typeof(o.NAME) == 'string' && (
746 o.toString == Function.prototype.toString ||
747 o.toString == Object.prototype.toString
748 )) {
749 return o.NAME;
750 }
751 }
752 try {
753 var ostring = (o + "");
754 } catch (e) {
755 return "[" + typeof(o) + "]";
756 }
757 if (typeof(o) == "function") {
758 ostring = ostring.replace(/^\s+/, "").replace(/\s+/g, " ");
759 var idx = ostring.indexOf("{");
760 if (idx != -1) {
761 ostring = ostring.substr(0, idx) + "{...}";
762 }
763 }
764 return ostring;
765 },
766
767 /** @id MochiKit.Base.reprArrayLike */
768 reprArrayLike: function (o) {
769 var m = MochiKit.Base;
770 return "[" + m.map(m.repr, o).join(", ") + "]";
771 },
772
773 /** @id MochiKit.Base.reprString */
774 reprString: function (o) {
775 return ('"' + o.replace(/(["\\])/g, '\\$1') + '"'
776 ).replace(/[\f]/g, "\\f"
777 ).replace(/[\b]/g, "\\b"
778 ).replace(/[\n]/g, "\\n"
779 ).replace(/[\t]/g, "\\t"
780 ).replace(/[\r]/g, "\\r");
781 },
782
783 /** @id MochiKit.Base.reprNumber */
784 reprNumber: function (o) {
785 return o + "";
786 },
787
788 /** @id MochiKit.Base.registerJSON */
789 registerJSON: function (name, check, wrap, /* optional */override) {
790 MochiKit.Base.jsonRegistry.register(name, check, wrap, override);
791 },
792
793
794 /** @id MochiKit.Base.evalJSON */
795 evalJSON: function () {
796 return eval("(" + MochiKit.Base._filterJSON(arguments[0]) + ")");
797 },
798
799 _filterJSON: function (s) {
800 var m = s.match(/^\s*\/\*(.*)\*\/\s*$/);
801 if (m) {
802 return m[1];
803 }
804 return s;
805 },
806
807 /** @id MochiKit.Base.serializeJSON */
808 serializeJSON: function (o) {
809 var objtype = typeof(o);
810 if (objtype == "number" || objtype == "boolean") {
811 return o + "";
812 } else if (o === null) {
813 return "null";
814 }
815 var m = MochiKit.Base;
816 var reprString = m.reprString;
817 if (objtype == "string") {
818 return reprString(o);
819 }
820 // recurse
821 var me = arguments.callee;
822 // short-circuit for objects that support "json" serialization
823 // if they return "self" then just pass-through...
824 var newObj;
825 if (typeof(o.__json__) == "function") {
826 newObj = o.__json__();
827 if (o !== newObj) {
828 return me(newObj);
829 }
830 }
831 if (typeof(o.json) == "function") {
832 newObj = o.json();
833 if (o !== newObj) {
834 return me(newObj);
835 }
836 }
837 // array
838 if (objtype != "function" && typeof(o.length) == "number") {
839 var res = [];
840 for (var i = 0; i < o.length; i++) {
841 var val = me(o[i]);
842 if (typeof(val) != "string") {
843 val = "undefined";
844 }
845 res.push(val);
846 }
847 return "[" + res.join(", ") + "]";
848 }
849 // look in the registry
850 try {
851 newObj = m.jsonRegistry.match(o);
852 if (o !== newObj) {
853 return me(newObj);
854 }
855 } catch (e) {
856 if (e != m.NotFound) {
857 // something really bad happened
858 throw e;
859 }
860 }
861 // undefined is outside of the spec
862 if (objtype == "undefined") {
863 throw new TypeError("undefined can not be serialized as JSON");
864 }
865 // it's a function with no adapter, bad
866 if (objtype == "function") {
867 return null;
868 }
869 // generic object code path
870 res = [];
871 for (var k in o) {
872 var useKey;
873 if (typeof(k) == "number") {
874 useKey = '"' + k + '"';
875 } else if (typeof(k) == "string") {
876 useKey = reprString(k);
877 } else {
878 // skip non-string or number keys
879 continue;
880 }
881 val = me(o[k]);
882 if (typeof(val) != "string") {
883 // skip non-serializable values
884 continue;
885 }
886 res.push(useKey + ":" + val);
887 }
888 return "{" + res.join(", ") + "}";
889 },
890
891
892 /** @id MochiKit.Base.objEqual */
893 objEqual: function (a, b) {
894 return (MochiKit.Base.compare(a, b) === 0);
895 },
896
897 /** @id MochiKit.Base.arrayEqual */
898 arrayEqual: function (self, arr) {
899 if (self.length != arr.length) {
900 return false;
901 }
902 return (MochiKit.Base.compare(self, arr) === 0);
903 },
904
905 /** @id MochiKit.Base.concat */
906 concat: function (/* lst... */) {
907 var rval = [];
908 var extend = MochiKit.Base.extend;
909 for (var i = 0; i < arguments.length; i++) {
910 extend(rval, arguments[i]);
911 }
912 return rval;
913 },
914
915 /** @id MochiKit.Base.keyComparator */
916 keyComparator: function (key/* ... */) {
917 // fast-path for single key comparisons
918 var m = MochiKit.Base;
919 var compare = m.compare;
920 if (arguments.length == 1) {
921 return function (a, b) {
922 return compare(a[key], b[key]);
923 };
924 }
925 var compareKeys = m.extend(null, arguments);
926 return function (a, b) {
927 var rval = 0;
928 // keep comparing until something is inequal or we run out of
929 // keys to compare
930 for (var i = 0; (rval === 0) && (i < compareKeys.length); i++) {
931 var key = compareKeys[i];
932 rval = compare(a[key], b[key]);
933 }
934 return rval;
935 };
936 },
937
938 /** @id MochiKit.Base.reverseKeyComparator */
939 reverseKeyComparator: function (key) {
940 var comparator = MochiKit.Base.keyComparator.apply(this, arguments);
941 return function (a, b) {
942 return comparator(b, a);
943 };
944 },
945
946 /** @id MochiKit.Base.partial */
947 partial: function (func) {
948 var m = MochiKit.Base;
949 return m.bind.apply(this, m.extend([func, undefined], arguments, 1));
950 },
951
952 /** @id MochiKit.Base.listMinMax */
953 listMinMax: function (which, lst) {
954 if (lst.length === 0) {
955 return null;
956 }
957 var cur = lst[0];
958 var compare = MochiKit.Base.compare;
959 for (var i = 1; i < lst.length; i++) {
960 var o = lst[i];
961 if (compare(o, cur) == which) {
962 cur = o;
963 }
964 }
965 return cur;
966 },
967
968 /** @id MochiKit.Base.objMax */
969 objMax: function (/* obj... */) {
970 return MochiKit.Base.listMinMax(1, arguments);
971 },
972
973 /** @id MochiKit.Base.objMin */
974 objMin: function (/* obj... */) {
975 return MochiKit.Base.listMinMax(-1, arguments);
976 },
977
978 /** @id MochiKit.Base.findIdentical */
979 findIdentical: function (lst, value, start/* = 0 */, /* optional */end) {
980 if (typeof(end) == "undefined" || end === null) {
981 end = lst.length;
982 }
983 if (typeof(start) == "undefined" || start === null) {
984 start = 0;
985 }
986 for (var i = start; i < end; i++) {
987 if (lst[i] === value) {
988 return i;
989 }
990 }
991 return -1;
992 },
993
994 /** @id MochiKit.Base.mean */
995 mean: function(/* lst... */) {
996 /* http://www.nist.gov/dads/HTML/mean.html */
997 var sum = 0;
998
999 var m = MochiKit.Base;
1000 var args = m.extend(null, arguments);
1001 var count = args.length;
1002
1003 while (args.length) {
1004 var o = args.shift();
1005 if (o && typeof(o) == "object" && typeof(o.length) == "number") {
1006 count += o.length - 1;
1007 for (var i = o.length - 1; i >= 0; i--) {
1008 sum += o[i];
1009 }
1010 } else {
1011 sum += o;
1012 }
1013 }
1014
1015 if (count <= 0) {
1016 throw new TypeError('mean() requires at least one argument');
1017 }
1018
1019 return sum/count;
1020 },
1021
1022 /** @id MochiKit.Base.median */
1023 median: function(/* lst... */) {
1024 /* http://www.nist.gov/dads/HTML/median.html */
1025 var data = MochiKit.Base.flattenArguments(arguments);
1026 if (data.length === 0) {
1027 throw new TypeError('median() requires at least one argument');
1028 }
1029 data.sort(compare);
1030 if (data.length % 2 == 0) {
1031 var upper = data.length / 2;
1032 return (data[upper] + data[upper - 1]) / 2;
1033 } else {
1034 return data[(data.length - 1) / 2];
1035 }
1036 },
1037
1038 /** @id MochiKit.Base.findValue */
1039 findValue: function (lst, value, start/* = 0 */, /* optional */end) {
1040 if (typeof(end) == "undefined" || end === null) {
1041 end = lst.length;
1042 }
1043 if (typeof(start) == "undefined" || start === null) {
1044 start = 0;
1045 }
1046 var cmp = MochiKit.Base.compare;
1047 for (var i = start; i < end; i++) {
1048 if (cmp(lst[i], value) === 0) {
1049 return i;
1050 }
1051 }
1052 return -1;
1053 },
1054
1055 /** @id MochiKit.Base.nodeWalk */
1056 nodeWalk: function (node, visitor) {
1057 var nodes = [node];
1058 var extend = MochiKit.Base.extend;
1059 while (nodes.length) {
1060 var res = visitor(nodes.shift());
1061 if (res) {
1062 extend(nodes, res);
1063 }
1064 }
1065 },
1066
1067
1068 /** @id MochiKit.Base.nameFunctions */
1069 nameFunctions: function (namespace) {
1070 var base = namespace.NAME;
1071 if (typeof(base) == 'undefined') {
1072 base = '';
1073 } else {
1074 base = base + '.';
1075 }
1076 for (var name in namespace) {
1077 var o = namespace[name];
1078 if (typeof(o) == 'function' && typeof(o.NAME) == 'undefined') {
1079 try {
1080 o.NAME = base + name;
1081 } catch (e) {
1082 // pass
1083 }
1084 }
1085 }
1086 },
1087
1088
1089 /** @id MochiKit.Base.queryString */
1090 queryString: function (names, values) {
1091 // check to see if names is a string or a DOM element, and if
1092 // MochiKit.DOM is available. If so, drop it like it's a form
1093 // Ugliest conditional in MochiKit? Probably!
1094 if (typeof(MochiKit.DOM) != "undefined" && arguments.length == 1
1095 && (typeof(names) == "string" || (
1096 typeof(names.nodeType) != "undefined" && names.nodeType > 0
1097 ))
1098 ) {
1099 var kv = MochiKit.DOM.formContents(names);
1100 names = kv[0];
1101 values = kv[1];
1102 } else if (arguments.length == 1) {
1103 // Allow the return value of formContents to be passed directly
1104 if (typeof(names.length) == "number" && names.length == 2) {
1105 return arguments.callee(names[0], names[1]);
1106 }
1107 var o = names;
1108 names = [];
1109 values = [];
1110 for (var k in o) {
1111 var v = o[k];
1112 if (typeof(v) == "function") {
1113 continue;
1114 } else if (MochiKit.Base.isArrayLike(v)){
1115 for (var i = 0; i < v.length; i++) {
1116 names.push(k);
1117 values.push(v[i]);
1118 }
1119 } else {
1120 names.push(k);
1121 values.push(v);
1122 }
1123 }
1124 }
1125 var rval = [];
1126 var len = Math.min(names.length, values.length);
1127 var urlEncode = MochiKit.Base.urlEncode;
1128 for (var i = 0; i < len; i++) {
1129 v = values[i];
1130 if (typeof(v) != 'undefined' && v !== null) {
1131 rval.push(urlEncode(names[i]) + "=" + urlEncode(v));
1132 }
1133 }
1134 return rval.join("&");
1135 },
1136
1137
1138 /** @id MochiKit.Base.parseQueryString */
1139 parseQueryString: function (encodedString, useArrays) {
1140 // strip a leading '?' from the encoded string
1141 var qstr = (encodedString.charAt(0) == "?")
1142 ? encodedString.substring(1)
1143 : encodedString;
1144 var pairs = qstr.replace(/\+/g, "%20").split(/(\&amp\;|\&\#38\;|\&#x26;|\&)/);
1145 var o = {};
1146 var decode;
1147 if (typeof(decodeURIComponent) != "undefined") {
1148 decode = decodeURIComponent;
1149 } else {
1150 decode = unescape;
1151 }
1152 if (useArrays) {
1153 for (var i = 0; i < pairs.length; i++) {
1154 var pair = pairs[i].split("=");
1155 var name = decode(pair.shift());
1156 if (!name) {
1157 continue;
1158 }
1159 var arr = o[name];
1160 if (!(arr instanceof Array)) {
1161 arr = [];
1162 o[name] = arr;
1163 }
1164 arr.push(decode(pair.join("=")));
1165 }
1166 } else {
1167 for (i = 0; i < pairs.length; i++) {
1168 pair = pairs[i].split("=");
1169 var name = pair.shift();
1170 if (!name) {
1171 continue;
1172 }
1173 o[decode(name)] = decode(pair.join("="));
1174 }
1175 }
1176 return o;
1177 }
1178 });
1179
1180 /** @id MochiKit.Base.AdapterRegistry */
1181 MochiKit.Base.AdapterRegistry = function () {
1182 this.pairs = [];
1183 };
1184
1185 MochiKit.Base.AdapterRegistry.prototype = {
1186 /** @id MochiKit.Base.AdapterRegistry.prototype.register */
1187 register: function (name, check, wrap, /* optional */ override) {
1188 if (override) {
1189 this.pairs.unshift([name, check, wrap]);
1190 } else {
1191 this.pairs.push([name, check, wrap]);
1192 }
1193 },
1194
1195 /** @id MochiKit.Base.AdapterRegistry.prototype.match */
1196 match: function (/* ... */) {
1197 for (var i = 0; i < this.pairs.length; i++) {
1198 var pair = this.pairs[i];
1199 if (pair[1].apply(this, arguments)) {
1200 return pair[2].apply(this, arguments);
1201 }
1202 }
1203 throw MochiKit.Base.NotFound;
1204 },
1205
1206 /** @id MochiKit.Base.AdapterRegistry.prototype.unregister */
1207 unregister: function (name) {
1208 for (var i = 0; i < this.pairs.length; i++) {
1209 var pair = this.pairs[i];
1210 if (pair[0] == name) {
1211 this.pairs.splice(i, 1);
1212 return true;
1213 }
1214 }
1215 return false;
1216 }
1217 };
1218
1219
1220 MochiKit.Base.EXPORT = [
1221 "flattenArray",
1222 "noop",
1223 "camelize",
1224 "counter",
1225 "clone",
1226 "extend",
1227 "update",
1228 "updatetree",
1229 "setdefault",
1230 "keys",
1231 "values",
1232 "items",
1233 "NamedError",
1234 "operator",
1235 "forwardCall",
1236 "itemgetter",
1237 "typeMatcher",
1238 "isCallable",
1239 "isUndefined",
1240 "isUndefinedOrNull",
1241 "isNull",
1242 "isEmpty",
1243 "isNotEmpty",
1244 "isArrayLike",
1245 "isDateLike",
1246 "xmap",
1247 "map",
1248 "xfilter",
1249 "filter",
1250 "methodcaller",
1251 "compose",
1252 "bind",
1253 "bindMethods",
1254 "NotFound",
1255 "AdapterRegistry",
1256 "registerComparator",
1257 "compare",
1258 "registerRepr",
1259 "repr",
1260 "objEqual",
1261 "arrayEqual",
1262 "concat",
1263 "keyComparator",
1264 "reverseKeyComparator",
1265 "partial",
1266 "merge",
1267 "listMinMax",
1268 "listMax",
1269 "listMin",
1270 "objMax",
1271 "objMin",
1272 "nodeWalk",
1273 "zip",
1274 "urlEncode",
1275 "queryString",
1276 "serializeJSON",
1277 "registerJSON",
1278 "evalJSON",
1279 "parseQueryString",
1280 "findValue",
1281 "findIdentical",
1282 "flattenArguments",
1283 "method",
1284 "average",
1285 "mean",
1286 "median"
1287 ];
1288
1289 MochiKit.Base.EXPORT_OK = [
1290 "nameFunctions",
1291 "comparatorRegistry",
1292 "reprRegistry",
1293 "jsonRegistry",
1294 "compareDateLike",
1295 "compareArrayLike",
1296 "reprArrayLike",
1297 "reprString",
1298 "reprNumber"
1299 ];
1300
1301 MochiKit.Base._exportSymbols = function (globals, module) {
1302 if (!MochiKit.__export__) {
1303 return;
1304 }
1305 var all = module.EXPORT_TAGS[":all"];
1306 for (var i = 0; i < all.length; i++) {
1307 globals[all[i]] = module[all[i]];
1308 }
1309 };
1310
1311 MochiKit.Base.__new__ = function () {
1312 // A singleton raised when no suitable adapter is found
1313 var m = this;
1314
1315 // convenience
1316 /** @id MochiKit.Base.noop */
1317 m.noop = m.operator.identity;
1318
1319 // Backwards compat
1320 m.forward = m.forwardCall;
1321 m.find = m.findValue;
1322
1323 if (typeof(encodeURIComponent) != "undefined") {
1324 /** @id MochiKit.Base.urlEncode */
1325 m.urlEncode = function (unencoded) {
1326 return encodeURIComponent(unencoded).replace(/\'/g, '%27');
1327 };
1328 } else {
1329 m.urlEncode = function (unencoded) {
1330 return escape(unencoded
1331 ).replace(/\+/g, '%2B'
1332 ).replace(/\"/g,'%22'
1333 ).rval.replace(/\'/g, '%27');
1334 };
1335 }
1336
1337 /** @id MochiKit.Base.NamedError */
1338 m.NamedError = function (name) {
1339 this.message = name;
1340 this.name = name;
1341 };
1342 m.NamedError.prototype = new Error();
1343 m.update(m.NamedError.prototype, {
1344 repr: function () {
1345 if (this.message && this.message != this.name) {
1346 return this.name + "(" + m.repr(this.message) + ")";
1347 } else {
1348 return this.name + "()";
1349 }
1350 },
1351 toString: m.forwardCall("repr")
1352 });
1353
1354 /** @id MochiKit.Base.NotFound */
1355 m.NotFound = new m.NamedError("MochiKit.Base.NotFound");
1356
1357
1358 /** @id MochiKit.Base.listMax */
1359 m.listMax = m.partial(m.listMinMax, 1);
1360 /** @id MochiKit.Base.listMin */
1361 m.listMin = m.partial(m.listMinMax, -1);
1362
1363 /** @id MochiKit.Base.isCallable */
1364 m.isCallable = m.typeMatcher('function');
1365 /** @id MochiKit.Base.isUndefined */
1366 m.isUndefined = m.typeMatcher('undefined');
1367
1368 /** @id MochiKit.Base.merge */
1369 m.merge = m.partial(m.update, null);
1370 /** @id MochiKit.Base.zip */
1371 m.zip = m.partial(m.map, null);
1372
1373 /** @id MochiKit.Base.average */
1374 m.average = m.mean;
1375
1376 /** @id MochiKit.Base.comparatorRegistry */
1377 m.comparatorRegistry = new m.AdapterRegistry();
1378 m.registerComparator("dateLike", m.isDateLike, m.compareDateLike);
1379 m.registerComparator("arrayLike", m.isArrayLike, m.compareArrayLike);
1380
1381 /** @id MochiKit.Base.reprRegistry */
1382 m.reprRegistry = new m.AdapterRegistry();
1383 m.registerRepr("arrayLike", m.isArrayLike, m.reprArrayLike);
1384 m.registerRepr("string", m.typeMatcher("string"), m.reprString);
1385 m.registerRepr("numbers", m.typeMatcher("number", "boolean"), m.reprNumber);
1386
1387 /** @id MochiKit.Base.jsonRegistry */
1388 m.jsonRegistry = new m.AdapterRegistry();
1389
1390 var all = m.concat(m.EXPORT, m.EXPORT_OK);
1391 m.EXPORT_TAGS = {
1392 ":common": m.concat(m.EXPORT_OK),
1393 ":all": all
1394 };
1395
1396 m.nameFunctions(this);
1397
1398 };
1399
1400 MochiKit.Base.__new__();
1401
1402 //
1403 // XXX: Internet Explorer blows
1404 //
1405 if (MochiKit.__export__) {
1406 compare = MochiKit.Base.compare;
1407 compose = MochiKit.Base.compose;
1408 serializeJSON = MochiKit.Base.serializeJSON;
1409 mean = MochiKit.Base.mean;
1410 median = MochiKit.Base.median;
1411 }
1412
1413 MochiKit.Base._exportSymbols(this, MochiKit.Base);
This diff has been collapsed as it changes many lines, (902 lines changed) Show them Hide them
@@ -0,0 +1,902 b''
1 /***
2
3 MochiKit.Color 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005 Bob Ippolito and others. All rights Reserved.
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide('MochiKit.Color');
13 dojo.require('MochiKit.Base');
14 dojo.require('MochiKit.DOM');
15 dojo.require('MochiKit.Style');
16 }
17
18 if (typeof(JSAN) != 'undefined') {
19 JSAN.use("MochiKit.Base", []);
20 JSAN.use("MochiKit.DOM", []);
21 JSAN.use("MochiKit.Style", []);
22 }
23
24 try {
25 if (typeof(MochiKit.Base) == 'undefined') {
26 throw "";
27 }
28 } catch (e) {
29 throw "MochiKit.Color depends on MochiKit.Base";
30 }
31
32 try {
33 if (typeof(MochiKit.DOM) == 'undefined') {
34 throw "";
35 }
36 } catch (e) {
37 throw "MochiKit.Color depends on MochiKit.DOM";
38 }
39
40 try {
41 if (typeof(MochiKit.Style) == 'undefined') {
42 throw "";
43 }
44 } catch (e) {
45 throw "MochiKit.Color depends on MochiKit.Style";
46 }
47
48 if (typeof(MochiKit.Color) == "undefined") {
49 MochiKit.Color = {};
50 }
51
52 MochiKit.Color.NAME = "MochiKit.Color";
53 MochiKit.Color.VERSION = "1.4";
54
55 MochiKit.Color.__repr__ = function () {
56 return "[" + this.NAME + " " + this.VERSION + "]";
57 };
58
59 MochiKit.Color.toString = function () {
60 return this.__repr__();
61 };
62
63
64 /** @id MochiKit.Color.Color */
65 MochiKit.Color.Color = function (red, green, blue, alpha) {
66 if (typeof(alpha) == 'undefined' || alpha === null) {
67 alpha = 1.0;
68 }
69 this.rgb = {
70 r: red,
71 g: green,
72 b: blue,
73 a: alpha
74 };
75 };
76
77
78 // Prototype methods
79
80 MochiKit.Color.Color.prototype = {
81
82 __class__: MochiKit.Color.Color,
83
84 /** @id MochiKit.Color.Color.prototype.colorWithAlpha */
85 colorWithAlpha: function (alpha) {
86 var rgb = this.rgb;
87 var m = MochiKit.Color;
88 return m.Color.fromRGB(rgb.r, rgb.g, rgb.b, alpha);
89 },
90
91 /** @id MochiKit.Color.Color.prototype.colorWithHue */
92 colorWithHue: function (hue) {
93 // get an HSL model, and set the new hue...
94 var hsl = this.asHSL();
95 hsl.h = hue;
96 var m = MochiKit.Color;
97 // convert back to RGB...
98 return m.Color.fromHSL(hsl);
99 },
100
101 /** @id MochiKit.Color.Color.prototype.colorWithSaturation */
102 colorWithSaturation: function (saturation) {
103 // get an HSL model, and set the new hue...
104 var hsl = this.asHSL();
105 hsl.s = saturation;
106 var m = MochiKit.Color;
107 // convert back to RGB...
108 return m.Color.fromHSL(hsl);
109 },
110
111 /** @id MochiKit.Color.Color.prototype.colorWithLightness */
112 colorWithLightness: function (lightness) {
113 // get an HSL model, and set the new hue...
114 var hsl = this.asHSL();
115 hsl.l = lightness;
116 var m = MochiKit.Color;
117 // convert back to RGB...
118 return m.Color.fromHSL(hsl);
119 },
120
121 /** @id MochiKit.Color.Color.prototype.darkerColorWithLevel */
122 darkerColorWithLevel: function (level) {
123 var hsl = this.asHSL();
124 hsl.l = Math.max(hsl.l - level, 0);
125 var m = MochiKit.Color;
126 return m.Color.fromHSL(hsl);
127 },
128
129 /** @id MochiKit.Color.Color.prototype.lighterColorWithLevel */
130 lighterColorWithLevel: function (level) {
131 var hsl = this.asHSL();
132 hsl.l = Math.min(hsl.l + level, 1);
133 var m = MochiKit.Color;
134 return m.Color.fromHSL(hsl);
135 },
136
137 /** @id MochiKit.Color.Color.prototype.blendedColor */
138 blendedColor: function (other, /* optional */ fraction) {
139 if (typeof(fraction) == 'undefined' || fraction === null) {
140 fraction = 0.5;
141 }
142 var sf = 1.0 - fraction;
143 var s = this.rgb;
144 var d = other.rgb;
145 var df = fraction;
146 return MochiKit.Color.Color.fromRGB(
147 (s.r * sf) + (d.r * df),
148 (s.g * sf) + (d.g * df),
149 (s.b * sf) + (d.b * df),
150 (s.a * sf) + (d.a * df)
151 );
152 },
153
154 /** @id MochiKit.Color.Color.prototype.compareRGB */
155 compareRGB: function (other) {
156 var a = this.asRGB();
157 var b = other.asRGB();
158 return MochiKit.Base.compare(
159 [a.r, a.g, a.b, a.a],
160 [b.r, b.g, b.b, b.a]
161 );
162 },
163
164 /** @id MochiKit.Color.Color.prototype.isLight */
165 isLight: function () {
166 return this.asHSL().b > 0.5;
167 },
168
169 /** @id MochiKit.Color.Color.prototype.isDark */
170 isDark: function () {
171 return (!this.isLight());
172 },
173
174 /** @id MochiKit.Color.Color.prototype.toHSLString */
175 toHSLString: function () {
176 var c = this.asHSL();
177 var ccc = MochiKit.Color.clampColorComponent;
178 var rval = this._hslString;
179 if (!rval) {
180 var mid = (
181 ccc(c.h, 360).toFixed(0)
182 + "," + ccc(c.s, 100).toPrecision(4) + "%"
183 + "," + ccc(c.l, 100).toPrecision(4) + "%"
184 );
185 var a = c.a;
186 if (a >= 1) {
187 a = 1;
188 rval = "hsl(" + mid + ")";
189 } else {
190 if (a <= 0) {
191 a = 0;
192 }
193 rval = "hsla(" + mid + "," + a + ")";
194 }
195 this._hslString = rval;
196 }
197 return rval;
198 },
199
200 /** @id MochiKit.Color.Color.prototype.toRGBString */
201 toRGBString: function () {
202 var c = this.rgb;
203 var ccc = MochiKit.Color.clampColorComponent;
204 var rval = this._rgbString;
205 if (!rval) {
206 var mid = (
207 ccc(c.r, 255).toFixed(0)
208 + "," + ccc(c.g, 255).toFixed(0)
209 + "," + ccc(c.b, 255).toFixed(0)
210 );
211 if (c.a != 1) {
212 rval = "rgba(" + mid + "," + c.a + ")";
213 } else {
214 rval = "rgb(" + mid + ")";
215 }
216 this._rgbString = rval;
217 }
218 return rval;
219 },
220
221 /** @id MochiKit.Color.Color.prototype.asRGB */
222 asRGB: function () {
223 return MochiKit.Base.clone(this.rgb);
224 },
225
226 /** @id MochiKit.Color.Color.prototype.toHexString */
227 toHexString: function () {
228 var m = MochiKit.Color;
229 var c = this.rgb;
230 var ccc = MochiKit.Color.clampColorComponent;
231 var rval = this._hexString;
232 if (!rval) {
233 rval = ("#" +
234 m.toColorPart(ccc(c.r, 255)) +
235 m.toColorPart(ccc(c.g, 255)) +
236 m.toColorPart(ccc(c.b, 255))
237 );
238 this._hexString = rval;
239 }
240 return rval;
241 },
242
243 /** @id MochiKit.Color.Color.prototype.asHSV */
244 asHSV: function () {
245 var hsv = this.hsv;
246 var c = this.rgb;
247 if (typeof(hsv) == 'undefined' || hsv === null) {
248 hsv = MochiKit.Color.rgbToHSV(this.rgb);
249 this.hsv = hsv;
250 }
251 return MochiKit.Base.clone(hsv);
252 },
253
254 /** @id MochiKit.Color.Color.prototype.asHSL */
255 asHSL: function () {
256 var hsl = this.hsl;
257 var c = this.rgb;
258 if (typeof(hsl) == 'undefined' || hsl === null) {
259 hsl = MochiKit.Color.rgbToHSL(this.rgb);
260 this.hsl = hsl;
261 }
262 return MochiKit.Base.clone(hsl);
263 },
264
265 /** @id MochiKit.Color.Color.prototype.toString */
266 toString: function () {
267 return this.toRGBString();
268 },
269
270 /** @id MochiKit.Color.Color.prototype.repr */
271 repr: function () {
272 var c = this.rgb;
273 var col = [c.r, c.g, c.b, c.a];
274 return this.__class__.NAME + "(" + col.join(", ") + ")";
275 }
276
277 };
278
279 // Constructor methods
280
281 MochiKit.Base.update(MochiKit.Color.Color, {
282 /** @id MochiKit.Color.Color.fromRGB */
283 fromRGB: function (red, green, blue, alpha) {
284 // designated initializer
285 var Color = MochiKit.Color.Color;
286 if (arguments.length == 1) {
287 var rgb = red;
288 red = rgb.r;
289 green = rgb.g;
290 blue = rgb.b;
291 if (typeof(rgb.a) == 'undefined') {
292 alpha = undefined;
293 } else {
294 alpha = rgb.a;
295 }
296 }
297 return new Color(red, green, blue, alpha);
298 },
299
300 /** @id MochiKit.Color.Color.fromHSL */
301 fromHSL: function (hue, saturation, lightness, alpha) {
302 var m = MochiKit.Color;
303 return m.Color.fromRGB(m.hslToRGB.apply(m, arguments));
304 },
305
306 /** @id MochiKit.Color.Color.fromHSV */
307 fromHSV: function (hue, saturation, value, alpha) {
308 var m = MochiKit.Color;
309 return m.Color.fromRGB(m.hsvToRGB.apply(m, arguments));
310 },
311
312 /** @id MochiKit.Color.Color.fromName */
313 fromName: function (name) {
314 var Color = MochiKit.Color.Color;
315 // Opera 9 seems to "quote" named colors(?!)
316 if (name.charAt(0) == '"') {
317 name = name.substr(1, name.length - 2);
318 }
319 var htmlColor = Color._namedColors[name.toLowerCase()];
320 if (typeof(htmlColor) == 'string') {
321 return Color.fromHexString(htmlColor);
322 } else if (name == "transparent") {
323 return Color.transparentColor();
324 }
325 return null;
326 },
327
328 /** @id MochiKit.Color.Color.fromString */
329 fromString: function (colorString) {
330 var self = MochiKit.Color.Color;
331 var three = colorString.substr(0, 3);
332 if (three == "rgb") {
333 return self.fromRGBString(colorString);
334 } else if (three == "hsl") {
335 return self.fromHSLString(colorString);
336 } else if (colorString.charAt(0) == "#") {
337 return self.fromHexString(colorString);
338 }
339 return self.fromName(colorString);
340 },
341
342
343 /** @id MochiKit.Color.Color.fromHexString */
344 fromHexString: function (hexCode) {
345 if (hexCode.charAt(0) == '#') {
346 hexCode = hexCode.substring(1);
347 }
348 var components = [];
349 var i, hex;
350 if (hexCode.length == 3) {
351 for (i = 0; i < 3; i++) {
352 hex = hexCode.substr(i, 1);
353 components.push(parseInt(hex + hex, 16) / 255.0);
354 }
355 } else {
356 for (i = 0; i < 6; i += 2) {
357 hex = hexCode.substr(i, 2);
358 components.push(parseInt(hex, 16) / 255.0);
359 }
360 }
361 var Color = MochiKit.Color.Color;
362 return Color.fromRGB.apply(Color, components);
363 },
364
365
366 _fromColorString: function (pre, method, scales, colorCode) {
367 // parses either HSL or RGB
368 if (colorCode.indexOf(pre) === 0) {
369 colorCode = colorCode.substring(colorCode.indexOf("(", 3) + 1, colorCode.length - 1);
370 }
371 var colorChunks = colorCode.split(/\s*,\s*/);
372 var colorFloats = [];
373 for (var i = 0; i < colorChunks.length; i++) {
374 var c = colorChunks[i];
375 var val;
376 var three = c.substring(c.length - 3);
377 if (c.charAt(c.length - 1) == '%') {
378 val = 0.01 * parseFloat(c.substring(0, c.length - 1));
379 } else if (three == "deg") {
380 val = parseFloat(c) / 360.0;
381 } else if (three == "rad") {
382 val = parseFloat(c) / (Math.PI * 2);
383 } else {
384 val = scales[i] * parseFloat(c);
385 }
386 colorFloats.push(val);
387 }
388 return this[method].apply(this, colorFloats);
389 },
390
391 /** @id MochiKit.Color.Color.fromComputedStyle */
392 fromComputedStyle: function (elem, style) {
393 var d = MochiKit.DOM;
394 var cls = MochiKit.Color.Color;
395 for (elem = d.getElement(elem); elem; elem = elem.parentNode) {
396 var actualColor = MochiKit.Style.getStyle.apply(d, arguments);
397 if (!actualColor) {
398 continue;
399 }
400 var color = cls.fromString(actualColor);
401 if (!color) {
402 break;
403 }
404 if (color.asRGB().a > 0) {
405 return color;
406 }
407 }
408 return null;
409 },
410
411 /** @id MochiKit.Color.Color.fromBackground */
412 fromBackground: function (elem) {
413 var cls = MochiKit.Color.Color;
414 return cls.fromComputedStyle(
415 elem, "backgroundColor", "background-color") || cls.whiteColor();
416 },
417
418 /** @id MochiKit.Color.Color.fromText */
419 fromText: function (elem) {
420 var cls = MochiKit.Color.Color;
421 return cls.fromComputedStyle(
422 elem, "color", "color") || cls.blackColor();
423 },
424
425 /** @id MochiKit.Color.Color.namedColors */
426 namedColors: function () {
427 return MochiKit.Base.clone(MochiKit.Color.Color._namedColors);
428 }
429 });
430
431
432 // Module level functions
433
434 MochiKit.Base.update(MochiKit.Color, {
435 /** @id MochiKit.Color.clampColorComponent */
436 clampColorComponent: function (v, scale) {
437 v *= scale;
438 if (v < 0) {
439 return 0;
440 } else if (v > scale) {
441 return scale;
442 } else {
443 return v;
444 }
445 },
446
447 _hslValue: function (n1, n2, hue) {
448 if (hue > 6.0) {
449 hue -= 6.0;
450 } else if (hue < 0.0) {
451 hue += 6.0;
452 }
453 var val;
454 if (hue < 1.0) {
455 val = n1 + (n2 - n1) * hue;
456 } else if (hue < 3.0) {
457 val = n2;
458 } else if (hue < 4.0) {
459 val = n1 + (n2 - n1) * (4.0 - hue);
460 } else {
461 val = n1;
462 }
463 return val;
464 },
465
466 /** @id MochiKit.Color.hsvToRGB */
467 hsvToRGB: function (hue, saturation, value, alpha) {
468 if (arguments.length == 1) {
469 var hsv = hue;
470 hue = hsv.h;
471 saturation = hsv.s;
472 value = hsv.v;
473 alpha = hsv.a;
474 }
475 var red;
476 var green;
477 var blue;
478 if (saturation === 0) {
479 red = value;
480 green = value;
481 blue = value;
482 } else {
483 var i = Math.floor(hue * 6);
484 var f = (hue * 6) - i;
485 var p = value * (1 - saturation);
486 var q = value * (1 - (saturation * f));
487 var t = value * (1 - (saturation * (1 - f)));
488 switch (i) {
489 case 1: red = q; green = value; blue = p; break;
490 case 2: red = p; green = value; blue = t; break;
491 case 3: red = p; green = q; blue = value; break;
492 case 4: red = t; green = p; blue = value; break;
493 case 5: red = value; green = p; blue = q; break;
494 case 6: // fall through
495 case 0: red = value; green = t; blue = p; break;
496 }
497 }
498 return {
499 r: red,
500 g: green,
501 b: blue,
502 a: alpha
503 };
504 },
505
506 /** @id MochiKit.Color.hslToRGB */
507 hslToRGB: function (hue, saturation, lightness, alpha) {
508 if (arguments.length == 1) {
509 var hsl = hue;
510 hue = hsl.h;
511 saturation = hsl.s;
512 lightness = hsl.l;
513 alpha = hsl.a;
514 }
515 var red;
516 var green;
517 var blue;
518 if (saturation === 0) {
519 red = lightness;
520 green = lightness;
521 blue = lightness;
522 } else {
523 var m2;
524 if (lightness <= 0.5) {
525 m2 = lightness * (1.0 + saturation);
526 } else {
527 m2 = lightness + saturation - (lightness * saturation);
528 }
529 var m1 = (2.0 * lightness) - m2;
530 var f = MochiKit.Color._hslValue;
531 var h6 = hue * 6.0;
532 red = f(m1, m2, h6 + 2);
533 green = f(m1, m2, h6);
534 blue = f(m1, m2, h6 - 2);
535 }
536 return {
537 r: red,
538 g: green,
539 b: blue,
540 a: alpha
541 };
542 },
543
544 /** @id MochiKit.Color.rgbToHSV */
545 rgbToHSV: function (red, green, blue, alpha) {
546 if (arguments.length == 1) {
547 var rgb = red;
548 red = rgb.r;
549 green = rgb.g;
550 blue = rgb.b;
551 alpha = rgb.a;
552 }
553 var max = Math.max(Math.max(red, green), blue);
554 var min = Math.min(Math.min(red, green), blue);
555 var hue;
556 var saturation;
557 var value = max;
558 if (min == max) {
559 hue = 0;
560 saturation = 0;
561 } else {
562 var delta = (max - min);
563 saturation = delta / max;
564
565 if (red == max) {
566 hue = (green - blue) / delta;
567 } else if (green == max) {
568 hue = 2 + ((blue - red) / delta);
569 } else {
570 hue = 4 + ((red - green) / delta);
571 }
572 hue /= 6;
573 if (hue < 0) {
574 hue += 1;
575 }
576 if (hue > 1) {
577 hue -= 1;
578 }
579 }
580 return {
581 h: hue,
582 s: saturation,
583 v: value,
584 a: alpha
585 };
586 },
587
588 /** @id MochiKit.Color.rgbToHSL */
589 rgbToHSL: function (red, green, blue, alpha) {
590 if (arguments.length == 1) {
591 var rgb = red;
592 red = rgb.r;
593 green = rgb.g;
594 blue = rgb.b;
595 alpha = rgb.a;
596 }
597 var max = Math.max(red, Math.max(green, blue));
598 var min = Math.min(red, Math.min(green, blue));
599 var hue;
600 var saturation;
601 var lightness = (max + min) / 2.0;
602 var delta = max - min;
603 if (delta === 0) {
604 hue = 0;
605 saturation = 0;
606 } else {
607 if (lightness <= 0.5) {
608 saturation = delta / (max + min);
609 } else {
610 saturation = delta / (2 - max - min);
611 }
612 if (red == max) {
613 hue = (green - blue) / delta;
614 } else if (green == max) {
615 hue = 2 + ((blue - red) / delta);
616 } else {
617 hue = 4 + ((red - green) / delta);
618 }
619 hue /= 6;
620 if (hue < 0) {
621 hue += 1;
622 }
623 if (hue > 1) {
624 hue -= 1;
625 }
626
627 }
628 return {
629 h: hue,
630 s: saturation,
631 l: lightness,
632 a: alpha
633 };
634 },
635
636 /** @id MochiKit.Color.toColorPart */
637 toColorPart: function (num) {
638 num = Math.round(num);
639 var digits = num.toString(16);
640 if (num < 16) {
641 return '0' + digits;
642 }
643 return digits;
644 },
645
646 __new__: function () {
647 var m = MochiKit.Base;
648 /** @id MochiKit.Color.fromRGBString */
649 this.Color.fromRGBString = m.bind(
650 this.Color._fromColorString, this.Color, "rgb", "fromRGB",
651 [1.0/255.0, 1.0/255.0, 1.0/255.0, 1]
652 );
653 /** @id MochiKit.Color.fromHSLString */
654 this.Color.fromHSLString = m.bind(
655 this.Color._fromColorString, this.Color, "hsl", "fromHSL",
656 [1.0/360.0, 0.01, 0.01, 1]
657 );
658
659 var third = 1.0 / 3.0;
660 /** @id MochiKit.Color.colors */
661 var colors = {
662 // NSColor colors plus transparent
663 /** @id MochiKit.Color.blackColor */
664 black: [0, 0, 0],
665 /** @id MochiKit.Color.blueColor */
666 blue: [0, 0, 1],
667 /** @id MochiKit.Color.brownColor */
668 brown: [0.6, 0.4, 0.2],
669 /** @id MochiKit.Color.cyanColor */
670 cyan: [0, 1, 1],
671 /** @id MochiKit.Color.darkGrayColor */
672 darkGray: [third, third, third],
673 /** @id MochiKit.Color.grayColor */
674 gray: [0.5, 0.5, 0.5],
675 /** @id MochiKit.Color.greenColor */
676 green: [0, 1, 0],
677 /** @id MochiKit.Color.lightGrayColor */
678 lightGray: [2 * third, 2 * third, 2 * third],
679 /** @id MochiKit.Color.magentaColor */
680 magenta: [1, 0, 1],
681 /** @id MochiKit.Color.orangeColor */
682 orange: [1, 0.5, 0],
683 /** @id MochiKit.Color.purpleColor */
684 purple: [0.5, 0, 0.5],
685 /** @id MochiKit.Color.redColor */
686 red: [1, 0, 0],
687 /** @id MochiKit.Color.transparentColor */
688 transparent: [0, 0, 0, 0],
689 /** @id MochiKit.Color.whiteColor */
690 white: [1, 1, 1],
691 /** @id MochiKit.Color.yellowColor */
692 yellow: [1, 1, 0]
693 };
694
695 var makeColor = function (name, r, g, b, a) {
696 var rval = this.fromRGB(r, g, b, a);
697 this[name] = function () { return rval; };
698 return rval;
699 };
700
701 for (var k in colors) {
702 var name = k + "Color";
703 var bindArgs = m.concat(
704 [makeColor, this.Color, name],
705 colors[k]
706 );
707 this.Color[name] = m.bind.apply(null, bindArgs);
708 }
709
710 var isColor = function () {
711 for (var i = 0; i < arguments.length; i++) {
712 if (!(arguments[i] instanceof Color)) {
713 return false;
714 }
715 }
716 return true;
717 };
718
719 var compareColor = function (a, b) {
720 return a.compareRGB(b);
721 };
722
723 m.nameFunctions(this);
724
725 m.registerComparator(this.Color.NAME, isColor, compareColor);
726
727 this.EXPORT_TAGS = {
728 ":common": this.EXPORT,
729 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
730 };
731
732 }
733 });
734
735 MochiKit.Color.EXPORT = [
736 "Color"
737 ];
738
739 MochiKit.Color.EXPORT_OK = [
740 "clampColorComponent",
741 "rgbToHSL",
742 "hslToRGB",
743 "rgbToHSV",
744 "hsvToRGB",
745 "toColorPart"
746 ];
747
748 MochiKit.Color.__new__();
749
750 MochiKit.Base._exportSymbols(this, MochiKit.Color);
751
752 // Full table of css3 X11 colors <http://www.w3.org/TR/css3-color/#X11COLORS>
753
754 MochiKit.Color.Color._namedColors = {
755 aliceblue: "#f0f8ff",
756 antiquewhite: "#faebd7",
757 aqua: "#00ffff",
758 aquamarine: "#7fffd4",
759 azure: "#f0ffff",
760 beige: "#f5f5dc",
761 bisque: "#ffe4c4",
762 black: "#000000",
763 blanchedalmond: "#ffebcd",
764 blue: "#0000ff",
765 blueviolet: "#8a2be2",
766 brown: "#a52a2a",
767 burlywood: "#deb887",
768 cadetblue: "#5f9ea0",
769 chartreuse: "#7fff00",
770 chocolate: "#d2691e",
771 coral: "#ff7f50",
772 cornflowerblue: "#6495ed",
773 cornsilk: "#fff8dc",
774 crimson: "#dc143c",
775 cyan: "#00ffff",
776 darkblue: "#00008b",
777 darkcyan: "#008b8b",
778 darkgoldenrod: "#b8860b",
779 darkgray: "#a9a9a9",
780 darkgreen: "#006400",
781 darkgrey: "#a9a9a9",
782 darkkhaki: "#bdb76b",
783 darkmagenta: "#8b008b",
784 darkolivegreen: "#556b2f",
785 darkorange: "#ff8c00",
786 darkorchid: "#9932cc",
787 darkred: "#8b0000",
788 darksalmon: "#e9967a",
789 darkseagreen: "#8fbc8f",
790 darkslateblue: "#483d8b",
791 darkslategray: "#2f4f4f",
792 darkslategrey: "#2f4f4f",
793 darkturquoise: "#00ced1",
794 darkviolet: "#9400d3",
795 deeppink: "#ff1493",
796 deepskyblue: "#00bfff",
797 dimgray: "#696969",
798 dimgrey: "#696969",
799 dodgerblue: "#1e90ff",
800 firebrick: "#b22222",
801 floralwhite: "#fffaf0",
802 forestgreen: "#228b22",
803 fuchsia: "#ff00ff",
804 gainsboro: "#dcdcdc",
805 ghostwhite: "#f8f8ff",
806 gold: "#ffd700",
807 goldenrod: "#daa520",
808 gray: "#808080",
809 green: "#008000",
810 greenyellow: "#adff2f",
811 grey: "#808080",
812 honeydew: "#f0fff0",
813 hotpink: "#ff69b4",
814 indianred: "#cd5c5c",
815 indigo: "#4b0082",
816 ivory: "#fffff0",
817 khaki: "#f0e68c",
818 lavender: "#e6e6fa",
819 lavenderblush: "#fff0f5",
820 lawngreen: "#7cfc00",
821 lemonchiffon: "#fffacd",
822 lightblue: "#add8e6",
823 lightcoral: "#f08080",
824 lightcyan: "#e0ffff",
825 lightgoldenrodyellow: "#fafad2",
826 lightgray: "#d3d3d3",
827 lightgreen: "#90ee90",
828 lightgrey: "#d3d3d3",
829 lightpink: "#ffb6c1",
830 lightsalmon: "#ffa07a",
831 lightseagreen: "#20b2aa",
832 lightskyblue: "#87cefa",
833 lightslategray: "#778899",
834 lightslategrey: "#778899",
835 lightsteelblue: "#b0c4de",
836 lightyellow: "#ffffe0",
837 lime: "#00ff00",
838 limegreen: "#32cd32",
839 linen: "#faf0e6",
840 magenta: "#ff00ff",
841 maroon: "#800000",
842 mediumaquamarine: "#66cdaa",
843 mediumblue: "#0000cd",
844 mediumorchid: "#ba55d3",
845 mediumpurple: "#9370db",
846 mediumseagreen: "#3cb371",
847 mediumslateblue: "#7b68ee",
848 mediumspringgreen: "#00fa9a",
849 mediumturquoise: "#48d1cc",
850 mediumvioletred: "#c71585",
851 midnightblue: "#191970",
852 mintcream: "#f5fffa",
853 mistyrose: "#ffe4e1",
854 moccasin: "#ffe4b5",
855 navajowhite: "#ffdead",
856 navy: "#000080",
857 oldlace: "#fdf5e6",
858 olive: "#808000",
859 olivedrab: "#6b8e23",
860 orange: "#ffa500",
861 orangered: "#ff4500",
862 orchid: "#da70d6",
863 palegoldenrod: "#eee8aa",
864 palegreen: "#98fb98",
865 paleturquoise: "#afeeee",
866 palevioletred: "#db7093",
867 papayawhip: "#ffefd5",
868 peachpuff: "#ffdab9",
869 peru: "#cd853f",
870 pink: "#ffc0cb",
871 plum: "#dda0dd",
872 powderblue: "#b0e0e6",
873 purple: "#800080",
874 red: "#ff0000",
875 rosybrown: "#bc8f8f",
876 royalblue: "#4169e1",
877 saddlebrown: "#8b4513",
878 salmon: "#fa8072",
879 sandybrown: "#f4a460",
880 seagreen: "#2e8b57",
881 seashell: "#fff5ee",
882 sienna: "#a0522d",
883 silver: "#c0c0c0",
884 skyblue: "#87ceeb",
885 slateblue: "#6a5acd",
886 slategray: "#708090",
887 slategrey: "#708090",
888 snow: "#fffafa",
889 springgreen: "#00ff7f",
890 steelblue: "#4682b4",
891 tan: "#d2b48c",
892 teal: "#008080",
893 thistle: "#d8bfd8",
894 tomato: "#ff6347",
895 turquoise: "#40e0d0",
896 violet: "#ee82ee",
897 wheat: "#f5deb3",
898 white: "#ffffff",
899 whitesmoke: "#f5f5f5",
900 yellow: "#ffff00",
901 yellowgreen: "#9acd32"
902 };
This diff has been collapsed as it changes many lines, (1281 lines changed) Show them Hide them
@@ -0,0 +1,1281 b''
1 /***
2
3 MochiKit.DOM 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005 Bob Ippolito. All rights Reserved.
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide("MochiKit.DOM");
13 dojo.require("MochiKit.Base");
14 }
15 if (typeof(JSAN) != 'undefined') {
16 JSAN.use("MochiKit.Base", []);
17 }
18
19 try {
20 if (typeof(MochiKit.Base) == 'undefined') {
21 throw "";
22 }
23 } catch (e) {
24 throw "MochiKit.DOM depends on MochiKit.Base!";
25 }
26
27 if (typeof(MochiKit.DOM) == 'undefined') {
28 MochiKit.DOM = {};
29 }
30
31 MochiKit.DOM.NAME = "MochiKit.DOM";
32 MochiKit.DOM.VERSION = "1.4";
33 MochiKit.DOM.__repr__ = function () {
34 return "[" + this.NAME + " " + this.VERSION + "]";
35 };
36 MochiKit.DOM.toString = function () {
37 return this.__repr__();
38 };
39
40 MochiKit.DOM.EXPORT = [
41 "removeEmptyTextNodes",
42 "formContents",
43 "currentWindow",
44 "currentDocument",
45 "withWindow",
46 "withDocument",
47 "registerDOMConverter",
48 "coerceToDOM",
49 "createDOM",
50 "createDOMFunc",
51 "isChildNode",
52 "getNodeAttribute",
53 "removeNodeAttribute",
54 "setNodeAttribute",
55 "updateNodeAttributes",
56 "appendChildNodes",
57 "insertSiblingNodesAfter",
58 "insertSiblingNodesBefore",
59 "replaceChildNodes",
60 "removeElement",
61 "swapDOM",
62 "BUTTON",
63 "TT",
64 "PRE",
65 "H1",
66 "H2",
67 "H3",
68 "BR",
69 "CANVAS",
70 "HR",
71 "LABEL",
72 "TEXTAREA",
73 "FORM",
74 "STRONG",
75 "SELECT",
76 "OPTION",
77 "OPTGROUP",
78 "LEGEND",
79 "FIELDSET",
80 "P",
81 "UL",
82 "OL",
83 "LI",
84 "TD",
85 "TR",
86 "THEAD",
87 "TBODY",
88 "TFOOT",
89 "TABLE",
90 "TH",
91 "INPUT",
92 "SPAN",
93 "A",
94 "DIV",
95 "IMG",
96 "getElement",
97 "$",
98 "getElementsByTagAndClassName",
99 "addToCallStack",
100 "addLoadEvent",
101 "focusOnLoad",
102 "setElementClass",
103 "toggleElementClass",
104 "addElementClass",
105 "removeElementClass",
106 "swapElementClass",
107 "hasElementClass",
108 "escapeHTML",
109 "toHTML",
110 "emitHTML",
111 "scrapeText",
112 "isParent",
113 "getFirstParentByTagAndClassName",
114 "makeClipping",
115 "undoClipping",
116 "makePositioned",
117 "undoPositioned",
118 "getFirstElementByTagAndClassName"
119 ];
120
121 MochiKit.DOM.EXPORT_OK = [
122 "domConverters"
123 ];
124
125 MochiKit.DOM.DEPRECATED = [
126 ['computedStyle', 'MochiKit.Style.getStyle', '1.4'],
127 /** @id MochiKit.DOM.elementDimensions */
128 ['elementDimensions', 'MochiKit.Style.getElementDimensions', '1.4'],
129 /** @id MochiKit.DOM.elementPosition */
130 ['elementPosition', 'MochiKit.Style.getElementPosition', '1.4'],
131 ['hideElement', 'MochiKit.Style.hideElement', '1.4'],
132 /** @id MochiKit.DOM.setElementDimensions */
133 ['setElementDimensions', 'MochiKit.Style.setElementDimensions', '1.4'],
134 /** @id MochiKit.DOM.setElementPosition */
135 ['setElementPosition', 'MochiKit.Style.setElementPosition', '1.4'],
136 ['setDisplayForElement', 'MochiKit.Style.setDisplayForElement', '1.4'],
137 /** @id MochiKit.DOM.setOpacity */
138 ['setOpacity', 'MochiKit.Style.setOpacity', '1.4'],
139 ['showElement', 'MochiKit.Style.showElement', '1.4'],
140 /** @id MochiKit.DOM.Coordinates */
141 ['Coordinates', 'MochiKit.Style.Coordinates', '1.4'], // FIXME: broken
142 /** @id MochiKit.DOM.Dimensions */
143 ['Dimensions', 'MochiKit.Style.Dimensions', '1.4'] // FIXME: broken
144 ];
145
146 /** @id MochiKit.DOM.getViewportDimensions */
147 MochiKit.DOM.getViewportDimensions = new Function('' +
148 'if (!MochiKit["Style"]) {' +
149 ' throw new Error("This function has been deprecated and depends on MochiKit.Style.");' +
150 '}' +
151 'return MochiKit.Style.getViewportDimensions.apply(this, arguments);');
152
153 MochiKit.Base.update(MochiKit.DOM, {
154
155 /** @id MochiKit.DOM.currentWindow */
156 currentWindow: function () {
157 return MochiKit.DOM._window;
158 },
159
160 /** @id MochiKit.DOM.currentDocument */
161 currentDocument: function () {
162 return MochiKit.DOM._document;
163 },
164
165 /** @id MochiKit.DOM.withWindow */
166 withWindow: function (win, func) {
167 var self = MochiKit.DOM;
168 var oldDoc = self._document;
169 var oldWin = self._window;
170 var rval;
171 try {
172 self._window = win;
173 self._document = win.document;
174 rval = func();
175 } catch (e) {
176 self._window = oldWin;
177 self._document = oldDoc;
178 throw e;
179 }
180 self._window = oldWin;
181 self._document = oldDoc;
182 return rval;
183 },
184
185 /** @id MochiKit.DOM.formContents */
186 formContents: function (elem/* = document.body */) {
187 var names = [];
188 var values = [];
189 var m = MochiKit.Base;
190 var self = MochiKit.DOM;
191 if (typeof(elem) == "undefined" || elem === null) {
192 elem = self._document.body;
193 } else {
194 elem = self.getElement(elem);
195 }
196 m.nodeWalk(elem, function (elem) {
197 var name = elem.name;
198 if (m.isNotEmpty(name)) {
199 var tagName = elem.tagName.toUpperCase();
200 if (tagName === "INPUT"
201 && (elem.type == "radio" || elem.type == "checkbox")
202 && !elem.checked
203 ) {
204 return null;
205 }
206 if (tagName === "SELECT") {
207 if (elem.type == "select-one") {
208 if (elem.selectedIndex >= 0) {
209 var opt = elem.options[elem.selectedIndex];
210 var v = opt.value;
211 if (!v) {
212 var h = opt.outerHTML;
213 // internet explorer sure does suck.
214 if (h && !h.match(/^[^>]+\svalue\s*=/i)) {
215 v = opt.text;
216 }
217 }
218 names.push(name);
219 values.push(v);
220 return null;
221 }
222 // no form elements?
223 names.push(name);
224 values.push("");
225 return null;
226 } else {
227 var opts = elem.options;
228 if (!opts.length) {
229 names.push(name);
230 values.push("");
231 return null;
232 }
233 for (var i = 0; i < opts.length; i++) {
234 var opt = opts[i];
235 if (!opt.selected) {
236 continue;
237 }
238 var v = opt.value;
239 if (!v) {
240 var h = opt.outerHTML;
241 // internet explorer sure does suck.
242 if (h && !h.match(/^[^>]+\svalue\s*=/i)) {
243 v = opt.text;
244 }
245 }
246 names.push(name);
247 values.push(v);
248 }
249 return null;
250 }
251 }
252 if (tagName === "FORM" || tagName === "P" || tagName === "SPAN"
253 || tagName === "DIV"
254 ) {
255 return elem.childNodes;
256 }
257 names.push(name);
258 values.push(elem.value || '');
259 return null;
260 }
261 return elem.childNodes;
262 });
263 return [names, values];
264 },
265
266 /** @id MochiKit.DOM.withDocument */
267 withDocument: function (doc, func) {
268 var self = MochiKit.DOM;
269 var oldDoc = self._document;
270 var rval;
271 try {
272 self._document = doc;
273 rval = func();
274 } catch (e) {
275 self._document = oldDoc;
276 throw e;
277 }
278 self._document = oldDoc;
279 return rval;
280 },
281
282 /** @id MochiKit.DOM.registerDOMConverter */
283 registerDOMConverter: function (name, check, wrap, /* optional */override) {
284 MochiKit.DOM.domConverters.register(name, check, wrap, override);
285 },
286
287 /** @id MochiKit.DOM.coerceToDOM */
288 coerceToDOM: function (node, ctx) {
289 var m = MochiKit.Base;
290 var im = MochiKit.Iter;
291 var self = MochiKit.DOM;
292 if (im) {
293 var iter = im.iter;
294 var repeat = im.repeat;
295 var map = m.map;
296 }
297 var domConverters = self.domConverters;
298 var coerceToDOM = arguments.callee;
299 var NotFound = m.NotFound;
300 while (true) {
301 if (typeof(node) == 'undefined' || node === null) {
302 return null;
303 }
304 // this is a safari childNodes object, avoiding crashes w/ attr
305 // lookup
306 if (typeof(node) == "function" &&
307 typeof(node.length) == "number" &&
308 !(node instanceof Function)) {
309 node = im.list(node);
310 }
311 if (typeof(node.nodeType) != 'undefined' && node.nodeType > 0) {
312 return node;
313 }
314 if (typeof(node) == 'number' || typeof(node) == 'boolean') {
315 node = node.toString();
316 // FALL THROUGH
317 }
318 if (typeof(node) == 'string') {
319 return self._document.createTextNode(node);
320 }
321 if (typeof(node.__dom__) == 'function') {
322 node = node.__dom__(ctx);
323 continue;
324 }
325 if (typeof(node.dom) == 'function') {
326 node = node.dom(ctx);
327 continue;
328 }
329 if (typeof(node) == 'function') {
330 node = node.apply(ctx, [ctx]);
331 continue;
332 }
333
334 if (im) {
335 // iterable
336 var iterNodes = null;
337 try {
338 iterNodes = iter(node);
339 } catch (e) {
340 // pass
341 }
342 if (iterNodes) {
343 return map(coerceToDOM, iterNodes, repeat(ctx));
344 }
345 }
346
347 // adapter
348 try {
349 node = domConverters.match(node, ctx);
350 continue;
351 } catch (e) {
352 if (e != NotFound) {
353 throw e;
354 }
355 }
356
357 // fallback
358 return self._document.createTextNode(node.toString());
359 }
360 // mozilla warnings aren't too bright
361 return undefined;
362 },
363
364 /** @id MochiKit.DOM.isChildNode */
365 isChildNode: function (node, maybeparent) {
366 var self = MochiKit.DOM;
367 if (typeof(node) == "string") {
368 node = self.getElement(node);
369 }
370 if (typeof(maybeparent) == "string") {
371 maybeparent = self.getElement(maybeparent);
372 }
373 if (typeof(node) == 'undefined' || node === null || node === self._document) {
374 return false;
375 }
376 do {
377 if (node === maybeparent) {
378 return true;
379 }
380 var tagName = node.tagName;
381 node = node.parentNode;
382 if (!tagName) {
383 break;
384 }
385 tagName = tagName.toUpperCase();
386 } while (tagName != "BODY" && tagName != "HTML");
387 return false;
388 },
389
390 /** @id MochiKit.DOM.setNodeAttribute */
391 setNodeAttribute: function (node, attr, value) {
392 var o = {};
393 o[attr] = value;
394 try {
395 return MochiKit.DOM.updateNodeAttributes(node, o);
396 } catch (e) {
397 // pass
398 }
399 return null;
400 },
401
402 /** @id MochiKit.DOM.getNodeAttribute */
403 getNodeAttribute: function (node, attr) {
404 var self = MochiKit.DOM;
405 var rename = self.attributeArray.renames[attr];
406 node = self.getElement(node);
407 try {
408 if (rename) {
409 return node[rename];
410 }
411 return node.getAttribute(attr);
412 } catch (e) {
413 // pass
414 }
415 return null;
416 },
417
418 /** @id MochiKit.DOM.removeNodeAttribute */
419 removeNodeAttribute: function (node, attr) {
420 var self = MochiKit.DOM;
421 var rename = self.attributeArray.renames[attr];
422 node = self.getElement(node);
423 try {
424 if (rename) {
425 return node[rename];
426 }
427 return node.removeAttribute(attr);
428 } catch (e) {
429 // pass
430 }
431 return null;
432 },
433
434 /** @id MochiKit.DOM.updateNodeAttributes */
435 updateNodeAttributes: function (node, attrs) {
436 var elem = node;
437 var self = MochiKit.DOM;
438 if (typeof(node) == 'string') {
439 elem = self.getElement(node);
440 }
441 if (attrs) {
442 var updatetree = MochiKit.Base.updatetree;
443 if (self.attributeArray.compliant) {
444 // not IE, good.
445 for (var k in attrs) {
446 var v = attrs[k];
447 if (typeof(v) == 'object' && typeof(elem[k]) == 'object') {
448 if (k == "style" && MochiKit.Style) {
449 MochiKit.Style.setStyle(elem, v);
450 } else {
451 updatetree(elem[k], v);
452 }
453 } else if (k.substring(0, 2) == "on") {
454 if (typeof(v) == "string") {
455 v = new Function(v);
456 }
457 elem[k] = v;
458 } else {
459 elem.setAttribute(k, v);
460 }
461 }
462 } else {
463 // IE is insane in the membrane
464 var renames = self.attributeArray.renames;
465 for (var k in attrs) {
466 v = attrs[k];
467 var renamed = renames[k];
468 if (k == "style" && typeof(v) == "string") {
469 elem.style.cssText = v;
470 } else if (typeof(renamed) == "string") {
471 elem[renamed] = v;
472 } else if (typeof(elem[k]) == 'object'
473 && typeof(v) == 'object') {
474 if (k == "style" && MochiKit.Style) {
475 MochiKit.Style.setStyle(elem, v);
476 } else {
477 updatetree(elem[k], v);
478 }
479 } else if (k.substring(0, 2) == "on") {
480 if (typeof(v) == "string") {
481 v = new Function(v);
482 }
483 elem[k] = v;
484 } else {
485 elem.setAttribute(k, v);
486 }
487 }
488 }
489 }
490 return elem;
491 },
492
493 /** @id MochiKit.DOM.appendChildNodes */
494 appendChildNodes: function (node/*, nodes...*/) {
495 var elem = node;
496 var self = MochiKit.DOM;
497 if (typeof(node) == 'string') {
498 elem = self.getElement(node);
499 }
500 var nodeStack = [
501 self.coerceToDOM(
502 MochiKit.Base.extend(null, arguments, 1),
503 elem
504 )
505 ];
506 var concat = MochiKit.Base.concat;
507 while (nodeStack.length) {
508 var n = nodeStack.shift();
509 if (typeof(n) == 'undefined' || n === null) {
510 // pass
511 } else if (typeof(n.nodeType) == 'number') {
512 elem.appendChild(n);
513 } else {
514 nodeStack = concat(n, nodeStack);
515 }
516 }
517 return elem;
518 },
519
520
521 /** @id MochiKit.DOM.insertSiblingNodesBefore */
522 insertSiblingNodesBefore: function (node/*, nodes...*/) {
523 var elem = node;
524 var self = MochiKit.DOM;
525 if (typeof(node) == 'string') {
526 elem = self.getElement(node);
527 }
528 var nodeStack = [
529 self.coerceToDOM(
530 MochiKit.Base.extend(null, arguments, 1),
531 elem
532 )
533 ];
534 var parentnode = elem.parentNode;
535 var concat = MochiKit.Base.concat;
536 while (nodeStack.length) {
537 var n = nodeStack.shift();
538 if (typeof(n) == 'undefined' || n === null) {
539 // pass
540 } else if (typeof(n.nodeType) == 'number') {
541 parentnode.insertBefore(n, elem);
542 } else {
543 nodeStack = concat(n, nodeStack);
544 }
545 }
546 return parentnode;
547 },
548
549 /** @id MochiKit.DOM.insertSiblingNodesAfter */
550 insertSiblingNodesAfter: function (node/*, nodes...*/) {
551 var elem = node;
552 var self = MochiKit.DOM;
553
554 if (typeof(node) == 'string') {
555 elem = self.getElement(node);
556 }
557 var nodeStack = [
558 self.coerceToDOM(
559 MochiKit.Base.extend(null, arguments, 1),
560 elem
561 )
562 ];
563
564 if (elem.nextSibling) {
565 return self.insertSiblingNodesBefore(elem.nextSibling, nodeStack);
566 }
567 else {
568 return self.appendChildNodes(elem.parentNode, nodeStack);
569 }
570 },
571
572 /** @id MochiKit.DOM.replaceChildNodes */
573 replaceChildNodes: function (node/*, nodes...*/) {
574 var elem = node;
575 var self = MochiKit.DOM;
576 if (typeof(node) == 'string') {
577 elem = self.getElement(node);
578 arguments[0] = elem;
579 }
580 var child;
581 while ((child = elem.firstChild)) {
582 elem.removeChild(child);
583 }
584 if (arguments.length < 2) {
585 return elem;
586 } else {
587 return self.appendChildNodes.apply(this, arguments);
588 }
589 },
590
591 /** @id MochiKit.DOM.createDOM */
592 createDOM: function (name, attrs/*, nodes... */) {
593 var elem;
594 var self = MochiKit.DOM;
595 var m = MochiKit.Base;
596 if (typeof(attrs) == "string" || typeof(attrs) == "number") {
597 var args = m.extend([name, null], arguments, 1);
598 return arguments.callee.apply(this, args);
599 }
600 if (typeof(name) == 'string') {
601 // Internet Explorer is dumb
602 var xhtml = self._xhtml;
603 if (attrs && !self.attributeArray.compliant) {
604 // http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/name_2.asp
605 var contents = "";
606 if ('name' in attrs) {
607 contents += ' name="' + self.escapeHTML(attrs.name) + '"';
608 }
609 if (name == 'input' && 'type' in attrs) {
610 contents += ' type="' + self.escapeHTML(attrs.type) + '"';
611 }
612 if (contents) {
613 name = "<" + name + contents + ">";
614 xhtml = false;
615 }
616 }
617 var d = self._document;
618 if (xhtml && d === document) {
619 elem = d.createElementNS("http://www.w3.org/1999/xhtml", name);
620 } else {
621 elem = d.createElement(name);
622 }
623 } else {
624 elem = name;
625 }
626 if (attrs) {
627 self.updateNodeAttributes(elem, attrs);
628 }
629 if (arguments.length <= 2) {
630 return elem;
631 } else {
632 var args = m.extend([elem], arguments, 2);
633 return self.appendChildNodes.apply(this, args);
634 }
635 },
636
637 /** @id MochiKit.DOM.createDOMFunc */
638 createDOMFunc: function (/* tag, attrs, *nodes */) {
639 var m = MochiKit.Base;
640 return m.partial.apply(
641 this,
642 m.extend([MochiKit.DOM.createDOM], arguments)
643 );
644 },
645
646 /** @id MochiKit.DOM.removeElement */
647 removeElement: function (elem) {
648 var e = MochiKit.DOM.getElement(elem);
649 e.parentNode.removeChild(e);
650 return e;
651 },
652
653 /** @id MochiKit.DOM.swapDOM */
654 swapDOM: function (dest, src) {
655 var self = MochiKit.DOM;
656 dest = self.getElement(dest);
657 var parent = dest.parentNode;
658 if (src) {
659 src = self.getElement(src);
660 parent.replaceChild(src, dest);
661 } else {
662 parent.removeChild(dest);
663 }
664 return src;
665 },
666
667 /** @id MochiKit.DOM.getElement */
668 getElement: function (id) {
669 var self = MochiKit.DOM;
670 if (arguments.length == 1) {
671 return ((typeof(id) == "string") ?
672 self._document.getElementById(id) : id);
673 } else {
674 return MochiKit.Base.map(self.getElement, arguments);
675 }
676 },
677
678 /** @id MochiKit.DOM.getElementsByTagAndClassName */
679 getElementsByTagAndClassName: function (tagName, className,
680 /* optional */parent) {
681 var self = MochiKit.DOM;
682 if (typeof(tagName) == 'undefined' || tagName === null) {
683 tagName = '*';
684 }
685 if (typeof(parent) == 'undefined' || parent === null) {
686 parent = self._document;
687 }
688 parent = self.getElement(parent);
689 var children = (parent.getElementsByTagName(tagName)
690 || self._document.all);
691 if (typeof(className) == 'undefined' || className === null) {
692 return MochiKit.Base.extend(null, children);
693 }
694
695 var elements = [];
696 for (var i = 0; i < children.length; i++) {
697 var child = children[i];
698 var cls = child.className;
699 if (!cls) {
700 continue;
701 }
702 var classNames = cls.split(' ');
703 for (var j = 0; j < classNames.length; j++) {
704 if (classNames[j] == className) {
705 elements.push(child);
706 break;
707 }
708 }
709 }
710
711 return elements;
712 },
713
714 _newCallStack: function (path, once) {
715 var rval = function () {
716 var callStack = arguments.callee.callStack;
717 for (var i = 0; i < callStack.length; i++) {
718 if (callStack[i].apply(this, arguments) === false) {
719 break;
720 }
721 }
722 if (once) {
723 try {
724 this[path] = null;
725 } catch (e) {
726 // pass
727 }
728 }
729 };
730 rval.callStack = [];
731 return rval;
732 },
733
734 /** @id MochiKit.DOM.addToCallStack */
735 addToCallStack: function (target, path, func, once) {
736 var self = MochiKit.DOM;
737 var existing = target[path];
738 var regfunc = existing;
739 if (!(typeof(existing) == 'function'
740 && typeof(existing.callStack) == "object"
741 && existing.callStack !== null)) {
742 regfunc = self._newCallStack(path, once);
743 if (typeof(existing) == 'function') {
744 regfunc.callStack.push(existing);
745 }
746 target[path] = regfunc;
747 }
748 regfunc.callStack.push(func);
749 },
750
751 /** @id MochiKit.DOM.addLoadEvent */
752 addLoadEvent: function (func) {
753 var self = MochiKit.DOM;
754 self.addToCallStack(self._window, "onload", func, true);
755
756 },
757
758 /** @id MochiKit.DOM.focusOnLoad */
759 focusOnLoad: function (element) {
760 var self = MochiKit.DOM;
761 self.addLoadEvent(function () {
762 element = self.getElement(element);
763 if (element) {
764 element.focus();
765 }
766 });
767 },
768
769 /** @id MochiKit.DOM.setElementClass */
770 setElementClass: function (element, className) {
771 var self = MochiKit.DOM;
772 var obj = self.getElement(element);
773 if (self.attributeArray.compliant) {
774 obj.setAttribute("class", className);
775 } else {
776 obj.setAttribute("className", className);
777 }
778 },
779
780 /** @id MochiKit.DOM.toggleElementClass */
781 toggleElementClass: function (className/*, element... */) {
782 var self = MochiKit.DOM;
783 for (var i = 1; i < arguments.length; i++) {
784 var obj = self.getElement(arguments[i]);
785 if (!self.addElementClass(obj, className)) {
786 self.removeElementClass(obj, className);
787 }
788 }
789 },
790
791 /** @id MochiKit.DOM.addElementClass */
792 addElementClass: function (element, className) {
793 var self = MochiKit.DOM;
794 var obj = self.getElement(element);
795 var cls = obj.className;
796 // trivial case, no className yet
797 if (cls == undefined || cls.length === 0) {
798 self.setElementClass(obj, className);
799 return true;
800 }
801 // the other trivial case, already set as the only class
802 if (cls == className) {
803 return false;
804 }
805 var classes = cls.split(" ");
806 for (var i = 0; i < classes.length; i++) {
807 // already present
808 if (classes[i] == className) {
809 return false;
810 }
811 }
812 // append class
813 self.setElementClass(obj, cls + " " + className);
814 return true;
815 },
816
817 /** @id MochiKit.DOM.removeElementClass */
818 removeElementClass: function (element, className) {
819 var self = MochiKit.DOM;
820 var obj = self.getElement(element);
821 var cls = obj.className;
822 // trivial case, no className yet
823 if (cls == undefined || cls.length === 0) {
824 return false;
825 }
826 // other trivial case, set only to className
827 if (cls == className) {
828 self.setElementClass(obj, "");
829 return true;
830 }
831 var classes = cls.split(" ");
832 for (var i = 0; i < classes.length; i++) {
833 // already present
834 if (classes[i] == className) {
835 // only check sane case where the class is used once
836 classes.splice(i, 1);
837 self.setElementClass(obj, classes.join(" "));
838 return true;
839 }
840 }
841 // not found
842 return false;
843 },
844
845 /** @id MochiKit.DOM.swapElementClass */
846 swapElementClass: function (element, fromClass, toClass) {
847 var obj = MochiKit.DOM.getElement(element);
848 var res = MochiKit.DOM.removeElementClass(obj, fromClass);
849 if (res) {
850 MochiKit.DOM.addElementClass(obj, toClass);
851 }
852 return res;
853 },
854
855 /** @id MochiKit.DOM.hasElementClass */
856 hasElementClass: function (element, className/*...*/) {
857 var obj = MochiKit.DOM.getElement(element);
858 var cls = obj.className;
859 if (!cls) {
860 return false;
861 }
862 var classes = cls.split(" ");
863 for (var i = 1; i < arguments.length; i++) {
864 var good = false;
865 for (var j = 0; j < classes.length; j++) {
866 if (classes[j] == arguments[i]) {
867 good = true;
868 break;
869 }
870 }
871 if (!good) {
872 return false;
873 }
874 }
875 return true;
876 },
877
878 /** @id MochiKit.DOM.escapeHTML */
879 escapeHTML: function (s) {
880 return s.replace(/&/g, "&amp;"
881 ).replace(/"/g, "&quot;"
882 ).replace(/</g, "&lt;"
883 ).replace(/>/g, "&gt;");
884 },
885
886 /** @id MochiKit.DOM.toHTML */
887 toHTML: function (dom) {
888 return MochiKit.DOM.emitHTML(dom).join("");
889 },
890
891 /** @id MochiKit.DOM.emitHTML */
892 emitHTML: function (dom, /* optional */lst) {
893 if (typeof(lst) == 'undefined' || lst === null) {
894 lst = [];
895 }
896 // queue is the call stack, we're doing this non-recursively
897 var queue = [dom];
898 var self = MochiKit.DOM;
899 var escapeHTML = self.escapeHTML;
900 var attributeArray = self.attributeArray;
901 while (queue.length) {
902 dom = queue.pop();
903 if (typeof(dom) == 'string') {
904 lst.push(dom);
905 } else if (dom.nodeType == 1) {
906 // we're not using higher order stuff here
907 // because safari has heisenbugs.. argh.
908 //
909 // I think it might have something to do with
910 // garbage collection and function calls.
911 lst.push('<' + dom.tagName.toLowerCase());
912 var attributes = [];
913 var domAttr = attributeArray(dom);
914 for (var i = 0; i < domAttr.length; i++) {
915 var a = domAttr[i];
916 attributes.push([
917 " ",
918 a.name,
919 '="',
920 escapeHTML(a.value),
921 '"'
922 ]);
923 }
924 attributes.sort();
925 for (i = 0; i < attributes.length; i++) {
926 var attrs = attributes[i];
927 for (var j = 0; j < attrs.length; j++) {
928 lst.push(attrs[j]);
929 }
930 }
931 if (dom.hasChildNodes()) {
932 lst.push(">");
933 // queue is the FILO call stack, so we put the close tag
934 // on first
935 queue.push("</" + dom.tagName.toLowerCase() + ">");
936 var cnodes = dom.childNodes;
937 for (i = cnodes.length - 1; i >= 0; i--) {
938 queue.push(cnodes[i]);
939 }
940 } else {
941 lst.push('/>');
942 }
943 } else if (dom.nodeType == 3) {
944 lst.push(escapeHTML(dom.nodeValue));
945 }
946 }
947 return lst;
948 },
949
950 /** @id MochiKit.DOM.scrapeText */
951 scrapeText: function (node, /* optional */asArray) {
952 var rval = [];
953 (function (node) {
954 var cn = node.childNodes;
955 if (cn) {
956 for (var i = 0; i < cn.length; i++) {
957 arguments.callee.call(this, cn[i]);
958 }
959 }
960 var nodeValue = node.nodeValue;
961 if (typeof(nodeValue) == 'string') {
962 rval.push(nodeValue);
963 }
964 })(MochiKit.DOM.getElement(node));
965 if (asArray) {
966 return rval;
967 } else {
968 return rval.join("");
969 }
970 },
971
972 /** @id MochiKit.DOM.removeEmptyTextNodes */
973 removeEmptyTextNodes: function (element) {
974 element = MochiKit.DOM.getElement(element);
975 for (var i = 0; i < element.childNodes.length; i++) {
976 var node = element.childNodes[i];
977 if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) {
978 node.parentNode.removeChild(node);
979 }
980 }
981 },
982
983 /** @id MochiKit.DOM.makeClipping */
984 makeClipping: function (element) {
985 element = MochiKit.DOM.getElement(element);
986 var oldOverflow = element.style.overflow;
987 if ((MochiKit.Style.getStyle(element, 'overflow') || 'visible') != 'hidden') {
988 element.style.overflow = 'hidden';
989 }
990 return oldOverflow;
991 },
992
993 /** @id MochiKit.DOM.undoClipping */
994 undoClipping: function (element, overflow) {
995 element = MochiKit.DOM.getElement(element);
996 if (!overflow) {
997 return;
998 }
999 element.style.overflow = overflow;
1000 },
1001
1002 /** @id MochiKit.DOM.makePositioned */
1003 makePositioned: function (element) {
1004 element = MochiKit.DOM.getElement(element);
1005 var pos = MochiKit.Style.getStyle(element, 'position');
1006 if (pos == 'static' || !pos) {
1007 element.style.position = 'relative';
1008 // Opera returns the offset relative to the positioning context,
1009 // when an element is position relative but top and left have
1010 // not been defined
1011 if (/Opera/.test(navigator.userAgent)) {
1012 element.style.top = 0;
1013 element.style.left = 0;
1014 }
1015 }
1016 },
1017
1018 /** @id MochiKit.DOM.undoPositioned */
1019 undoPositioned: function (element) {
1020 element = MochiKit.DOM.getElement(element);
1021 if (element.style.position == 'relative') {
1022 element.style.position = element.style.top = element.style.left = element.style.bottom = element.style.right = '';
1023 }
1024 },
1025
1026 /** @id MochiKit.DOM.getFirstElementByTagAndClassName */
1027 getFirstElementByTagAndClassName: function (tagName, className,
1028 /* optional */parent) {
1029 var self = MochiKit.DOM;
1030 if (typeof(tagName) == 'undefined' || tagName === null) {
1031 tagName = '*';
1032 }
1033 if (typeof(parent) == 'undefined' || parent === null) {
1034 parent = self._document;
1035 }
1036 parent = self.getElement(parent);
1037 var children = (parent.getElementsByTagName(tagName)
1038 || self._document.all);
1039 if (typeof(className) == 'undefined' || className === null) {
1040 return children[0];
1041 }
1042
1043 for (var i = 0; i < children.length; i++) {
1044 var child = children[i];
1045 var classNames = child.className.split(' ');
1046 for (var j = 0; j < classNames.length; j++) {
1047 if (classNames[j] == className) {
1048 return child;
1049 }
1050 }
1051 }
1052 },
1053
1054 /** @id MochiKit.DOM.getFirstParentByTagAndClassName */
1055 getFirstParentByTagAndClassName: function (elem, tagName, className) {
1056 var self = MochiKit.DOM;
1057 elem = self.getElement(elem);
1058 if (typeof(tagName) == 'undefined' || tagName === null) {
1059 tagName = '*';
1060 } else {
1061 tagName = tagName.toUpperCase();
1062 }
1063 if (typeof(className) == 'undefined' || className === null) {
1064 className = null;
1065 }
1066
1067 var classList = '';
1068 var curTagName = '';
1069 while (elem && elem.tagName) {
1070 elem = elem.parentNode;
1071 if (tagName == '*' && className === null) {
1072 return elem;
1073 }
1074 classList = elem.className.split(' ');
1075 curTagName = elem.tagName.toUpperCase();
1076 if (className === null && tagName == curTagName) {
1077 return elem;
1078 } else if (className !== null) {
1079 for (var i = 0; i < classList.length; i++) {
1080 if (tagName == '*' && classList[i] == className) {
1081 return elem;
1082 } else if (tagName == curTagName && classList[i] == className) {
1083 return elem;
1084 }
1085 }
1086 }
1087 }
1088 return elem;
1089 },
1090
1091 /** @id MochiKit.DOM.isParent */
1092 isParent: function (child, element) {
1093 if (!child.parentNode || child == element) {
1094 return false;
1095 }
1096
1097 if (child.parentNode == element) {
1098 return true;
1099 }
1100
1101 return MochiKit.DOM.isParent(child.parentNode, element);
1102 },
1103
1104 __new__: function (win) {
1105
1106 var m = MochiKit.Base;
1107 if (typeof(document) != "undefined") {
1108 this._document = document;
1109 var kXULNSURI = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
1110 this._xhtml = (document.documentElement &&
1111 document.createElementNS &&
1112 document.documentElement.namespaceURI === kXULNSURI);
1113 } else if (MochiKit.MockDOM) {
1114 this._document = MochiKit.MockDOM.document;
1115 }
1116 this._window = win;
1117
1118 this.domConverters = new m.AdapterRegistry();
1119
1120 var __tmpElement = this._document.createElement("span");
1121 var attributeArray;
1122 if (__tmpElement && __tmpElement.attributes &&
1123 __tmpElement.attributes.length > 0) {
1124 // for braindead browsers (IE) that insert extra junk
1125 var filter = m.filter;
1126 attributeArray = function (node) {
1127 return filter(attributeArray.ignoreAttrFilter, node.attributes);
1128 };
1129 attributeArray.ignoreAttr = {};
1130 var attrs = __tmpElement.attributes;
1131 var ignoreAttr = attributeArray.ignoreAttr;
1132 for (var i = 0; i < attrs.length; i++) {
1133 var a = attrs[i];
1134 ignoreAttr[a.name] = a.value;
1135 }
1136 attributeArray.ignoreAttrFilter = function (a) {
1137 return (attributeArray.ignoreAttr[a.name] != a.value);
1138 };
1139 attributeArray.compliant = false;
1140 attributeArray.renames = {
1141 "class": "className",
1142 "checked": "defaultChecked",
1143 "usemap": "useMap",
1144 "for": "htmlFor",
1145 "readonly": "readOnly",
1146 "colspan": "colSpan",
1147 "bgcolor": "bgColor",
1148 "cellspacing": "cellSpacing",
1149 "cellpadding": "cellPadding"
1150 };
1151 } else {
1152 attributeArray = function (node) {
1153 /***
1154
1155 Return an array of attributes for a given node,
1156 filtering out attributes that don't belong for
1157 that are inserted by "Certain Browsers".
1158
1159 ***/
1160 return node.attributes;
1161 };
1162 attributeArray.compliant = true;
1163 attributeArray.renames = {};
1164 }
1165 this.attributeArray = attributeArray;
1166
1167 // FIXME: this really belongs in Base, and could probably be cleaner
1168 var _deprecated = function(fromModule, arr) {
1169 var modules = arr[1].split('.');
1170 var str = '';
1171 var obj = {};
1172
1173 str += 'if (!MochiKit.' + modules[1] + ') { throw new Error("';
1174 str += 'This function has been deprecated and depends on MochiKit.';
1175 str += modules[1] + '.");}';
1176 str += 'return MochiKit.' + modules[1] + '.' + arr[0];
1177 str += '.apply(this, arguments);';
1178
1179 obj[modules[2]] = new Function(str);
1180 MochiKit.Base.update(MochiKit[fromModule], obj);
1181 }
1182 for (var i; i < MochiKit.DOM.DEPRECATED.length; i++) {
1183 _deprecated('DOM', MochiKit.DOM.DEPRECATED[i]);
1184 }
1185
1186 // shorthand for createDOM syntax
1187 var createDOMFunc = this.createDOMFunc;
1188 /** @id MochiKit.DOM.UL */
1189 this.UL = createDOMFunc("ul");
1190 /** @id MochiKit.DOM.OL */
1191 this.OL = createDOMFunc("ol");
1192 /** @id MochiKit.DOM.LI */
1193 this.LI = createDOMFunc("li");
1194 /** @id MochiKit.DOM.TD */
1195 this.TD = createDOMFunc("td");
1196 /** @id MochiKit.DOM.TR */
1197 this.TR = createDOMFunc("tr");
1198 /** @id MochiKit.DOM.TBODY */
1199 this.TBODY = createDOMFunc("tbody");
1200 /** @id MochiKit.DOM.THEAD */
1201 this.THEAD = createDOMFunc("thead");
1202 /** @id MochiKit.DOM.TFOOT */
1203 this.TFOOT = createDOMFunc("tfoot");
1204 /** @id MochiKit.DOM.TABLE */
1205 this.TABLE = createDOMFunc("table");
1206 /** @id MochiKit.DOM.TH */
1207 this.TH = createDOMFunc("th");
1208 /** @id MochiKit.DOM.INPUT */
1209 this.INPUT = createDOMFunc("input");
1210 /** @id MochiKit.DOM.SPAN */
1211 this.SPAN = createDOMFunc("span");
1212 /** @id MochiKit.DOM.A */
1213 this.A = createDOMFunc("a");
1214 /** @id MochiKit.DOM.DIV */
1215 this.DIV = createDOMFunc("div");
1216 /** @id MochiKit.DOM.IMG */
1217 this.IMG = createDOMFunc("img");
1218 /** @id MochiKit.DOM.BUTTON */
1219 this.BUTTON = createDOMFunc("button");
1220 /** @id MochiKit.DOM.TT */
1221 this.TT = createDOMFunc("tt");
1222 /** @id MochiKit.DOM.PRE */
1223 this.PRE = createDOMFunc("pre");
1224 /** @id MochiKit.DOM.H1 */
1225 this.H1 = createDOMFunc("h1");
1226 /** @id MochiKit.DOM.H2 */
1227 this.H2 = createDOMFunc("h2");
1228 /** @id MochiKit.DOM.H3 */
1229 this.H3 = createDOMFunc("h3");
1230 /** @id MochiKit.DOM.BR */
1231 this.BR = createDOMFunc("br");
1232 /** @id MochiKit.DOM.HR */
1233 this.HR = createDOMFunc("hr");
1234 /** @id MochiKit.DOM.LABEL */
1235 this.LABEL = createDOMFunc("label");
1236 /** @id MochiKit.DOM.TEXTAREA */
1237 this.TEXTAREA = createDOMFunc("textarea");
1238 /** @id MochiKit.DOM.FORM */
1239 this.FORM = createDOMFunc("form");
1240 /** @id MochiKit.DOM.P */
1241 this.P = createDOMFunc("p");
1242 /** @id MochiKit.DOM.SELECT */
1243 this.SELECT = createDOMFunc("select");
1244 /** @id MochiKit.DOM.OPTION */
1245 this.OPTION = createDOMFunc("option");
1246 /** @id MochiKit.DOM.OPTGROUP */
1247 this.OPTGROUP = createDOMFunc("optgroup");
1248 /** @id MochiKit.DOM.LEGEND */
1249 this.LEGEND = createDOMFunc("legend");
1250 /** @id MochiKit.DOM.FIELDSET */
1251 this.FIELDSET = createDOMFunc("fieldset");
1252 /** @id MochiKit.DOM.STRONG */
1253 this.STRONG = createDOMFunc("strong");
1254 /** @id MochiKit.DOM.CANVAS */
1255 this.CANVAS = createDOMFunc("canvas");
1256
1257 /** @id MochiKit.DOM.$ */
1258 this.$ = this.getElement;
1259
1260 this.EXPORT_TAGS = {
1261 ":common": this.EXPORT,
1262 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
1263 };
1264
1265 m.nameFunctions(this);
1266
1267 }
1268 });
1269
1270
1271 MochiKit.DOM.__new__(((typeof(window) == "undefined") ? this : window));
1272
1273 //
1274 // XXX: Internet Explorer blows
1275 //
1276 if (MochiKit.__export__) {
1277 withWindow = MochiKit.DOM.withWindow;
1278 withDocument = MochiKit.DOM.withDocument;
1279 }
1280
1281 MochiKit.Base._exportSymbols(this, MochiKit.DOM);
@@ -0,0 +1,216 b''
1 /***
2
3 MochiKit.DateTime 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005 Bob Ippolito. All rights Reserved.
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide('MochiKit.DateTime');
13 }
14
15 if (typeof(MochiKit) == 'undefined') {
16 MochiKit = {};
17 }
18
19 if (typeof(MochiKit.DateTime) == 'undefined') {
20 MochiKit.DateTime = {};
21 }
22
23 MochiKit.DateTime.NAME = "MochiKit.DateTime";
24 MochiKit.DateTime.VERSION = "1.4";
25 MochiKit.DateTime.__repr__ = function () {
26 return "[" + this.NAME + " " + this.VERSION + "]";
27 };
28 MochiKit.DateTime.toString = function () {
29 return this.__repr__();
30 };
31
32 /** @id MochiKit.DateTime.isoDate */
33 MochiKit.DateTime.isoDate = function (str) {
34 str = str + "";
35 if (typeof(str) != "string" || str.length === 0) {
36 return null;
37 }
38 var iso = str.split('-');
39 if (iso.length === 0) {
40 return null;
41 }
42 return new Date(iso[0], iso[1] - 1, iso[2]);
43 };
44
45 MochiKit.DateTime._isoRegexp = /(\d{4,})(?:-(\d{1,2})(?:-(\d{1,2})(?:[T ](\d{1,2}):(\d{1,2})(?::(\d{1,2})(?:\.(\d+))?)?(?:(Z)|([+-])(\d{1,2})(?::(\d{1,2}))?)?)?)?)?/;
46
47 /** @id MochiKit.DateTime.isoTimestamp */
48 MochiKit.DateTime.isoTimestamp = function (str) {
49 str = str + "";
50 if (typeof(str) != "string" || str.length === 0) {
51 return null;
52 }
53 var res = str.match(MochiKit.DateTime._isoRegexp);
54 if (typeof(res) == "undefined" || res === null) {
55 return null;
56 }
57 var year, month, day, hour, min, sec, msec;
58 year = parseInt(res[1], 10);
59 if (typeof(res[2]) == "undefined" || res[2] === '') {
60 return new Date(year);
61 }
62 month = parseInt(res[2], 10) - 1;
63 day = parseInt(res[3], 10);
64 if (typeof(res[4]) == "undefined" || res[4] === '') {
65 return new Date(year, month, day);
66 }
67 hour = parseInt(res[4], 10);
68 min = parseInt(res[5], 10);
69 sec = (typeof(res[6]) != "undefined" && res[6] !== '') ? parseInt(res[6], 10) : 0;
70 if (typeof(res[7]) != "undefined" && res[7] !== '') {
71 msec = Math.round(1000.0 * parseFloat("0." + res[7]));
72 } else {
73 msec = 0;
74 }
75 if ((typeof(res[8]) == "undefined" || res[8] === '') && (typeof(res[9]) == "undefined" || res[9] === '')) {
76 return new Date(year, month, day, hour, min, sec, msec);
77 }
78 var ofs;
79 if (typeof(res[9]) != "undefined" && res[9] !== '') {
80 ofs = parseInt(res[10], 10) * 3600000;
81 if (typeof(res[11]) != "undefined" && res[11] !== '') {
82 ofs += parseInt(res[11], 10) * 60000;
83 }
84 if (res[9] == "-") {
85 ofs = -ofs;
86 }
87 } else {
88 ofs = 0;
89 }
90 return new Date(Date.UTC(year, month, day, hour, min, sec, msec) - ofs);
91 };
92
93 /** @id MochiKit.DateTime.toISOTime */
94 MochiKit.DateTime.toISOTime = function (date, realISO/* = false */) {
95 if (typeof(date) == "undefined" || date === null) {
96 return null;
97 }
98 var hh = date.getHours();
99 var mm = date.getMinutes();
100 var ss = date.getSeconds();
101 var lst = [
102 ((realISO && (hh < 10)) ? "0" + hh : hh),
103 ((mm < 10) ? "0" + mm : mm),
104 ((ss < 10) ? "0" + ss : ss)
105 ];
106 return lst.join(":");
107 };
108
109 /** @id MochiKit.DateTime.toISOTimeStamp */
110 MochiKit.DateTime.toISOTimestamp = function (date, realISO/* = false*/) {
111 if (typeof(date) == "undefined" || date === null) {
112 return null;
113 }
114 var sep = realISO ? "T" : " ";
115 var foot = realISO ? "Z" : "";
116 if (realISO) {
117 date = new Date(date.getTime() + (date.getTimezoneOffset() * 60000));
118 }
119 return MochiKit.DateTime.toISODate(date) + sep + MochiKit.DateTime.toISOTime(date, realISO) + foot;
120 };
121
122 /** @id MochiKit.DateTime.toISODate */
123 MochiKit.DateTime.toISODate = function (date) {
124 if (typeof(date) == "undefined" || date === null) {
125 return null;
126 }
127 var _padTwo = MochiKit.DateTime._padTwo;
128 return [
129 date.getFullYear(),
130 _padTwo(date.getMonth() + 1),
131 _padTwo(date.getDate())
132 ].join("-");
133 };
134
135 /** @id MochiKit.DateTime.americanDate */
136 MochiKit.DateTime.americanDate = function (d) {
137 d = d + "";
138 if (typeof(d) != "string" || d.length === 0) {
139 return null;
140 }
141 var a = d.split('/');
142 return new Date(a[2], a[0] - 1, a[1]);
143 };
144
145 MochiKit.DateTime._padTwo = function (n) {
146 return (n > 9) ? n : "0" + n;
147 };
148
149 /** @id MochiKit.DateTime.toPaddedAmericanDate */
150 MochiKit.DateTime.toPaddedAmericanDate = function (d) {
151 if (typeof(d) == "undefined" || d === null) {
152 return null;
153 }
154 var _padTwo = MochiKit.DateTime._padTwo;
155 return [
156 _padTwo(d.getMonth() + 1),
157 _padTwo(d.getDate()),
158 d.getFullYear()
159 ].join('/');
160 };
161
162 /** @id MochiKit.DateTime.toAmericanDate */
163 MochiKit.DateTime.toAmericanDate = function (d) {
164 if (typeof(d) == "undefined" || d === null) {
165 return null;
166 }
167 return [d.getMonth() + 1, d.getDate(), d.getFullYear()].join('/');
168 };
169
170 MochiKit.DateTime.EXPORT = [
171 "isoDate",
172 "isoTimestamp",
173 "toISOTime",
174 "toISOTimestamp",
175 "toISODate",
176 "americanDate",
177 "toPaddedAmericanDate",
178 "toAmericanDate"
179 ];
180
181 MochiKit.DateTime.EXPORT_OK = [];
182 MochiKit.DateTime.EXPORT_TAGS = {
183 ":common": MochiKit.DateTime.EXPORT,
184 ":all": MochiKit.DateTime.EXPORT
185 };
186
187 MochiKit.DateTime.__new__ = function () {
188 // MochiKit.Base.nameFunctions(this);
189 var base = this.NAME + ".";
190 for (var k in this) {
191 var o = this[k];
192 if (typeof(o) == 'function' && typeof(o.NAME) == 'undefined') {
193 try {
194 o.NAME = base + k;
195 } catch (e) {
196 // pass
197 }
198 }
199 }
200 };
201
202 MochiKit.DateTime.__new__();
203
204 if (typeof(MochiKit.Base) != "undefined") {
205 MochiKit.Base._exportSymbols(this, MochiKit.DateTime);
206 } else {
207 (function (globals, module) {
208 if ((typeof(JSAN) == 'undefined' && typeof(dojo) == 'undefined')
209 || (MochiKit.__export__ === false)) {
210 var all = module.EXPORT_TAGS[":all"];
211 for (var i = 0; i < all.length; i++) {
212 globals[all[i]] = module[all[i]];
213 }
214 }
215 })(this, MochiKit.DateTime);
216 }
This diff has been collapsed as it changes many lines, (824 lines changed) Show them Hide them
@@ -0,0 +1,824 b''
1 /***
2 MochiKit.DragAndDrop 1.4
3
4 See <http://mochikit.com/> for documentation, downloads, license, etc.
5
6 Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
7 Mochi-ized By Thomas Herve (_firstname_@nimail.org)
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide('MochiKit.DragAndDrop');
13 dojo.require('MochiKit.Base');
14 dojo.require('MochiKit.DOM');
15 dojo.require('MochiKit.Iter');
16 dojo.require('MochiKit.Visual');
17 dojo.require('MochiKit.Signal');
18 }
19
20 if (typeof(JSAN) != 'undefined') {
21 JSAN.use("MochiKit.Base", []);
22 JSAN.use("MochiKit.DOM", []);
23 JSAN.use("MochiKit.Visual", []);
24 JSAN.use("MochiKit.Iter", []);
25 JSAN.use("MochiKit.Signal", []);
26 }
27
28 try {
29 if (typeof(MochiKit.Base) == 'undefined' ||
30 typeof(MochiKit.DOM) == 'undefined' ||
31 typeof(MochiKit.Visual) == 'undefined' ||
32 typeof(MochiKit.Signal) == 'undefined' ||
33 typeof(MochiKit.Iter) == 'undefined') {
34 throw "";
35 }
36 } catch (e) {
37 throw "MochiKit.DragAndDrop depends on MochiKit.Base, MochiKit.DOM, MochiKit.Visual, MochiKit.Signal and MochiKit.Iter!";
38 }
39
40 if (typeof(MochiKit.DragAndDrop) == 'undefined') {
41 MochiKit.DragAndDrop = {};
42 }
43
44 MochiKit.DragAndDrop.NAME = 'MochiKit.DragAndDrop';
45 MochiKit.DragAndDrop.VERSION = '1.4';
46
47 MochiKit.DragAndDrop.__repr__ = function () {
48 return '[' + this.NAME + ' ' + this.VERSION + ']';
49 };
50
51 MochiKit.DragAndDrop.toString = function () {
52 return this.__repr__();
53 };
54
55 MochiKit.DragAndDrop.EXPORT = [
56 "Droppable",
57 "Draggable"
58 ];
59
60 MochiKit.DragAndDrop.EXPORT_OK = [
61 "Droppables",
62 "Draggables"
63 ];
64
65 MochiKit.DragAndDrop.Droppables = {
66 /***
67
68 Manage all droppables. Shouldn't be used, use the Droppable object instead.
69
70 ***/
71 drops: [],
72
73 remove: function (element) {
74 this.drops = MochiKit.Base.filter(function (d) {
75 return d.element != MochiKit.DOM.getElement(element);
76 }, this.drops);
77 },
78
79 register: function (drop) {
80 this.drops.push(drop);
81 },
82
83 unregister: function (drop) {
84 this.drops = MochiKit.Base.filter(function (d) {
85 return d != drop;
86 }, this.drops);
87 },
88
89 prepare: function (element) {
90 MochiKit.Base.map(function (drop) {
91 if (drop.isAccepted(element)) {
92 if (drop.options.activeclass) {
93 MochiKit.DOM.addElementClass(drop.element,
94 drop.options.activeclass);
95 }
96 drop.options.onactive(drop.element, element);
97 }
98 }, this.drops);
99 },
100
101 findDeepestChild: function (drops) {
102 deepest = drops[0];
103
104 for (i = 1; i < drops.length; ++i) {
105 if (MochiKit.DOM.isParent(drops[i].element, deepest.element)) {
106 deepest = drops[i];
107 }
108 }
109 return deepest;
110 },
111
112 show: function (point, element) {
113 if (!this.drops.length) {
114 return;
115 }
116 var affected = [];
117
118 if (this.last_active) {
119 this.last_active.deactivate();
120 }
121 MochiKit.Iter.forEach(this.drops, function (drop) {
122 if (drop.isAffected(point, element)) {
123 affected.push(drop);
124 }
125 });
126 if (affected.length > 0) {
127 drop = this.findDeepestChild(affected);
128 MochiKit.Position.within(drop.element, point.page.x, point.page.y);
129 drop.options.onhover(element, drop.element,
130 MochiKit.Position.overlap(drop.options.overlap, drop.element));
131 drop.activate();
132 }
133 },
134
135 fire: function (event, element) {
136 if (!this.last_active) {
137 return;
138 }
139 MochiKit.Position.prepare();
140
141 if (this.last_active.isAffected(event.mouse(), element)) {
142 this.last_active.options.ondrop(element,
143 this.last_active.element, event);
144 }
145 },
146
147 reset: function (element) {
148 MochiKit.Base.map(function (drop) {
149 if (drop.options.activeclass) {
150 MochiKit.DOM.removeElementClass(drop.element,
151 drop.options.activeclass);
152 }
153 drop.options.ondesactive(drop.element, element);
154 }, this.drops);
155 if (this.last_active) {
156 this.last_active.deactivate();
157 }
158 }
159 };
160
161 /** @id MochiKit.DragAndDrop.Droppable */
162 MochiKit.DragAndDrop.Droppable = function (element, options) {
163 var cls = arguments.callee;
164 if (!(this instanceof cls)) {
165 return new cls(element, options);
166 }
167 this.__init__(element, options);
168 };
169
170 MochiKit.DragAndDrop.Droppable.prototype = {
171 /***
172
173 A droppable object. Simple use is to create giving an element:
174
175 new MochiKit.DragAndDrop.Droppable('myelement');
176
177 Generally you'll want to define the 'ondrop' function and maybe the
178 'accept' option to filter draggables.
179
180 ***/
181 __class__: MochiKit.DragAndDrop.Droppable,
182
183 __init__: function (element, /* optional */options) {
184 var d = MochiKit.DOM;
185 var b = MochiKit.Base;
186 this.element = d.getElement(element);
187 this.options = b.update({
188
189 /** @id MochiKit.DragAndDrop.greedy */
190 greedy: true,
191
192 /** @id MochiKit.DragAndDrop.hoverclass */
193 hoverclass: null,
194
195 /** @id MochiKit.DragAndDrop.activeclass */
196 activeclass: null,
197
198 /** @id MochiKit.DragAndDrop.hoverfunc */
199 hoverfunc: b.noop,
200
201 /** @id MochiKit.DragAndDrop.accept */
202 accept: null,
203
204 /** @id MochiKit.DragAndDrop.onactive */
205 onactive: b.noop,
206
207 /** @id MochiKit.DragAndDrop.ondesactive */
208 ondesactive: b.noop,
209
210 /** @id MochiKit.DragAndDrop.onhover */
211 onhover: b.noop,
212
213 /** @id MochiKit.DragAndDrop.ondrop */
214 ondrop: b.noop,
215
216 /** @id MochiKit.DragAndDrop.containment */
217 containment: [],
218 tree: false
219 }, options);
220
221 // cache containers
222 this.options._containers = [];
223 b.map(MochiKit.Base.bind(function (c) {
224 this.options._containers.push(d.getElement(c));
225 }, this), this.options.containment);
226
227 d.makePositioned(this.element); // fix IE
228
229 MochiKit.DragAndDrop.Droppables.register(this);
230 },
231
232 /** @id MochiKit.DragAndDrop.isContained */
233 isContained: function (element) {
234 if (this.options._containers.length) {
235 var containmentNode;
236 if (this.options.tree) {
237 containmentNode = element.treeNode;
238 } else {
239 containmentNode = element.parentNode;
240 }
241 return MochiKit.Iter.some(this.options._containers, function (c) {
242 return containmentNode == c;
243 });
244 } else {
245 return true;
246 }
247 },
248
249 /** @id MochiKit.DragAndDrop.isAccepted */
250 isAccepted: function (element) {
251 return ((!this.options.accept) || MochiKit.Iter.some(
252 this.options.accept, function (c) {
253 return MochiKit.DOM.hasElementClass(element, c);
254 }));
255 },
256
257 /** @id MochiKit.DragAndDrop.isAffected */
258 isAffected: function (point, element) {
259 return ((this.element != element) &&
260 this.isContained(element) &&
261 this.isAccepted(element) &&
262 MochiKit.Position.within(this.element, point.page.x,
263 point.page.y));
264 },
265
266 /** @id MochiKit.DragAndDrop.deactivate */
267 deactivate: function () {
268 /***
269
270 A droppable is deactivate when a draggable has been over it and left.
271
272 ***/
273 if (this.options.hoverclass) {
274 MochiKit.DOM.removeElementClass(this.element,
275 this.options.hoverclass);
276 }
277 this.options.hoverfunc(this.element, false);
278 MochiKit.DragAndDrop.Droppables.last_active = null;
279 },
280
281 /** @id MochiKit.DragAndDrop.activate */
282 activate: function () {
283 /***
284
285 A droppable is active when a draggable is over it.
286
287 ***/
288 if (this.options.hoverclass) {
289 MochiKit.DOM.addElementClass(this.element, this.options.hoverclass);
290 }
291 this.options.hoverfunc(this.element, true);
292 MochiKit.DragAndDrop.Droppables.last_active = this;
293 },
294
295 /** @id MochiKit.DragAndDrop.destroy */
296 destroy: function () {
297 /***
298
299 Delete this droppable.
300
301 ***/
302 MochiKit.DragAndDrop.Droppables.unregister(this);
303 },
304
305 /** @id MochiKit.DragAndDrop.repr */
306 repr: function () {
307 return '[' + this.__class__.NAME + ", options:" + MochiKit.Base.repr(this.options) + "]";
308 }
309 };
310
311 MochiKit.DragAndDrop.Draggables = {
312 /***
313
314 Manage draggables elements. Not intended to direct use.
315
316 ***/
317 drags: [],
318
319 register: function (draggable) {
320 if (this.drags.length === 0) {
321 var conn = MochiKit.Signal.connect;
322 this.eventMouseUp = conn(document, 'onmouseup', this, this.endDrag);
323 this.eventMouseMove = conn(document, 'onmousemove', this,
324 this.updateDrag);
325 this.eventKeypress = conn(document, 'onkeypress', this,
326 this.keyPress);
327 }
328 this.drags.push(draggable);
329 },
330
331 unregister: function (draggable) {
332 this.drags = MochiKit.Base.filter(function (d) {
333 return d != draggable;
334 }, this.drags);
335 if (this.drags.length === 0) {
336 var disc = MochiKit.Signal.disconnect;
337 disc(this.eventMouseUp);
338 disc(this.eventMouseMove);
339 disc(this.eventKeypress);
340 }
341 },
342
343 activate: function (draggable) {
344 // allows keypress events if window is not currently focused
345 // fails for Safari
346 window.focus();
347 this.activeDraggable = draggable;
348 },
349
350 deactivate: function () {
351 this.activeDraggable = null;
352 },
353
354 updateDrag: function (event) {
355 if (!this.activeDraggable) {
356 return;
357 }
358 var pointer = event.mouse();
359 // Mozilla-based browsers fire successive mousemove events with
360 // the same coordinates, prevent needless redrawing (moz bug?)
361 if (this._lastPointer && (MochiKit.Base.repr(this._lastPointer.page) ==
362 MochiKit.Base.repr(pointer.page))) {
363 return;
364 }
365 this._lastPointer = pointer;
366 this.activeDraggable.updateDrag(event, pointer);
367 },
368
369 endDrag: function (event) {
370 if (!this.activeDraggable) {
371 return;
372 }
373 this._lastPointer = null;
374 this.activeDraggable.endDrag(event);
375 this.activeDraggable = null;
376 },
377
378 keyPress: function (event) {
379 if (this.activeDraggable) {
380 this.activeDraggable.keyPress(event);
381 }
382 },
383
384 notify: function (eventName, draggable, event) {
385 MochiKit.Signal.signal(this, eventName, draggable, event);
386 }
387 };
388
389 /** @id MochiKit.DragAndDrop.Draggable */
390 MochiKit.DragAndDrop.Draggable = function (element, options) {
391 var cls = arguments.callee;
392 if (!(this instanceof cls)) {
393 return new cls(element, options);
394 }
395 this.__init__(element, options);
396 };
397
398 MochiKit.DragAndDrop.Draggable.prototype = {
399 /***
400
401 A draggable object. Simple instantiate :
402
403 new MochiKit.DragAndDrop.Draggable('myelement');
404
405 ***/
406 __class__ : MochiKit.DragAndDrop.Draggable,
407
408 __init__: function (element, /* optional */options) {
409 var v = MochiKit.Visual;
410 var b = MochiKit.Base;
411 options = b.update({
412
413 /** @id MochiKit.DragAndDrop.handle */
414 handle: false,
415
416 /** @id MochiKit.DragAndDrop.starteffect */
417 starteffect: function (innerelement) {
418 this._savedOpacity = MochiKit.Style.getStyle(innerelement, 'opacity') || 1.0;
419 new v.Opacity(innerelement, {duration:0.2, from:this._savedOpacity, to:0.7});
420 },
421 /** @id MochiKit.DragAndDrop.reverteffect */
422 reverteffect: function (innerelement, top_offset, left_offset) {
423 var dur = Math.sqrt(Math.abs(top_offset^2) +
424 Math.abs(left_offset^2))*0.02;
425 return new v.Move(innerelement,
426 {x: -left_offset, y: -top_offset, duration: dur});
427 },
428
429 /** @id MochiKit.DragAndDrop.endeffect */
430 endeffect: function (innerelement) {
431 new v.Opacity(innerelement, {duration:0.2, from:0.7, to:this._savedOpacity});
432 },
433
434 /** @id MochiKit.DragAndDrop.onchange */
435 onchange: b.noop,
436
437 /** @id MochiKit.DragAndDrop.zindex */
438 zindex: 1000,
439
440 /** @id MochiKit.DragAndDrop.revert */
441 revert: false,
442
443 /** @id MochiKit.DragAndDrop.scroll */
444 scroll: false,
445
446 /** @id MochiKit.DragAndDrop.scrollSensitivity */
447 scrollSensitivity: 20,
448
449 /** @id MochiKit.DragAndDrop.scrollSpeed */
450 scrollSpeed: 15,
451 // false, or xy or [x, y] or function (x, y){return [x, y];}
452
453 /** @id MochiKit.DragAndDrop.snap */
454 snap: false
455 }, options);
456
457 var d = MochiKit.DOM;
458 this.element = d.getElement(element);
459
460 if (options.handle && (typeof(options.handle) == 'string')) {
461 this.handle = d.getFirstElementByTagAndClassName(null,
462 options.handle, this.element);
463 }
464 if (!this.handle) {
465 this.handle = d.getElement(options.handle);
466 }
467 if (!this.handle) {
468 this.handle = this.element;
469 }
470
471 if (options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
472 options.scroll = d.getElement(options.scroll);
473 this._isScrollChild = MochiKit.DOM.isChildNode(this.element, options.scroll);
474 }
475
476 d.makePositioned(this.element); // fix IE
477
478 this.delta = this.currentDelta();
479 this.options = options;
480 this.dragging = false;
481
482 this.eventMouseDown = MochiKit.Signal.connect(this.handle,
483 'onmousedown', this, this.initDrag);
484 MochiKit.DragAndDrop.Draggables.register(this);
485 },
486
487 /** @id MochiKit.DragAndDrop.destroy */
488 destroy: function () {
489 MochiKit.Signal.disconnect(this.eventMouseDown);
490 MochiKit.DragAndDrop.Draggables.unregister(this);
491 },
492
493 /** @id MochiKit.DragAndDrop.currentDelta */
494 currentDelta: function () {
495 var s = MochiKit.Style.getStyle;
496 return [
497 parseInt(s(this.element, 'left') || '0'),
498 parseInt(s(this.element, 'top') || '0')];
499 },
500
501 /** @id MochiKit.DragAndDrop.initDrag */
502 initDrag: function (event) {
503 if (!event.mouse().button.left) {
504 return;
505 }
506 // abort on form elements, fixes a Firefox issue
507 var src = event.target();
508 var tagName = (src.tagName || '').toUpperCase();
509 if (tagName === 'INPUT' || tagName === 'SELECT' ||
510 tagName === 'OPTION' || tagName === 'BUTTON' ||
511 tagName === 'TEXTAREA') {
512 return;
513 }
514
515 if (this._revert) {
516 this._revert.cancel();
517 this._revert = null;
518 }
519
520 var pointer = event.mouse();
521 var pos = MochiKit.Position.cumulativeOffset(this.element);
522 this.offset = [pointer.page.x - pos.x, pointer.page.y - pos.y];
523
524 MochiKit.DragAndDrop.Draggables.activate(this);
525 event.stop();
526 },
527
528 /** @id MochiKit.DragAndDrop.startDrag */
529 startDrag: function (event) {
530 this.dragging = true;
531 if (this.options.selectclass) {
532 MochiKit.DOM.addElementClass(this.element,
533 this.options.selectclass);
534 }
535 if (this.options.zindex) {
536 this.originalZ = parseInt(MochiKit.Style.getStyle(this.element,
537 'z-index') || '0');
538 this.element.style.zIndex = this.options.zindex;
539 }
540
541 if (this.options.ghosting) {
542 this._clone = this.element.cloneNode(true);
543 this.ghostPosition = MochiKit.Position.absolutize(this.element);
544 this.element.parentNode.insertBefore(this._clone, this.element);
545 }
546
547 if (this.options.scroll) {
548 if (this.options.scroll == window) {
549 var where = this._getWindowScroll(this.options.scroll);
550 this.originalScrollLeft = where.left;
551 this.originalScrollTop = where.top;
552 } else {
553 this.originalScrollLeft = this.options.scroll.scrollLeft;
554 this.originalScrollTop = this.options.scroll.scrollTop;
555 }
556 }
557
558 MochiKit.DragAndDrop.Droppables.prepare(this.element);
559 MochiKit.DragAndDrop.Draggables.notify('start', this, event);
560 if (this.options.starteffect) {
561 this.options.starteffect(this.element);
562 }
563 },
564
565 /** @id MochiKit.DragAndDrop.updateDrag */
566 updateDrag: function (event, pointer) {
567 if (!this.dragging) {
568 this.startDrag(event);
569 }
570 MochiKit.Position.prepare();
571 MochiKit.DragAndDrop.Droppables.show(pointer, this.element);
572 MochiKit.DragAndDrop.Draggables.notify('drag', this, event);
573 this.draw(pointer);
574 this.options.onchange(this);
575
576 if (this.options.scroll) {
577 this.stopScrolling();
578 var p, q;
579 if (this.options.scroll == window) {
580 var s = this._getWindowScroll(this.options.scroll);
581 p = new MochiKit.Style.Coordinates(s.left, s.top);
582 q = new MochiKit.Style.Coordinates(s.left + s.width,
583 s.top + s.height);
584 } else {
585 p = MochiKit.Position.page(this.options.scroll);
586 p.x += this.options.scroll.scrollLeft;
587 p.y += this.options.scroll.scrollTop;
588 p.x += (window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0);
589 p.y += (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0);
590 q = new MochiKit.Style.Coordinates(p.x + this.options.scroll.offsetWidth,
591 p.y + this.options.scroll.offsetHeight);
592 }
593 var speed = [0, 0];
594 if (pointer.page.x > (q.x - this.options.scrollSensitivity)) {
595 speed[0] = pointer.page.x - (q.x - this.options.scrollSensitivity);
596 } else if (pointer.page.x < (p.x + this.options.scrollSensitivity)) {
597 speed[0] = pointer.page.x - (p.x + this.options.scrollSensitivity);
598 }
599 if (pointer.page.y > (q.y - this.options.scrollSensitivity)) {
600 speed[1] = pointer.page.y - (q.y - this.options.scrollSensitivity);
601 } else if (pointer.page.y < (p.y + this.options.scrollSensitivity)) {
602 speed[1] = pointer.page.y - (p.y + this.options.scrollSensitivity);
603 }
604 this.startScrolling(speed);
605 }
606
607 // fix AppleWebKit rendering
608 if (/AppleWebKit'/.test(navigator.appVersion)) {
609 window.scrollBy(0, 0);
610 }
611 event.stop();
612 },
613
614 /** @id MochiKit.DragAndDrop.finishDrag */
615 finishDrag: function (event, success) {
616 var dr = MochiKit.DragAndDrop;
617 this.dragging = false;
618 if (this.options.selectclass) {
619 MochiKit.DOM.removeElementClass(this.element,
620 this.options.selectclass);
621 }
622
623 if (this.options.ghosting) {
624 // XXX: from a user point of view, it would be better to remove
625 // the node only *after* the MochiKit.Visual.Move end when used
626 // with revert.
627 MochiKit.Position.relativize(this.element, this.ghostPosition);
628 MochiKit.DOM.removeElement(this._clone);
629 this._clone = null;
630 }
631
632 if (success) {
633 dr.Droppables.fire(event, this.element);
634 }
635 dr.Draggables.notify('end', this, event);
636
637 var revert = this.options.revert;
638 if (revert && typeof(revert) == 'function') {
639 revert = revert(this.element);
640 }
641
642 var d = this.currentDelta();
643 if (revert && this.options.reverteffect) {
644 this._revert = this.options.reverteffect(this.element,
645 d[1] - this.delta[1], d[0] - this.delta[0]);
646 } else {
647 this.delta = d;
648 }
649
650 if (this.options.zindex) {
651 this.element.style.zIndex = this.originalZ;
652 }
653
654 if (this.options.endeffect) {
655 this.options.endeffect(this.element);
656 }
657
658 dr.Draggables.deactivate();
659 dr.Droppables.reset(this.element);
660 },
661
662 /** @id MochiKit.DragAndDrop.keyPress */
663 keyPress: function (event) {
664 if (event.key().string != "KEY_ESCAPE") {
665 return;
666 }
667 this.finishDrag(event, false);
668 event.stop();
669 },
670
671 /** @id MochiKit.DragAndDrop.endDrag */
672 endDrag: function (event) {
673 if (!this.dragging) {
674 return;
675 }
676 this.stopScrolling();
677 this.finishDrag(event, true);
678 event.stop();
679 },
680
681 /** @id MochiKit.DragAndDrop.draw */
682 draw: function (point) {
683 var pos = MochiKit.Position.cumulativeOffset(this.element);
684 var d = this.currentDelta();
685 pos.x -= d[0];
686 pos.y -= d[1];
687
688 if (this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
689 pos.x -= this.options.scroll.scrollLeft - this.originalScrollLeft;
690 pos.y -= this.options.scroll.scrollTop - this.originalScrollTop;
691 }
692
693 var p = [point.page.x - pos.x - this.offset[0],
694 point.page.y - pos.y - this.offset[1]];
695
696 if (this.options.snap) {
697 if (typeof(this.options.snap) == 'function') {
698 p = this.options.snap(p[0], p[1]);
699 } else {
700 if (this.options.snap instanceof Array) {
701 var i = -1;
702 p = MochiKit.Base.map(MochiKit.Base.bind(function (v) {
703 i += 1;
704 return Math.round(v/this.options.snap[i]) *
705 this.options.snap[i];
706 }, this), p);
707 } else {
708 p = MochiKit.Base.map(MochiKit.Base.bind(function (v) {
709 return Math.round(v/this.options.snap) *
710 this.options.snap;
711 }, this), p);
712 }
713 }
714 }
715 var style = this.element.style;
716 if ((!this.options.constraint) ||
717 (this.options.constraint == 'horizontal')) {
718 style.left = p[0] + 'px';
719 }
720 if ((!this.options.constraint) ||
721 (this.options.constraint == 'vertical')) {
722 style.top = p[1] + 'px';
723 }
724 if (style.visibility == 'hidden') {
725 style.visibility = ''; // fix gecko rendering
726 }
727 },
728
729 /** @id MochiKit.DragAndDrop.stopScrolling */
730 stopScrolling: function () {
731 if (this.scrollInterval) {
732 clearInterval(this.scrollInterval);
733 this.scrollInterval = null;
734 MochiKit.DragAndDrop.Draggables._lastScrollPointer = null;
735 }
736 },
737
738 /** @id MochiKit.DragAndDrop.startScrolling */
739 startScrolling: function (speed) {
740 if (!speed[0] && !speed[1]) {
741 return;
742 }
743 this.scrollSpeed = [speed[0] * this.options.scrollSpeed,
744 speed[1] * this.options.scrollSpeed];
745 this.lastScrolled = new Date();
746 this.scrollInterval = setInterval(MochiKit.Base.bind(this.scroll, this), 10);
747 },
748
749 /** @id MochiKit.DragAndDrop.scroll */
750 scroll: function () {
751 var current = new Date();
752 var delta = current - this.lastScrolled;
753 this.lastScrolled = current;
754
755 if (this.options.scroll == window) {
756 var s = this._getWindowScroll(this.options.scroll);
757 if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
758 var dm = delta / 1000;
759 this.options.scroll.scrollTo(s.left + dm * this.scrollSpeed[0],
760 s.top + dm * this.scrollSpeed[1]);
761 }
762 } else {
763 this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
764 this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
765 }
766
767 var d = MochiKit.DragAndDrop;
768
769 MochiKit.Position.prepare();
770 d.Droppables.show(d.Draggables._lastPointer, this.element);
771 d.Draggables.notify('drag', this);
772 if (this._isScrollChild) {
773 d.Draggables._lastScrollPointer = d.Draggables._lastScrollPointer || d.Draggables._lastPointer;
774 d.Draggables._lastScrollPointer.x += this.scrollSpeed[0] * delta / 1000;
775 d.Draggables._lastScrollPointer.y += this.scrollSpeed[1] * delta / 1000;
776 if (d.Draggables._lastScrollPointer.x < 0) {
777 d.Draggables._lastScrollPointer.x = 0;
778 }
779 if (d.Draggables._lastScrollPointer.y < 0) {
780 d.Draggables._lastScrollPointer.y = 0;
781 }
782 this.draw(d.Draggables._lastScrollPointer);
783 }
784
785 this.options.onchange(this);
786 },
787
788 _getWindowScroll: function (win) {
789 var vp, w, h;
790 MochiKit.DOM.withWindow(win, function () {
791 vp = MochiKit.Style.getViewportPosition(win.document);
792 });
793 if (win.innerWidth) {
794 w = win.innerWidth;
795 h = win.innerHeight;
796 } else if (win.document.documentElement && win.document.documentElement.clientWidth) {
797 w = win.document.documentElement.clientWidth;
798 h = win.document.documentElement.clientHeight;
799 } else {
800 w = win.document.body.offsetWidth;
801 h = win.document.body.offsetHeight;
802 }
803 return {top: vp.x, left: vp.y, width: w, height: h};
804 },
805
806 /** @id MochiKit.DragAndDrop.repr */
807 repr: function () {
808 return '[' + this.__class__.NAME + ", options:" + MochiKit.Base.repr(this.options) + "]";
809 }
810 };
811
812 MochiKit.DragAndDrop.__new__ = function () {
813 MochiKit.Base.nameFunctions(this);
814
815 this.EXPORT_TAGS = {
816 ":common": this.EXPORT,
817 ":all": MochiKit.Base.concat(this.EXPORT, this.EXPORT_OK)
818 };
819 };
820
821 MochiKit.DragAndDrop.__new__();
822
823 MochiKit.Base._exportSymbols(this, MochiKit.DragAndDrop);
824
@@ -0,0 +1,305 b''
1 /***
2
3 MochiKit.Format 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005 Bob Ippolito. All rights Reserved.
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide('MochiKit.Format');
13 }
14
15 if (typeof(MochiKit) == 'undefined') {
16 MochiKit = {};
17 }
18
19 if (typeof(MochiKit.Format) == 'undefined') {
20 MochiKit.Format = {};
21 }
22
23 MochiKit.Format.NAME = "MochiKit.Format";
24 MochiKit.Format.VERSION = "1.4";
25 MochiKit.Format.__repr__ = function () {
26 return "[" + this.NAME + " " + this.VERSION + "]";
27 };
28 MochiKit.Format.toString = function () {
29 return this.__repr__();
30 };
31
32 MochiKit.Format._numberFormatter = function (placeholder, header, footer, locale, isPercent, precision, leadingZeros, separatorAt, trailingZeros) {
33 return function (num) {
34 num = parseFloat(num);
35 if (typeof(num) == "undefined" || num === null || isNaN(num)) {
36 return placeholder;
37 }
38 var curheader = header;
39 var curfooter = footer;
40 if (num < 0) {
41 num = -num;
42 } else {
43 curheader = curheader.replace(/-/, "");
44 }
45 var me = arguments.callee;
46 var fmt = MochiKit.Format.formatLocale(locale);
47 if (isPercent) {
48 num = num * 100.0;
49 curfooter = fmt.percent + curfooter;
50 }
51 num = MochiKit.Format.roundToFixed(num, precision);
52 var parts = num.split(/\./);
53 var whole = parts[0];
54 var frac = (parts.length == 1) ? "" : parts[1];
55 var res = "";
56 while (whole.length < leadingZeros) {
57 whole = "0" + whole;
58 }
59 if (separatorAt) {
60 while (whole.length > separatorAt) {
61 var i = whole.length - separatorAt;
62 //res = res + fmt.separator + whole.substring(i, whole.length);
63 res = fmt.separator + whole.substring(i, whole.length) + res;
64 whole = whole.substring(0, i);
65 }
66 }
67 res = whole + res;
68 if (precision > 0) {
69 while (frac.length < trailingZeros) {
70 frac = frac + "0";
71 }
72 res = res + fmt.decimal + frac;
73 }
74 return curheader + res + curfooter;
75 };
76 };
77
78 /** @id MochiKit.Format.numberFormatter */
79 MochiKit.Format.numberFormatter = function (pattern, placeholder/* = "" */, locale/* = "default" */) {
80 // http://java.sun.com/docs/books/tutorial/i18n/format/numberpattern.html
81 // | 0 | leading or trailing zeros
82 // | # | just the number
83 // | , | separator
84 // | . | decimal separator
85 // | % | Multiply by 100 and format as percent
86 if (typeof(placeholder) == "undefined") {
87 placeholder = "";
88 }
89 var match = pattern.match(/((?:[0#]+,)?[0#]+)(?:\.([0#]+))?(%)?/);
90 if (!match) {
91 throw TypeError("Invalid pattern");
92 }
93 var header = pattern.substr(0, match.index);
94 var footer = pattern.substr(match.index + match[0].length);
95 if (header.search(/-/) == -1) {
96 header = header + "-";
97 }
98 var whole = match[1];
99 var frac = (typeof(match[2]) == "string" && match[2] != "") ? match[2] : "";
100 var isPercent = (typeof(match[3]) == "string" && match[3] != "");
101 var tmp = whole.split(/,/);
102 var separatorAt;
103 if (typeof(locale) == "undefined") {
104 locale = "default";
105 }
106 if (tmp.length == 1) {
107 separatorAt = null;
108 } else {
109 separatorAt = tmp[1].length;
110 }
111 var leadingZeros = whole.length - whole.replace(/0/g, "").length;
112 var trailingZeros = frac.length - frac.replace(/0/g, "").length;
113 var precision = frac.length;
114 var rval = MochiKit.Format._numberFormatter(
115 placeholder, header, footer, locale, isPercent, precision,
116 leadingZeros, separatorAt, trailingZeros
117 );
118 var m = MochiKit.Base;
119 if (m) {
120 var fn = arguments.callee;
121 var args = m.concat(arguments);
122 rval.repr = function () {
123 return [
124 self.NAME,
125 "(",
126 map(m.repr, args).join(", "),
127 ")"
128 ].join("");
129 };
130 }
131 return rval;
132 };
133
134 /** @id MochiKit.Format.formatLocale */
135 MochiKit.Format.formatLocale = function (locale) {
136 if (typeof(locale) == "undefined" || locale === null) {
137 locale = "default";
138 }
139 if (typeof(locale) == "string") {
140 var rval = MochiKit.Format.LOCALE[locale];
141 if (typeof(rval) == "string") {
142 rval = arguments.callee(rval);
143 MochiKit.Format.LOCALE[locale] = rval;
144 }
145 return rval;
146 } else {
147 return locale;
148 }
149 };
150
151 /** @id MochiKit.Format.twoDigitAverage */
152 MochiKit.Format.twoDigitAverage = function (numerator, denominator) {
153 if (denominator) {
154 var res = numerator / denominator;
155 if (!isNaN(res)) {
156 return MochiKit.Format.twoDigitFloat(numerator / denominator);
157 }
158 }
159 return "0";
160 };
161
162 /** @id MochiKit.Format.twoDigitFloat */
163 MochiKit.Format.twoDigitFloat = function (someFloat) {
164 var sign = (someFloat < 0 ? '-' : '');
165 var s = Math.floor(Math.abs(someFloat) * 100).toString();
166 if (s == '0') {
167 return s;
168 }
169 if (s.length < 3) {
170 while (s.charAt(s.length - 1) == '0') {
171 s = s.substring(0, s.length - 1);
172 }
173 return sign + '0.' + s;
174 }
175 var head = sign + s.substring(0, s.length - 2);
176 var tail = s.substring(s.length - 2, s.length);
177 if (tail == '00') {
178 return head;
179 } else if (tail.charAt(1) == '0') {
180 return head + '.' + tail.charAt(0);
181 } else {
182 return head + '.' + tail;
183 }
184 };
185
186 /** @id MochiKit.Format.lstrip */
187 MochiKit.Format.lstrip = function (str, /* optional */chars) {
188 str = str + "";
189 if (typeof(str) != "string") {
190 return null;
191 }
192 if (!chars) {
193 return str.replace(/^\s+/, "");
194 } else {
195 return str.replace(new RegExp("^[" + chars + "]+"), "");
196 }
197 };
198
199 /** @id MochiKit.Format.rstrip */
200 MochiKit.Format.rstrip = function (str, /* optional */chars) {
201 str = str + "";
202 if (typeof(str) != "string") {
203 return null;
204 }
205 if (!chars) {
206 return str.replace(/\s+$/, "");
207 } else {
208 return str.replace(new RegExp("[" + chars + "]+$"), "");
209 }
210 };
211
212 /** @id MochiKit.Format.strip */
213 MochiKit.Format.strip = function (str, /* optional */chars) {
214 var self = MochiKit.Format;
215 return self.rstrip(self.lstrip(str, chars), chars);
216 };
217
218 /** @id MochiKit.Format.truncToFixed */
219 MochiKit.Format.truncToFixed = function (aNumber, precision) {
220 aNumber = Math.floor(aNumber * Math.pow(10, precision));
221 var res = (aNumber * Math.pow(10, -precision)).toFixed(precision);
222 if (res.charAt(0) == ".") {
223 res = "0" + res;
224 }
225 return res;
226 };
227
228 /** @id MochiKit.Format.roundToFixed */
229 MochiKit.Format.roundToFixed = function (aNumber, precision) {
230 return MochiKit.Format.truncToFixed(
231 aNumber + 0.5 * Math.pow(10, -precision),
232 precision
233 );
234 };
235
236 /** @id MochiKit.Format.percentFormat */
237 MochiKit.Format.percentFormat = function (someFloat) {
238 return MochiKit.Format.twoDigitFloat(100 * someFloat) + '%';
239 };
240
241 MochiKit.Format.EXPORT = [
242 "truncToFixed",
243 "roundToFixed",
244 "numberFormatter",
245 "formatLocale",
246 "twoDigitAverage",
247 "twoDigitFloat",
248 "percentFormat",
249 "lstrip",
250 "rstrip",
251 "strip"
252 ];
253
254 MochiKit.Format.LOCALE = {
255 en_US: {separator: ",", decimal: ".", percent: "%"},
256 de_DE: {separator: ".", decimal: ",", percent: "%"},
257 pt_BR: {separator: ".", decimal: ",", percent: "%"},
258 fr_FR: {separator: " ", decimal: ",", percent: "%"},
259 "default": "en_US"
260 };
261
262 MochiKit.Format.EXPORT_OK = [];
263 MochiKit.Format.EXPORT_TAGS = {
264 ':all': MochiKit.Format.EXPORT,
265 ':common': MochiKit.Format.EXPORT
266 };
267
268 MochiKit.Format.__new__ = function () {
269 // MochiKit.Base.nameFunctions(this);
270 var base = this.NAME + ".";
271 var k, v, o;
272 for (k in this.LOCALE) {
273 o = this.LOCALE[k];
274 if (typeof(o) == "object") {
275 o.repr = function () { return this.NAME; };
276 o.NAME = base + "LOCALE." + k;
277 }
278 }
279 for (k in this) {
280 o = this[k];
281 if (typeof(o) == 'function' && typeof(o.NAME) == 'undefined') {
282 try {
283 o.NAME = base + k;
284 } catch (e) {
285 // pass
286 }
287 }
288 }
289 };
290
291 MochiKit.Format.__new__();
292
293 if (typeof(MochiKit.Base) != "undefined") {
294 MochiKit.Base._exportSymbols(this, MochiKit.Format);
295 } else {
296 (function (globals, module) {
297 if ((typeof(JSAN) == 'undefined' && typeof(dojo) == 'undefined')
298 || (MochiKit.__export__ === false)) {
299 var all = module.EXPORT_TAGS[":all"];
300 for (var i = 0; i < all.length; i++) {
301 globals[all[i]] = module[all[i]];
302 }
303 }
304 })(this, MochiKit.Format);
305 }
This diff has been collapsed as it changes many lines, (851 lines changed) Show them Hide them
@@ -0,0 +1,851 b''
1 /***
2
3 MochiKit.Iter 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005 Bob Ippolito. All rights Reserved.
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide('MochiKit.Iter');
13 dojo.require('MochiKit.Base');
14 }
15
16 if (typeof(JSAN) != 'undefined') {
17 JSAN.use("MochiKit.Base", []);
18 }
19
20 try {
21 if (typeof(MochiKit.Base) == 'undefined') {
22 throw "";
23 }
24 } catch (e) {
25 throw "MochiKit.Iter depends on MochiKit.Base!";
26 }
27
28 if (typeof(MochiKit.Iter) == 'undefined') {
29 MochiKit.Iter = {};
30 }
31
32 MochiKit.Iter.NAME = "MochiKit.Iter";
33 MochiKit.Iter.VERSION = "1.4";
34 MochiKit.Base.update(MochiKit.Iter, {
35 __repr__: function () {
36 return "[" + this.NAME + " " + this.VERSION + "]";
37 },
38 toString: function () {
39 return this.__repr__();
40 },
41
42 /** @id MochiKit.Iter.registerIteratorFactory */
43 registerIteratorFactory: function (name, check, iterfactory, /* optional */ override) {
44 MochiKit.Iter.iteratorRegistry.register(name, check, iterfactory, override);
45 },
46
47 /** @id MochiKit.Iter.iter */
48 iter: function (iterable, /* optional */ sentinel) {
49 var self = MochiKit.Iter;
50 if (arguments.length == 2) {
51 return self.takewhile(
52 function (a) { return a != sentinel; },
53 iterable
54 );
55 }
56 if (typeof(iterable.next) == 'function') {
57 return iterable;
58 } else if (typeof(iterable.iter) == 'function') {
59 return iterable.iter();
60 /*
61 } else if (typeof(iterable.__iterator__) == 'function') {
62 //
63 // XXX: We can't support JavaScript 1.7 __iterator__ directly
64 // because of Object.prototype.__iterator__
65 //
66 return iterable.__iterator__();
67 */
68 }
69
70 try {
71 return self.iteratorRegistry.match(iterable);
72 } catch (e) {
73 var m = MochiKit.Base;
74 if (e == m.NotFound) {
75 e = new TypeError(typeof(iterable) + ": " + m.repr(iterable) + " is not iterable");
76 }
77 throw e;
78 }
79 },
80
81 /** @id MochiKit.Iter.count */
82 count: function (n) {
83 if (!n) {
84 n = 0;
85 }
86 var m = MochiKit.Base;
87 return {
88 repr: function () { return "count(" + n + ")"; },
89 toString: m.forwardCall("repr"),
90 next: m.counter(n)
91 };
92 },
93
94 /** @id MochiKit.Iter.cycle */
95 cycle: function (p) {
96 var self = MochiKit.Iter;
97 var m = MochiKit.Base;
98 var lst = [];
99 var iterator = self.iter(p);
100 return {
101 repr: function () { return "cycle(...)"; },
102 toString: m.forwardCall("repr"),
103 next: function () {
104 try {
105 var rval = iterator.next();
106 lst.push(rval);
107 return rval;
108 } catch (e) {
109 if (e != self.StopIteration) {
110 throw e;
111 }
112 if (lst.length === 0) {
113 this.next = function () {
114 throw self.StopIteration;
115 };
116 } else {
117 var i = -1;
118 this.next = function () {
119 i = (i + 1) % lst.length;
120 return lst[i];
121 };
122 }
123 return this.next();
124 }
125 }
126 };
127 },
128
129 /** @id MochiKit.Iter.repeat */
130 repeat: function (elem, /* optional */n) {
131 var m = MochiKit.Base;
132 if (typeof(n) == 'undefined') {
133 return {
134 repr: function () {
135 return "repeat(" + m.repr(elem) + ")";
136 },
137 toString: m.forwardCall("repr"),
138 next: function () {
139 return elem;
140 }
141 };
142 }
143 return {
144 repr: function () {
145 return "repeat(" + m.repr(elem) + ", " + n + ")";
146 },
147 toString: m.forwardCall("repr"),
148 next: function () {
149 if (n <= 0) {
150 throw MochiKit.Iter.StopIteration;
151 }
152 n -= 1;
153 return elem;
154 }
155 };
156 },
157
158 /** @id MochiKit.Iter.next */
159 next: function (iterator) {
160 return iterator.next();
161 },
162
163 /** @id MochiKit.Iter.izip */
164 izip: function (p, q/*, ...*/) {
165 var m = MochiKit.Base;
166 var self = MochiKit.Iter;
167 var next = self.next;
168 var iterables = m.map(self.iter, arguments);
169 return {
170 repr: function () { return "izip(...)"; },
171 toString: m.forwardCall("repr"),
172 next: function () { return m.map(next, iterables); }
173 };
174 },
175
176 /** @id MochiKit.Iter.ifilter */
177 ifilter: function (pred, seq) {
178 var m = MochiKit.Base;
179 seq = MochiKit.Iter.iter(seq);
180 if (pred === null) {
181 pred = m.operator.truth;
182 }
183 return {
184 repr: function () { return "ifilter(...)"; },
185 toString: m.forwardCall("repr"),
186 next: function () {
187 while (true) {
188 var rval = seq.next();
189 if (pred(rval)) {
190 return rval;
191 }
192 }
193 // mozilla warnings aren't too bright
194 return undefined;
195 }
196 };
197 },
198
199 /** @id MochiKit.Iter.ifilterfalse */
200 ifilterfalse: function (pred, seq) {
201 var m = MochiKit.Base;
202 seq = MochiKit.Iter.iter(seq);
203 if (pred === null) {
204 pred = m.operator.truth;
205 }
206 return {
207 repr: function () { return "ifilterfalse(...)"; },
208 toString: m.forwardCall("repr"),
209 next: function () {
210 while (true) {
211 var rval = seq.next();
212 if (!pred(rval)) {
213 return rval;
214 }
215 }
216 // mozilla warnings aren't too bright
217 return undefined;
218 }
219 };
220 },
221
222 /** @id MochiKit.Iter.islice */
223 islice: function (seq/*, [start,] stop[, step] */) {
224 var self = MochiKit.Iter;
225 var m = MochiKit.Base;
226 seq = self.iter(seq);
227 var start = 0;
228 var stop = 0;
229 var step = 1;
230 var i = -1;
231 if (arguments.length == 2) {
232 stop = arguments[1];
233 } else if (arguments.length == 3) {
234 start = arguments[1];
235 stop = arguments[2];
236 } else {
237 start = arguments[1];
238 stop = arguments[2];
239 step = arguments[3];
240 }
241 return {
242 repr: function () {
243 return "islice(" + ["...", start, stop, step].join(", ") + ")";
244 },
245 toString: m.forwardCall("repr"),
246 next: function () {
247 var rval;
248 while (i < start) {
249 rval = seq.next();
250 i++;
251 }
252 if (start >= stop) {
253 throw self.StopIteration;
254 }
255 start += step;
256 return rval;
257 }
258 };
259 },
260
261 /** @id MochiKit.Iter.imap */
262 imap: function (fun, p, q/*, ...*/) {
263 var m = MochiKit.Base;
264 var self = MochiKit.Iter;
265 var iterables = m.map(self.iter, m.extend(null, arguments, 1));
266 var map = m.map;
267 var next = self.next;
268 return {
269 repr: function () { return "imap(...)"; },
270 toString: m.forwardCall("repr"),
271 next: function () {
272 return fun.apply(this, map(next, iterables));
273 }
274 };
275 },
276
277 /** @id MochiKit.Iter.applymap */
278 applymap: function (fun, seq, self) {
279 seq = MochiKit.Iter.iter(seq);
280 var m = MochiKit.Base;
281 return {
282 repr: function () { return "applymap(...)"; },
283 toString: m.forwardCall("repr"),
284 next: function () {
285 return fun.apply(self, seq.next());
286 }
287 };
288 },
289
290 /** @id MochiKit.Iter.chain */
291 chain: function (p, q/*, ...*/) {
292 // dumb fast path
293 var self = MochiKit.Iter;
294 var m = MochiKit.Base;
295 if (arguments.length == 1) {
296 return self.iter(arguments[0]);
297 }
298 var argiter = m.map(self.iter, arguments);
299 return {
300 repr: function () { return "chain(...)"; },
301 toString: m.forwardCall("repr"),
302 next: function () {
303 while (argiter.length > 1) {
304 try {
305 return argiter[0].next();
306 } catch (e) {
307 if (e != self.StopIteration) {
308 throw e;
309 }
310 argiter.shift();
311 }
312 }
313 if (argiter.length == 1) {
314 // optimize last element
315 var arg = argiter.shift();
316 this.next = m.bind("next", arg);
317 return this.next();
318 }
319 throw self.StopIteration;
320 }
321 };
322 },
323
324 /** @id MochiKit.Iter.takewhile */
325 takewhile: function (pred, seq) {
326 var self = MochiKit.Iter;
327 seq = self.iter(seq);
328 return {
329 repr: function () { return "takewhile(...)"; },
330 toString: MochiKit.Base.forwardCall("repr"),
331 next: function () {
332 var rval = seq.next();
333 if (!pred(rval)) {
334 this.next = function () {
335 throw self.StopIteration;
336 };
337 this.next();
338 }
339 return rval;
340 }
341 };
342 },
343
344 /** @id MochiKit.Iter.dropwhile */
345 dropwhile: function (pred, seq) {
346 seq = MochiKit.Iter.iter(seq);
347 var m = MochiKit.Base;
348 var bind = m.bind;
349 return {
350 "repr": function () { return "dropwhile(...)"; },
351 "toString": m.forwardCall("repr"),
352 "next": function () {
353 while (true) {
354 var rval = seq.next();
355 if (!pred(rval)) {
356 break;
357 }
358 }
359 this.next = bind("next", seq);
360 return rval;
361 }
362 };
363 },
364
365 _tee: function (ident, sync, iterable) {
366 sync.pos[ident] = -1;
367 var m = MochiKit.Base;
368 var listMin = m.listMin;
369 return {
370 repr: function () { return "tee(" + ident + ", ...)"; },
371 toString: m.forwardCall("repr"),
372 next: function () {
373 var rval;
374 var i = sync.pos[ident];
375
376 if (i == sync.max) {
377 rval = iterable.next();
378 sync.deque.push(rval);
379 sync.max += 1;
380 sync.pos[ident] += 1;
381 } else {
382 rval = sync.deque[i - sync.min];
383 sync.pos[ident] += 1;
384 if (i == sync.min && listMin(sync.pos) != sync.min) {
385 sync.min += 1;
386 sync.deque.shift();
387 }
388 }
389 return rval;
390 }
391 };
392 },
393
394 /** @id MochiKit.Iter.tee */
395 tee: function (iterable, n/* = 2 */) {
396 var rval = [];
397 var sync = {
398 "pos": [],
399 "deque": [],
400 "max": -1,
401 "min": -1
402 };
403 if (arguments.length == 1 || typeof(n) == "undefined" || n === null) {
404 n = 2;
405 }
406 var self = MochiKit.Iter;
407 iterable = self.iter(iterable);
408 var _tee = self._tee;
409 for (var i = 0; i < n; i++) {
410 rval.push(_tee(i, sync, iterable));
411 }
412 return rval;
413 },
414
415 /** @id MochiKit.Iter.list */
416 list: function (iterable) {
417 // Fast-path for Array and Array-like
418 var rval;
419 if (iterable instanceof Array) {
420 return iterable.slice();
421 }
422 // this is necessary to avoid a Safari crash
423 if (typeof(iterable) == "function" &&
424 !(iterable instanceof Function) &&
425 typeof(iterable.length) == 'number') {
426 rval = [];
427 for (var i = 0; i < iterable.length; i++) {
428 rval.push(iterable[i]);
429 }
430 return rval;
431 }
432
433 var self = MochiKit.Iter;
434 iterable = self.iter(iterable);
435 var rval = [];
436 try {
437 while (true) {
438 rval.push(iterable.next());
439 }
440 } catch (e) {
441 if (e != self.StopIteration) {
442 throw e;
443 }
444 return rval;
445 }
446 // mozilla warnings aren't too bright
447 return undefined;
448 },
449
450
451 /** @id MochiKit.Iter.reduce */
452 reduce: function (fn, iterable, /* optional */initial) {
453 var i = 0;
454 var x = initial;
455 var self = MochiKit.Iter;
456 iterable = self.iter(iterable);
457 if (arguments.length < 3) {
458 try {
459 x = iterable.next();
460 } catch (e) {
461 if (e == self.StopIteration) {
462 e = new TypeError("reduce() of empty sequence with no initial value");
463 }
464 throw e;
465 }
466 i++;
467 }
468 try {
469 while (true) {
470 x = fn(x, iterable.next());
471 }
472 } catch (e) {
473 if (e != self.StopIteration) {
474 throw e;
475 }
476 }
477 return x;
478 },
479
480 /** @id MochiKit.Iter.range */
481 range: function (/* [start,] stop[, step] */) {
482 var start = 0;
483 var stop = 0;
484 var step = 1;
485 if (arguments.length == 1) {
486 stop = arguments[0];
487 } else if (arguments.length == 2) {
488 start = arguments[0];
489 stop = arguments[1];
490 } else if (arguments.length == 3) {
491 start = arguments[0];
492 stop = arguments[1];
493 step = arguments[2];
494 } else {
495 throw new TypeError("range() takes 1, 2, or 3 arguments!");
496 }
497 if (step === 0) {
498 throw new TypeError("range() step must not be 0");
499 }
500 return {
501 next: function () {
502 if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
503 throw MochiKit.Iter.StopIteration;
504 }
505 var rval = start;
506 start += step;
507 return rval;
508 },
509 repr: function () {
510 return "range(" + [start, stop, step].join(", ") + ")";
511 },
512 toString: MochiKit.Base.forwardCall("repr")
513 };
514 },
515
516 /** @id MochiKit.Iter.sum */
517 sum: function (iterable, start/* = 0 */) {
518 if (typeof(start) == "undefined" || start === null) {
519 start = 0;
520 }
521 var x = start;
522 var self = MochiKit.Iter;
523 iterable = self.iter(iterable);
524 try {
525 while (true) {
526 x += iterable.next();
527 }
528 } catch (e) {
529 if (e != self.StopIteration) {
530 throw e;
531 }
532 }
533 return x;
534 },
535
536 /** @id MochiKit.Iter.exhaust */
537 exhaust: function (iterable) {
538 var self = MochiKit.Iter;
539 iterable = self.iter(iterable);
540 try {
541 while (true) {
542 iterable.next();
543 }
544 } catch (e) {
545 if (e != self.StopIteration) {
546 throw e;
547 }
548 }
549 },
550
551 /** @id MochiKit.Iter.forEach */
552 forEach: function (iterable, func, /* optional */self) {
553 var m = MochiKit.Base;
554 if (arguments.length > 2) {
555 func = m.bind(func, self);
556 }
557 // fast path for array
558 if (m.isArrayLike(iterable)) {
559 try {
560 for (var i = 0; i < iterable.length; i++) {
561 func(iterable[i]);
562 }
563 } catch (e) {
564 if (e != MochiKit.Iter.StopIteration) {
565 throw e;
566 }
567 }
568 } else {
569 self = MochiKit.Iter;
570 self.exhaust(self.imap(func, iterable));
571 }
572 },
573
574 /** @id MochiKit.Iter.every */
575 every: function (iterable, func) {
576 var self = MochiKit.Iter;
577 try {
578 self.ifilterfalse(func, iterable).next();
579 return false;
580 } catch (e) {
581 if (e != self.StopIteration) {
582 throw e;
583 }
584 return true;
585 }
586 },
587
588 /** @id MochiKit.Iter.sorted */
589 sorted: function (iterable, /* optional */cmp) {
590 var rval = MochiKit.Iter.list(iterable);
591 if (arguments.length == 1) {
592 cmp = MochiKit.Base.compare;
593 }
594 rval.sort(cmp);
595 return rval;
596 },
597
598 /** @id MochiKit.Iter.reversed */
599 reversed: function (iterable) {
600 var rval = MochiKit.Iter.list(iterable);
601 rval.reverse();
602 return rval;
603 },
604
605 /** @id MochiKit.Iter.some */
606 some: function (iterable, func) {
607 var self = MochiKit.Iter;
608 try {
609 self.ifilter(func, iterable).next();
610 return true;
611 } catch (e) {
612 if (e != self.StopIteration) {
613 throw e;
614 }
615 return false;
616 }
617 },
618
619 /** @id MochiKit.Iter.iextend */
620 iextend: function (lst, iterable) {
621 if (MochiKit.Base.isArrayLike(iterable)) {
622 // fast-path for array-like
623 for (var i = 0; i < iterable.length; i++) {
624 lst.push(iterable[i]);
625 }
626 } else {
627 var self = MochiKit.Iter;
628 iterable = self.iter(iterable);
629 try {
630 while (true) {
631 lst.push(iterable.next());
632 }
633 } catch (e) {
634 if (e != self.StopIteration) {
635 throw e;
636 }
637 }
638 }
639 return lst;
640 },
641
642 /** @id MochiKit.Iter.groupby */
643 groupby: function(iterable, /* optional */ keyfunc) {
644 var m = MochiKit.Base;
645 var self = MochiKit.Iter;
646 if (arguments.length < 2) {
647 keyfunc = m.operator.identity;
648 }
649 iterable = self.iter(iterable);
650
651 // shared
652 var pk = undefined;
653 var k = undefined;
654 var v;
655
656 function fetch() {
657 v = iterable.next();
658 k = keyfunc(v);
659 };
660
661 function eat() {
662 var ret = v;
663 v = undefined;
664 return ret;
665 };
666
667 var first = true;
668 var compare = m.compare;
669 return {
670 repr: function () { return "groupby(...)"; },
671 next: function() {
672 // iterator-next
673
674 // iterate until meet next group
675 while (compare(k, pk) === 0) {
676 fetch();
677 if (first) {
678 first = false;
679 break;
680 }
681 }
682 pk = k;
683 return [k, {
684 next: function() {
685 // subiterator-next
686 if (v == undefined) { // Is there something to eat?
687 fetch();
688 }
689 if (compare(k, pk) !== 0) {
690 throw self.StopIteration;
691 }
692 return eat();
693 }
694 }];
695 }
696 };
697 },
698
699 /** @id MochiKit.Iter.groupby_as_array */
700 groupby_as_array: function (iterable, /* optional */ keyfunc) {
701 var m = MochiKit.Base;
702 var self = MochiKit.Iter;
703 if (arguments.length < 2) {
704 keyfunc = m.operator.identity;
705 }
706
707 iterable = self.iter(iterable);
708 var result = [];
709 var first = true;
710 var prev_key;
711 var compare = m.compare;
712 while (true) {
713 try {
714 var value = iterable.next();
715 var key = keyfunc(value);
716 } catch (e) {
717 if (e == self.StopIteration) {
718 break;
719 }
720 throw e;
721 }
722 if (first || compare(key, prev_key) !== 0) {
723 var values = [];
724 result.push([key, values]);
725 }
726 values.push(value);
727 first = false;
728 prev_key = key;
729 }
730 return result;
731 },
732
733 /** @id MochiKit.Iter.arrayLikeIter */
734 arrayLikeIter: function (iterable) {
735 var i = 0;
736 return {
737 repr: function () { return "arrayLikeIter(...)"; },
738 toString: MochiKit.Base.forwardCall("repr"),
739 next: function () {
740 if (i >= iterable.length) {
741 throw MochiKit.Iter.StopIteration;
742 }
743 return iterable[i++];
744 }
745 };
746 },
747
748 /** @id MochiKit.Iter.hasIterateNext */
749 hasIterateNext: function (iterable) {
750 return (iterable && typeof(iterable.iterateNext) == "function");
751 },
752
753 /** @id MochiKit.Iter.iterateNextIter */
754 iterateNextIter: function (iterable) {
755 return {
756 repr: function () { return "iterateNextIter(...)"; },
757 toString: MochiKit.Base.forwardCall("repr"),
758 next: function () {
759 var rval = iterable.iterateNext();
760 if (rval === null || rval === undefined) {
761 throw MochiKit.Iter.StopIteration;
762 }
763 return rval;
764 }
765 };
766 }
767 });
768
769
770 MochiKit.Iter.EXPORT_OK = [
771 "iteratorRegistry",
772 "arrayLikeIter",
773 "hasIterateNext",
774 "iterateNextIter"
775 ];
776
777 MochiKit.Iter.EXPORT = [
778 "StopIteration",
779 "registerIteratorFactory",
780 "iter",
781 "count",
782 "cycle",
783 "repeat",
784 "next",
785 "izip",
786 "ifilter",
787 "ifilterfalse",
788 "islice",
789 "imap",
790 "applymap",
791 "chain",
792 "takewhile",
793 "dropwhile",
794 "tee",
795 "list",
796 "reduce",
797 "range",
798 "sum",
799 "exhaust",
800 "forEach",
801 "every",
802 "sorted",
803 "reversed",
804 "some",
805 "iextend",
806 "groupby",
807 "groupby_as_array"
808 ];
809
810 MochiKit.Iter.__new__ = function () {
811 var m = MochiKit.Base;
812 // Re-use StopIteration if exists (e.g. SpiderMonkey)
813 if (typeof(StopIteration) != "undefined") {
814 this.StopIteration = StopIteration;
815 } else {
816 /** @id MochiKit.Iter.StopIteration */
817 this.StopIteration = new m.NamedError("StopIteration");
818 }
819 this.iteratorRegistry = new m.AdapterRegistry();
820 // Register the iterator factory for arrays
821 this.registerIteratorFactory(
822 "arrayLike",
823 m.isArrayLike,
824 this.arrayLikeIter
825 );
826
827 this.registerIteratorFactory(
828 "iterateNext",
829 this.hasIterateNext,
830 this.iterateNextIter
831 );
832
833 this.EXPORT_TAGS = {
834 ":common": this.EXPORT,
835 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
836 };
837
838 m.nameFunctions(this);
839
840 };
841
842 MochiKit.Iter.__new__();
843
844 //
845 // XXX: Internet Explorer blows
846 //
847 if (MochiKit.__export__) {
848 reduce = MochiKit.Iter.reduce;
849 }
850
851 MochiKit.Base._exportSymbols(this, MochiKit.Iter);
@@ -0,0 +1,321 b''
1 /***
2
3 MochiKit.Logging 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005 Bob Ippolito. All rights Reserved.
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide('MochiKit.Logging');
13 dojo.require('MochiKit.Base');
14 }
15
16 if (typeof(JSAN) != 'undefined') {
17 JSAN.use("MochiKit.Base", []);
18 }
19
20 try {
21 if (typeof(MochiKit.Base) == 'undefined') {
22 throw "";
23 }
24 } catch (e) {
25 throw "MochiKit.Logging depends on MochiKit.Base!";
26 }
27
28 if (typeof(MochiKit.Logging) == 'undefined') {
29 MochiKit.Logging = {};
30 }
31
32 MochiKit.Logging.NAME = "MochiKit.Logging";
33 MochiKit.Logging.VERSION = "1.4";
34 MochiKit.Logging.__repr__ = function () {
35 return "[" + this.NAME + " " + this.VERSION + "]";
36 };
37
38 MochiKit.Logging.toString = function () {
39 return this.__repr__();
40 };
41
42
43 MochiKit.Logging.EXPORT = [
44 "LogLevel",
45 "LogMessage",
46 "Logger",
47 "alertListener",
48 "logger",
49 "log",
50 "logError",
51 "logDebug",
52 "logFatal",
53 "logWarning"
54 ];
55
56
57 MochiKit.Logging.EXPORT_OK = [
58 "logLevelAtLeast",
59 "isLogMessage",
60 "compareLogMessage"
61 ];
62
63
64 /** @id MochiKit.Logging.LogMessage */
65 MochiKit.Logging.LogMessage = function (num, level, info) {
66 this.num = num;
67 this.level = level;
68 this.info = info;
69 this.timestamp = new Date();
70 };
71
72 MochiKit.Logging.LogMessage.prototype = {
73 /** @id MochiKit.Logging.LogMessage.prototype.repr */
74 repr: function () {
75 var m = MochiKit.Base;
76 return 'LogMessage(' +
77 m.map(
78 m.repr,
79 [this.num, this.level, this.info]
80 ).join(', ') + ')';
81 },
82 /** @id MochiKit.Logging.LogMessage.prototype.toString */
83 toString: MochiKit.Base.forwardCall("repr")
84 };
85
86 MochiKit.Base.update(MochiKit.Logging, {
87 /** @id MochiKit.Logging.logLevelAtLeast */
88 logLevelAtLeast: function (minLevel) {
89 var self = MochiKit.Logging;
90 if (typeof(minLevel) == 'string') {
91 minLevel = self.LogLevel[minLevel];
92 }
93 return function (msg) {
94 var msgLevel = msg.level;
95 if (typeof(msgLevel) == 'string') {
96 msgLevel = self.LogLevel[msgLevel];
97 }
98 return msgLevel >= minLevel;
99 };
100 },
101
102 /** @id MochiKit.Logging.isLogMessage */
103 isLogMessage: function (/* ... */) {
104 var LogMessage = MochiKit.Logging.LogMessage;
105 for (var i = 0; i < arguments.length; i++) {
106 if (!(arguments[i] instanceof LogMessage)) {
107 return false;
108 }
109 }
110 return true;
111 },
112
113 /** @id MochiKit.Logging.compareLogMessage */
114 compareLogMessage: function (a, b) {
115 return MochiKit.Base.compare([a.level, a.info], [b.level, b.info]);
116 },
117
118 /** @id MochiKit.Logging.alertListener */
119 alertListener: function (msg) {
120 alert(
121 "num: " + msg.num +
122 "\nlevel: " + msg.level +
123 "\ninfo: " + msg.info.join(" ")
124 );
125 }
126
127 });
128
129 /** @id MochiKit.Logging.Logger */
130 MochiKit.Logging.Logger = function (/* optional */maxSize) {
131 this.counter = 0;
132 if (typeof(maxSize) == 'undefined' || maxSize === null) {
133 maxSize = -1;
134 }
135 this.maxSize = maxSize;
136 this._messages = [];
137 this.listeners = {};
138 this.useNativeConsole = false;
139 };
140
141 MochiKit.Logging.Logger.prototype = {
142 /** @id MochiKit.Logging.Logger.prototype.clear */
143 clear: function () {
144 this._messages.splice(0, this._messages.length);
145 },
146
147 /** @id MochiKit.Logging.Logger.prototype.logToConsole */
148 logToConsole: function (msg) {
149 if (typeof(window) != "undefined" && window.console
150 && window.console.log) {
151 // Safari and FireBug 0.4
152 // Percent replacement is a workaround for cute Safari crashing bug
153 window.console.log(msg.replace(/%/g, '\uFF05'));
154 } else if (typeof(opera) != "undefined" && opera.postError) {
155 // Opera
156 opera.postError(msg);
157 } else if (typeof(printfire) == "function") {
158 // FireBug 0.3 and earlier
159 printfire(msg);
160 } else if (typeof(Debug) != "undefined" && Debug.writeln) {
161 // IE Web Development Helper (?)
162 // http://www.nikhilk.net/Entry.aspx?id=93
163 Debug.writeln(msg);
164 } else if (typeof(debug) != "undefined" && debug.trace) {
165 // Atlas framework (?)
166 // http://www.nikhilk.net/Entry.aspx?id=93
167 debug.trace(msg);
168 }
169 },
170
171 /** @id MochiKit.Logging.Logger.prototype.dispatchListeners */
172 dispatchListeners: function (msg) {
173 for (var k in this.listeners) {
174 var pair = this.listeners[k];
175 if (pair.ident != k || (pair[0] && !pair[0](msg))) {
176 continue;
177 }
178 pair[1](msg);
179 }
180 },
181
182 /** @id MochiKit.Logging.Logger.prototype.addListener */
183 addListener: function (ident, filter, listener) {
184 if (typeof(filter) == 'string') {
185 filter = MochiKit.Logging.logLevelAtLeast(filter);
186 }
187 var entry = [filter, listener];
188 entry.ident = ident;
189 this.listeners[ident] = entry;
190 },
191
192 /** @id MochiKit.Logging.Logger.prototype.removeListener */
193 removeListener: function (ident) {
194 delete this.listeners[ident];
195 },
196
197 /** @id MochiKit.Logging.Logger.prototype.baseLog */
198 baseLog: function (level, message/*, ...*/) {
199 var msg = new MochiKit.Logging.LogMessage(
200 this.counter,
201 level,
202 MochiKit.Base.extend(null, arguments, 1)
203 );
204 this._messages.push(msg);
205 this.dispatchListeners(msg);
206 if (this.useNativeConsole) {
207 this.logToConsole(msg.level + ": " + msg.info.join(" "));
208 }
209 this.counter += 1;
210 while (this.maxSize >= 0 && this._messages.length > this.maxSize) {
211 this._messages.shift();
212 }
213 },
214
215 /** @id MochiKit.Logging.Logger.prototype.getMessages */
216 getMessages: function (howMany) {
217 var firstMsg = 0;
218 if (!(typeof(howMany) == 'undefined' || howMany === null)) {
219 firstMsg = Math.max(0, this._messages.length - howMany);
220 }
221 return this._messages.slice(firstMsg);
222 },
223
224 /** @id MochiKit.Logging.Logger.prototype.getMessageText */
225 getMessageText: function (howMany) {
226 if (typeof(howMany) == 'undefined' || howMany === null) {
227 howMany = 30;
228 }
229 var messages = this.getMessages(howMany);
230 if (messages.length) {
231 var lst = map(function (m) {
232 return '\n [' + m.num + '] ' + m.level + ': ' + m.info.join(' ');
233 }, messages);
234 lst.unshift('LAST ' + messages.length + ' MESSAGES:');
235 return lst.join('');
236 }
237 return '';
238 },
239
240 /** @id MochiKit.Logging.Logger.prototype.debuggingBookmarklet */
241 debuggingBookmarklet: function (inline) {
242 if (typeof(MochiKit.LoggingPane) == "undefined") {
243 alert(this.getMessageText());
244 } else {
245 MochiKit.LoggingPane.createLoggingPane(inline || false);
246 }
247 }
248 };
249
250 MochiKit.Logging.__new__ = function () {
251 this.LogLevel = {
252 ERROR: 40,
253 FATAL: 50,
254 WARNING: 30,
255 INFO: 20,
256 DEBUG: 10
257 };
258
259 var m = MochiKit.Base;
260 m.registerComparator("LogMessage",
261 this.isLogMessage,
262 this.compareLogMessage
263 );
264
265 var partial = m.partial;
266
267 var Logger = this.Logger;
268 var baseLog = Logger.prototype.baseLog;
269 m.update(this.Logger.prototype, {
270 debug: partial(baseLog, 'DEBUG'),
271 log: partial(baseLog, 'INFO'),
272 error: partial(baseLog, 'ERROR'),
273 fatal: partial(baseLog, 'FATAL'),
274 warning: partial(baseLog, 'WARNING')
275 });
276
277 // indirectly find logger so it can be replaced
278 var self = this;
279 var connectLog = function (name) {
280 return function () {
281 self.logger[name].apply(self.logger, arguments);
282 };
283 };
284
285 /** @id MochiKit.Logging.log */
286 this.log = connectLog('log');
287 /** @id MochiKit.Logging.logError */
288 this.logError = connectLog('error');
289 /** @id MochiKit.Logging.logDebug */
290 this.logDebug = connectLog('debug');
291 /** @id MochiKit.Logging.logFatal */
292 this.logFatal = connectLog('fatal');
293 /** @id MochiKit.Logging.logWarning */
294 this.logWarning = connectLog('warning');
295 this.logger = new Logger();
296 this.logger.useNativeConsole = true;
297
298 this.EXPORT_TAGS = {
299 ":common": this.EXPORT,
300 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
301 };
302
303 m.nameFunctions(this);
304
305 };
306
307 if (typeof(printfire) == "undefined" &&
308 typeof(document) != "undefined" && document.createEvent &&
309 typeof(dispatchEvent) != "undefined") {
310 // FireBug really should be less lame about this global function
311 printfire = function () {
312 printfire.args = arguments;
313 var ev = document.createEvent("Events");
314 ev.initEvent("printfire", false, true);
315 dispatchEvent(ev);
316 };
317 }
318
319 MochiKit.Logging.__new__();
320
321 MochiKit.Base._exportSymbols(this, MochiKit.Logging);
@@ -0,0 +1,374 b''
1 /***
2
3 MochiKit.LoggingPane 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005 Bob Ippolito. All rights Reserved.
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide('MochiKit.LoggingPane');
13 dojo.require('MochiKit.Logging');
14 dojo.require('MochiKit.Base');
15 }
16
17 if (typeof(JSAN) != 'undefined') {
18 JSAN.use("MochiKit.Logging", []);
19 JSAN.use("MochiKit.Base", []);
20 }
21
22 try {
23 if (typeof(MochiKit.Base) == 'undefined' || typeof(MochiKit.Logging) == 'undefined') {
24 throw "";
25 }
26 } catch (e) {
27 throw "MochiKit.LoggingPane depends on MochiKit.Base and MochiKit.Logging!";
28 }
29
30 if (typeof(MochiKit.LoggingPane) == 'undefined') {
31 MochiKit.LoggingPane = {};
32 }
33
34 MochiKit.LoggingPane.NAME = "MochiKit.LoggingPane";
35 MochiKit.LoggingPane.VERSION = "1.4";
36 MochiKit.LoggingPane.__repr__ = function () {
37 return "[" + this.NAME + " " + this.VERSION + "]";
38 };
39
40 MochiKit.LoggingPane.toString = function () {
41 return this.__repr__();
42 };
43
44 /** @id MochiKit.LoggingPane.createLoggingPane */
45 MochiKit.LoggingPane.createLoggingPane = function (inline/* = false */) {
46 var m = MochiKit.LoggingPane;
47 inline = !(!inline);
48 if (m._loggingPane && m._loggingPane.inline != inline) {
49 m._loggingPane.closePane();
50 m._loggingPane = null;
51 }
52 if (!m._loggingPane || m._loggingPane.closed) {
53 m._loggingPane = new m.LoggingPane(inline, MochiKit.Logging.logger);
54 }
55 return m._loggingPane;
56 };
57
58 /** @id MochiKit.LoggingPane.LoggingPane */
59 MochiKit.LoggingPane.LoggingPane = function (inline/* = false */, logger/* = MochiKit.Logging.logger */) {
60
61 /* Use a div if inline, pop up a window if not */
62 /* Create the elements */
63 if (typeof(logger) == "undefined" || logger === null) {
64 logger = MochiKit.Logging.logger;
65 }
66 this.logger = logger;
67 var update = MochiKit.Base.update;
68 var updatetree = MochiKit.Base.updatetree;
69 var bind = MochiKit.Base.bind;
70 var clone = MochiKit.Base.clone;
71 var win = window;
72 var uid = "_MochiKit_LoggingPane";
73 if (typeof(MochiKit.DOM) != "undefined") {
74 win = MochiKit.DOM.currentWindow();
75 }
76 if (!inline) {
77 // name the popup with the base URL for uniqueness
78 var url = win.location.href.split("?")[0].replace(/[#:\/.><&-]/g, "_");
79 var name = uid + "_" + url;
80 var nwin = win.open("", name, "dependent,resizable,height=200");
81 if (!nwin) {
82 alert("Not able to open debugging window due to pop-up blocking.");
83 return undefined;
84 }
85 nwin.document.write(
86 '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" '
87 + '"http://www.w3.org/TR/html4/loose.dtd">'
88 + '<html><head><title>[MochiKit.LoggingPane]</title></head>'
89 + '<body></body></html>'
90 );
91 nwin.document.close();
92 nwin.document.title += ' ' + win.document.title;
93 win = nwin;
94 }
95 var doc = win.document;
96 this.doc = doc;
97
98 // Connect to the debug pane if it already exists (i.e. in a window orphaned by the page being refreshed)
99 var debugPane = doc.getElementById(uid);
100 var existing_pane = !!debugPane;
101 if (debugPane && typeof(debugPane.loggingPane) != "undefined") {
102 debugPane.loggingPane.logger = this.logger;
103 debugPane.loggingPane.buildAndApplyFilter();
104 return debugPane.loggingPane;
105 }
106
107 if (existing_pane) {
108 // clear any existing contents
109 var child;
110 while ((child = debugPane.firstChild)) {
111 debugPane.removeChild(child);
112 }
113 } else {
114 debugPane = doc.createElement("div");
115 debugPane.id = uid;
116 }
117 debugPane.loggingPane = this;
118 var levelFilterField = doc.createElement("input");
119 var infoFilterField = doc.createElement("input");
120 var filterButton = doc.createElement("button");
121 var loadButton = doc.createElement("button");
122 var clearButton = doc.createElement("button");
123 var closeButton = doc.createElement("button");
124 var logPaneArea = doc.createElement("div");
125 var logPane = doc.createElement("div");
126
127 /* Set up the functions */
128 var listenerId = uid + "_Listener";
129 this.colorTable = clone(this.colorTable);
130 var messages = [];
131 var messageFilter = null;
132
133 /** @id MochiKit.LoggingPane.messageLevel */
134 var messageLevel = function (msg) {
135 var level = msg.level;
136 if (typeof(level) == "number") {
137 level = MochiKit.Logging.LogLevel[level];
138 }
139 return level;
140 };
141
142 /** @id MochiKit.LoggingPane.messageText */
143 var messageText = function (msg) {
144 return msg.info.join(" ");
145 };
146
147 /** @id MochiKit.LoggingPane.addMessageText */
148 var addMessageText = bind(function (msg) {
149 var level = messageLevel(msg);
150 var text = messageText(msg);
151 var c = this.colorTable[level];
152 var p = doc.createElement("span");
153 p.className = "MochiKit-LogMessage MochiKit-LogLevel-" + level;
154 p.style.cssText = "margin: 0px; white-space: -moz-pre-wrap; white-space: -o-pre-wrap; white-space: pre-wrap; white-space: pre-line; word-wrap: break-word; wrap-option: emergency; color: " + c;
155 p.appendChild(doc.createTextNode(level + ": " + text));
156 logPane.appendChild(p);
157 logPane.appendChild(doc.createElement("br"));
158 if (logPaneArea.offsetHeight > logPaneArea.scrollHeight) {
159 logPaneArea.scrollTop = 0;
160 } else {
161 logPaneArea.scrollTop = logPaneArea.scrollHeight;
162 }
163 }, this);
164
165 /** @id MochiKit.LoggingPane.addMessage */
166 var addMessage = function (msg) {
167 messages[messages.length] = msg;
168 addMessageText(msg);
169 };
170
171 /** @id MochiKit.LoggingPane.buildMessageFilter */
172 var buildMessageFilter = function () {
173 var levelre, infore;
174 try {
175 /* Catch any exceptions that might arise due to invalid regexes */
176 levelre = new RegExp(levelFilterField.value);
177 infore = new RegExp(infoFilterField.value);
178 } catch(e) {
179 /* If there was an error with the regexes, do no filtering */
180 logDebug("Error in filter regex: " + e.message);
181 return null;
182 }
183
184 return function (msg) {
185 return (
186 levelre.test(messageLevel(msg)) &&
187 infore.test(messageText(msg))
188 );
189 };
190 };
191
192 /** @id MochiKit.LoggingPane.clearMessagePane */
193 var clearMessagePane = function () {
194 while (logPane.firstChild) {
195 logPane.removeChild(logPane.firstChild);
196 }
197 };
198
199 /** @id MochiKit.LoggingPane.clearMessages */
200 var clearMessages = function () {
201 messages = [];
202 clearMessagePane();
203 };
204
205 /** @id MochiKit.LoggingPane.closePane */
206 var closePane = bind(function () {
207 if (this.closed) {
208 return;
209 }
210 this.closed = true;
211 if (MochiKit.LoggingPane._loggingPane == this) {
212 MochiKit.LoggingPane._loggingPane = null;
213 }
214 this.logger.removeListener(listenerId);
215 try {
216 try {
217 debugPane.loggingPane = null;
218 } catch(e) { logFatal("Bookmarklet was closed incorrectly."); }
219 if (inline) {
220 debugPane.parentNode.removeChild(debugPane);
221 } else {
222 this.win.close();
223 }
224 } catch(e) {}
225 }, this);
226
227 /** @id MochiKit.LoggingPane.filterMessages */
228 var filterMessages = function () {
229 clearMessagePane();
230
231 for (var i = 0; i < messages.length; i++) {
232 var msg = messages[i];
233 if (messageFilter === null || messageFilter(msg)) {
234 addMessageText(msg);
235 }
236 }
237 };
238
239 this.buildAndApplyFilter = function () {
240 messageFilter = buildMessageFilter();
241
242 filterMessages();
243
244 this.logger.removeListener(listenerId);
245 this.logger.addListener(listenerId, messageFilter, addMessage);
246 };
247
248
249 /** @id MochiKit.LoggingPane.loadMessages */
250 var loadMessages = bind(function () {
251 messages = this.logger.getMessages();
252 filterMessages();
253 }, this);
254
255 /** @id MochiKit.LoggingPane.filterOnEnter */
256 var filterOnEnter = bind(function (event) {
257 event = event || window.event;
258 key = event.which || event.keyCode;
259 if (key == 13) {
260 this.buildAndApplyFilter();
261 }
262 }, this);
263
264 /* Create the debug pane */
265 var style = "display: block; z-index: 1000; left: 0px; bottom: 0px; position: fixed; width: 100%; background-color: white; font: " + this.logFont;
266 if (inline) {
267 style += "; height: 10em; border-top: 2px solid black";
268 } else {
269 style += "; height: 100%;";
270 }
271 debugPane.style.cssText = style;
272
273 if (!existing_pane) {
274 doc.body.appendChild(debugPane);
275 }
276
277 /* Create the filter fields */
278 style = {"cssText": "width: 33%; display: inline; font: " + this.logFont};
279
280 updatetree(levelFilterField, {
281 "value": "FATAL|ERROR|WARNING|INFO|DEBUG",
282 "onkeypress": filterOnEnter,
283 "style": style
284 });
285 debugPane.appendChild(levelFilterField);
286
287 updatetree(infoFilterField, {
288 "value": ".*",
289 "onkeypress": filterOnEnter,
290 "style": style
291 });
292 debugPane.appendChild(infoFilterField);
293
294 /* Create the buttons */
295 style = "width: 8%; display:inline; font: " + this.logFont;
296
297 filterButton.appendChild(doc.createTextNode("Filter"));
298 filterButton.onclick = bind("buildAndApplyFilter", this);
299 filterButton.style.cssText = style;
300 debugPane.appendChild(filterButton);
301
302 loadButton.appendChild(doc.createTextNode("Load"));
303 loadButton.onclick = loadMessages;
304 loadButton.style.cssText = style;
305 debugPane.appendChild(loadButton);
306
307 clearButton.appendChild(doc.createTextNode("Clear"));
308 clearButton.onclick = clearMessages;
309 clearButton.style.cssText = style;
310 debugPane.appendChild(clearButton);
311
312 closeButton.appendChild(doc.createTextNode("Close"));
313 closeButton.onclick = closePane;
314 closeButton.style.cssText = style;
315 debugPane.appendChild(closeButton);
316
317 /* Create the logging pane */
318 logPaneArea.style.cssText = "overflow: auto; width: 100%";
319 logPane.style.cssText = "width: 100%; height: " + (inline ? "8em" : "100%");
320
321 logPaneArea.appendChild(logPane);
322 debugPane.appendChild(logPaneArea);
323
324 this.buildAndApplyFilter();
325 loadMessages();
326
327 if (inline) {
328 this.win = undefined;
329 } else {
330 this.win = win;
331 }
332 this.inline = inline;
333 this.closePane = closePane;
334 this.closed = false;
335
336
337 return this;
338 };
339
340 MochiKit.LoggingPane.LoggingPane.prototype = {
341 "logFont": "8pt Verdana,sans-serif",
342 "colorTable": {
343 "ERROR": "red",
344 "FATAL": "darkred",
345 "WARNING": "blue",
346 "INFO": "black",
347 "DEBUG": "green"
348 }
349 };
350
351
352 MochiKit.LoggingPane.EXPORT_OK = [
353 "LoggingPane"
354 ];
355
356 MochiKit.LoggingPane.EXPORT = [
357 "createLoggingPane"
358 ];
359
360 MochiKit.LoggingPane.__new__ = function () {
361 this.EXPORT_TAGS = {
362 ":common": this.EXPORT,
363 ":all": MochiKit.Base.concat(this.EXPORT, this.EXPORT_OK)
364 };
365
366 MochiKit.Base.nameFunctions(this);
367
368 MochiKit.LoggingPane._loggingPane = null;
369
370 };
371
372 MochiKit.LoggingPane.__new__();
373
374 MochiKit.Base._exportSymbols(this, MochiKit.LoggingPane);
@@ -0,0 +1,154 b''
1 /***
2
3 MochiKit.MochiKit 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005 Bob Ippolito. All rights Reserved.
8
9 ***/
10
11 if (typeof(MochiKit) == 'undefined') {
12 MochiKit = {};
13 }
14
15 if (typeof(MochiKit.MochiKit) == 'undefined') {
16 /** @id MochiKit.MochiKit */
17 MochiKit.MochiKit = {};
18 }
19
20 MochiKit.MochiKit.NAME = "MochiKit.MochiKit";
21 MochiKit.MochiKit.VERSION = "1.4";
22 MochiKit.MochiKit.__repr__ = function () {
23 return "[" + this.NAME + " " + this.VERSION + "]";
24 };
25
26 /** @id MochiKit.MochiKit.toString */
27 MochiKit.MochiKit.toString = function () {
28 return this.__repr__();
29 };
30
31 /** @id MochiKit.MochiKit.SUBMODULES */
32 MochiKit.MochiKit.SUBMODULES = [
33 "Base",
34 "Iter",
35 "Logging",
36 "DateTime",
37 "Format",
38 "Async",
39 "DOM",
40 "Selector",
41 "Style",
42 "LoggingPane",
43 "Color",
44 "Signal",
45 "Position",
46 "Visual"
47 ];
48
49 if (typeof(JSAN) != 'undefined' || typeof(dojo) != 'undefined') {
50 if (typeof(dojo) != 'undefined') {
51 dojo.provide('MochiKit.MochiKit');
52 dojo.require("MochiKit.*");
53 }
54 if (typeof(JSAN) != 'undefined') {
55 (function (lst) {
56 for (var i = 0; i < lst.length; i++) {
57 JSAN.use("MochiKit." + lst[i], []);
58 }
59 })(MochiKit.MochiKit.SUBMODULES);
60 }
61 (function () {
62 var extend = MochiKit.Base.extend;
63 var self = MochiKit.MochiKit;
64 var modules = self.SUBMODULES;
65 var EXPORT = [];
66 var EXPORT_OK = [];
67 var EXPORT_TAGS = {};
68 var i, k, m, all;
69 for (i = 0; i < modules.length; i++) {
70 m = MochiKit[modules[i]];
71 extend(EXPORT, m.EXPORT);
72 extend(EXPORT_OK, m.EXPORT_OK);
73 for (k in m.EXPORT_TAGS) {
74 EXPORT_TAGS[k] = extend(EXPORT_TAGS[k], m.EXPORT_TAGS[k]);
75 }
76 all = m.EXPORT_TAGS[":all"];
77 if (!all) {
78 all = extend(null, m.EXPORT, m.EXPORT_OK);
79 }
80 var j;
81 for (j = 0; j < all.length; j++) {
82 k = all[j];
83 self[k] = m[k];
84 }
85 }
86 self.EXPORT = EXPORT;
87 self.EXPORT_OK = EXPORT_OK;
88 self.EXPORT_TAGS = EXPORT_TAGS;
89 }());
90
91 } else {
92 if (typeof(MochiKit.__compat__) == 'undefined') {
93 MochiKit.__compat__ = true;
94 }
95 (function () {
96 if (typeof(document) == "undefined") {
97 return;
98 }
99 var scripts = document.getElementsByTagName("script");
100 var kXULNSURI = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
101 var base = null;
102 var baseElem = null;
103 var allScripts = {};
104 var i;
105 for (i = 0; i < scripts.length; i++) {
106 var src = scripts[i].getAttribute("src");
107 if (!src) {
108 continue;
109 }
110 allScripts[src] = true;
111 if (src.match(/MochiKit.js$/)) {
112 base = src.substring(0, src.lastIndexOf('MochiKit.js'));
113 baseElem = scripts[i];
114 }
115 }
116 if (base === null) {
117 return;
118 }
119 var modules = MochiKit.MochiKit.SUBMODULES;
120 for (var i = 0; i < modules.length; i++) {
121 if (MochiKit[modules[i]]) {
122 continue;
123 }
124 var uri = base + modules[i] + '.js';
125 if (uri in allScripts) {
126 continue;
127 }
128 if (document.documentElement &&
129 document.documentElement.namespaceURI == kXULNSURI) {
130 // XUL
131 var s = document.createElementNS(kXULNSURI, 'script');
132 s.setAttribute("id", "MochiKit_" + base + modules[i]);
133 s.setAttribute("src", uri);
134 s.setAttribute("type", "application/x-javascript");
135 baseElem.parentNode.appendChild(s);
136 } else {
137 // HTML
138 /*
139 DOM can not be used here because Safari does
140 deferred loading of scripts unless they are
141 in the document or inserted with document.write
142
143 This is not XHTML compliant. If you want XHTML
144 compliance then you must use the packed version of MochiKit
145 or include each script individually (basically unroll
146 these document.write calls into your XHTML source)
147
148 */
149 document.write('<script src="' + uri +
150 '" type="text/javascript"></script>');
151 }
152 };
153 })();
154 }
@@ -0,0 +1,115 b''
1 /***
2
3 MochiKit.MockDOM 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005 Bob Ippolito. All rights Reserved.
8
9 ***/
10
11 if (typeof(MochiKit) == "undefined") {
12 MochiKit = {};
13 }
14
15 if (typeof(MochiKit.MockDOM) == "undefined") {
16 MochiKit.MockDOM = {};
17 }
18
19 MochiKit.MockDOM.NAME = "MochiKit.MockDOM";
20 MochiKit.MockDOM.VERSION = "1.4";
21
22 MochiKit.MockDOM.__repr__ = function () {
23 return "[" + this.NAME + " " + this.VERSION + "]";
24 };
25
26 /** @id MochiKit.MockDOM.toString */
27 MochiKit.MockDOM.toString = function () {
28 return this.__repr__();
29 };
30
31 /** @id MochiKit.MockDOM.createDocument */
32 MochiKit.MockDOM.createDocument = function () {
33 var doc = new MochiKit.MockDOM.MockElement("DOCUMENT");
34 doc.body = doc.createElement("BODY");
35 doc.appendChild(doc.body);
36 return doc;
37 };
38
39 /** @id MochiKit.MockDOM.MockElement */
40 MochiKit.MockDOM.MockElement = function (name, data, ownerDocument) {
41 this.tagName = this.nodeName = name.toUpperCase();
42 this.ownerDocument = ownerDocument || null;
43 if (name == "DOCUMENT") {
44 this.nodeType = 9;
45 this.childNodes = [];
46 } else if (typeof(data) == "string") {
47 this.nodeValue = data;
48 this.nodeType = 3;
49 } else {
50 this.nodeType = 1;
51 this.childNodes = [];
52 }
53 if (name.substring(0, 1) == "<") {
54 var nameattr = name.substring(
55 name.indexOf('"') + 1, name.lastIndexOf('"'));
56 name = name.substring(1, name.indexOf(" "));
57 this.tagName = this.nodeName = name.toUpperCase();
58 this.setAttribute("name", nameattr);
59 }
60 };
61
62 MochiKit.MockDOM.MockElement.prototype = {
63 /** @id MochiKit.MockDOM.MockElement.prototype.createElement */
64 createElement: function (tagName) {
65 return new MochiKit.MockDOM.MockElement(tagName, null, this.nodeType == 9 ? this : this.ownerDocument);
66 },
67 /** @id MochiKit.MockDOM.MockElement.prototype.createTextNode */
68 createTextNode: function (text) {
69 return new MochiKit.MockDOM.MockElement("text", text, this.nodeType == 9 ? this : this.ownerDocument);
70 },
71 /** @id MochiKit.MockDOM.MockElement.prototype.setAttribute */
72 setAttribute: function (name, value) {
73 this[name] = value;
74 },
75 /** @id MochiKit.MockDOM.MockElement.prototype.getAttribute */
76 getAttribute: function (name) {
77 return this[name];
78 },
79 /** @id MochiKit.MockDOM.MockElement.prototype.appendChild */
80 appendChild: function (child) {
81 this.childNodes.push(child);
82 },
83 /** @id MochiKit.MockDOM.MockElement.prototype.toString */
84 toString: function () {
85 return "MockElement(" + this.tagName + ")";
86 },
87 /** @id MochiKit.MockDOM.MockElement.prototype.getElementsByTagName */
88 getElementsByTagName: function (tagName) {
89 var foundElements = [];
90 MochiKit.Base.nodeWalk(this, function(node){
91 if (tagName == '*' || tagName == node.tagName) {
92 foundElements.push(node);
93 return node.childNodes;
94 }
95 });
96 return foundElements;
97 }
98 };
99
100 /** @id MochiKit.MockDOM.EXPORT_OK */
101 MochiKit.MockDOM.EXPORT_OK = [
102 "mockElement",
103 "createDocument"
104 ];
105
106 /** @id MochiKit.MockDOM.EXPORT */
107 MochiKit.MockDOM.EXPORT = [
108 "document"
109 ];
110
111 MochiKit.MockDOM.__new__ = function () {
112 this.document = this.createDocument();
113 };
114
115 MochiKit.MockDOM.__new__();
@@ -0,0 +1,258 b''
1 /***
2
3 MochiKit.Position 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005-2006 Bob Ippolito and others. All rights Reserved.
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide('MochiKit.Position');
13 dojo.require('MochiKit.Base');
14 dojo.require('MochiKit.DOM');
15 dojo.require('MochiKit.Style');
16 }
17 if (typeof(JSAN) != 'undefined') {
18 JSAN.use('MochiKit.Base', []);
19 JSAN.use('MochiKit.DOM', []);
20 JSAN.use('MochiKit.Style', []);
21 }
22
23 try {
24 if (typeof(MochiKit.Base) == 'undefined' ||
25 typeof(MochiKit.Style) == 'undefined' ||
26 typeof(MochiKit.DOM) == 'undefined') {
27 throw '';
28 }
29 } catch (e) {
30 throw 'MochiKit.Style depends on MochiKit.Base, MochiKit.DOM, and MochiKit.Style!';
31 }
32
33 if (typeof(MochiKit.Position) == 'undefined') {
34 MochiKit.Position = {};
35 }
36
37 MochiKit.Position.NAME = 'MochiKit.Position';
38 MochiKit.Position.VERSION = '1.4';
39 MochiKit.Position.__repr__ = function () {
40 return '[' + this.NAME + ' ' + this.VERSION + ']';
41 };
42 MochiKit.Position.toString = function () {
43 return this.__repr__();
44 };
45
46 MochiKit.Position.EXPORT_OK = [];
47
48 MochiKit.Position.EXPORT = [
49 ];
50
51
52 MochiKit.Base.update(MochiKit.Position, {
53 // set to true if needed, warning: firefox performance problems
54 // NOT neeeded for page scrolling, only if draggable contained in
55 // scrollable elements
56 includeScrollOffsets: false,
57
58 /** @id MochiKit.Position.prepare */
59 prepare: function () {
60 var deltaX = window.pageXOffset
61 || document.documentElement.scrollLeft
62 || document.body.scrollLeft
63 || 0;
64 var deltaY = window.pageYOffset
65 || document.documentElement.scrollTop
66 || document.body.scrollTop
67 || 0;
68 this.windowOffset = new MochiKit.Style.Coordinates(deltaX, deltaY);
69 },
70
71 /** @id MochiKit.Position.cumulativeOffset */
72 cumulativeOffset: function (element) {
73 var valueT = 0;
74 var valueL = 0;
75 do {
76 valueT += element.offsetTop || 0;
77 valueL += element.offsetLeft || 0;
78 element = element.offsetParent;
79 } while (element);
80 return new MochiKit.Style.Coordinates(valueL, valueT);
81 },
82
83 /** @id MochiKit.Position.realOffset */
84 realOffset: function (element) {
85 var valueT = 0;
86 var valueL = 0;
87 do {
88 valueT += element.scrollTop || 0;
89 valueL += element.scrollLeft || 0;
90 element = element.parentNode;
91 } while (element);
92 return new MochiKit.Style.Coordinates(valueL, valueT);
93 },
94
95 /** @id MochiKit.Position.within */
96 within: function (element, x, y) {
97 if (this.includeScrollOffsets) {
98 return this.withinIncludingScrolloffsets(element, x, y);
99 }
100 this.xcomp = x;
101 this.ycomp = y;
102 this.offset = this.cumulativeOffset(element);
103 if (element.style.position == "fixed") {
104 this.offset.x += this.windowOffset.x;
105 this.offset.y += this.windowOffset.y;
106 }
107
108 return (y >= this.offset.y &&
109 y < this.offset.y + element.offsetHeight &&
110 x >= this.offset.x &&
111 x < this.offset.x + element.offsetWidth);
112 },
113
114 /** @id MochiKit.Position.withinIncludingScrolloffsets */
115 withinIncludingScrolloffsets: function (element, x, y) {
116 var offsetcache = this.realOffset(element);
117
118 this.xcomp = x + offsetcache.x - this.windowOffset.x;
119 this.ycomp = y + offsetcache.y - this.windowOffset.y;
120 this.offset = this.cumulativeOffset(element);
121
122 return (this.ycomp >= this.offset.y &&
123 this.ycomp < this.offset.y + element.offsetHeight &&
124 this.xcomp >= this.offset.x &&
125 this.xcomp < this.offset.x + element.offsetWidth);
126 },
127
128 // within must be called directly before
129 /** @id MochiKit.Position.overlap */
130 overlap: function (mode, element) {
131 if (!mode) {
132 return 0;
133 }
134 if (mode == 'vertical') {
135 return ((this.offset.y + element.offsetHeight) - this.ycomp) /
136 element.offsetHeight;
137 }
138 if (mode == 'horizontal') {
139 return ((this.offset.x + element.offsetWidth) - this.xcomp) /
140 element.offsetWidth;
141 }
142 },
143
144 /** @id MochiKit.Position.absolutize */
145 absolutize: function (element) {
146 element = MochiKit.DOM.getElement(element);
147 if (element.style.position == 'absolute') {
148 return;
149 }
150 MochiKit.Position.prepare();
151
152 var offsets = MochiKit.Position.positionedOffset(element);
153 var width = element.clientWidth;
154 var height = element.clientHeight;
155
156 var oldStyle = {
157 'position': element.style.position,
158 'left': offsets.x - parseFloat(element.style.left || 0),
159 'top': offsets.y - parseFloat(element.style.top || 0),
160 'width': element.style.width,
161 'height': element.style.height
162 };
163
164 element.style.position = 'absolute';
165 element.style.top = offsets.y + 'px';
166 element.style.left = offsets.x + 'px';
167 element.style.width = width + 'px';
168 element.style.height = height + 'px';
169
170 return oldStyle;
171 },
172
173 /** @id MochiKit.Position.positionedOffset */
174 positionedOffset: function (element) {
175 var valueT = 0, valueL = 0;
176 do {
177 valueT += element.offsetTop || 0;
178 valueL += element.offsetLeft || 0;
179 element = element.offsetParent;
180 if (element) {
181 p = MochiKit.Style.getStyle(element, 'position');
182 if (p == 'relative' || p == 'absolute') {
183 break;
184 }
185 }
186 } while (element);
187 return new MochiKit.Style.Coordinates(valueL, valueT);
188 },
189
190 /** @id MochiKit.Position.relativize */
191 relativize: function (element, oldPos) {
192 element = MochiKit.DOM.getElement(element);
193 if (element.style.position == 'relative') {
194 return;
195 }
196 MochiKit.Position.prepare();
197
198 var top = parseFloat(element.style.top || 0) -
199 (oldPos['top'] || 0);
200 var left = parseFloat(element.style.left || 0) -
201 (oldPos['left'] || 0);
202
203 element.style.position = oldPos['position'];
204 element.style.top = top + 'px';
205 element.style.left = left + 'px';
206 element.style.width = oldPos['width'];
207 element.style.height = oldPos['height'];
208 },
209
210 /** @id MochiKit.Position.clone */
211 clone: function (source, target) {
212 source = MochiKit.DOM.getElement(source);
213 target = MochiKit.DOM.getElement(target);
214 target.style.position = 'absolute';
215 var offsets = this.cumulativeOffset(source);
216 target.style.top = offsets.y + 'px';
217 target.style.left = offsets.x + 'px';
218 target.style.width = source.offsetWidth + 'px';
219 target.style.height = source.offsetHeight + 'px';
220 },
221
222 /** @id MochiKit.Position.page */
223 page: function (forElement) {
224 var valueT = 0;
225 var valueL = 0;
226
227 var element = forElement;
228 do {
229 valueT += element.offsetTop || 0;
230 valueL += element.offsetLeft || 0;
231
232 // Safari fix
233 if (element.offsetParent == document.body && MochiKit.Style.getStyle(element, 'position') == 'absolute') {
234 break;
235 }
236 } while (element = element.offsetParent);
237
238 element = forElement;
239 do {
240 valueT -= element.scrollTop || 0;
241 valueL -= element.scrollLeft || 0;
242 } while (element = element.parentNode);
243
244 return new MochiKit.Style.Coordinates(valueL, valueT);
245 }
246 });
247
248 MochiKit.Position.__new__ = function (win) {
249 var m = MochiKit.Base;
250 this.EXPORT_TAGS = {
251 ':common': this.EXPORT,
252 ':all': m.concat(this.EXPORT, this.EXPORT_OK)
253 };
254
255 m.nameFunctions(this);
256 };
257
258 MochiKit.Position.__new__(this); No newline at end of file
@@ -0,0 +1,431 b''
1 /***
2
3 MochiKit.Selector 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005 Bob Ippolito and others. All rights Reserved.
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide('MochiKit.Selector');
13 dojo.require('MochiKit.Base');
14 dojo.require('MochiKit.DOM');
15 dojo.require('MochiKit.Iter');
16 }
17
18 if (typeof(JSAN) != 'undefined') {
19 JSAN.use("MochiKit.Base", []);
20 JSAN.use("MochiKit.DOM", []);
21 JSAN.use("MochiKit.Iter", []);
22 }
23
24 try {
25 if (typeof(MochiKit.Base) === 'undefined' ||
26 typeof(MochiKit.DOM) === 'undefined' ||
27 typeof(MochiKit.Iter) === 'undefined') {
28 throw "";
29 }
30 } catch (e) {
31 throw "MochiKit.Selector depends on MochiKit.Base, MochiKit.DOM and MochiKit.Iter!";
32 }
33
34 if (typeof(MochiKit.Selector) == 'undefined') {
35 MochiKit.Selector = {};
36 }
37
38 MochiKit.Selector.NAME = "MochiKit.Selector";
39
40 MochiKit.Selector.VERSION = "1.4";
41
42 MochiKit.Selector.__repr__ = function () {
43 return "[" + this.NAME + " " + this.VERSION + "]";
44 };
45
46 MochiKit.Selector.toString = function () {
47 return this.__repr__();
48 };
49
50 MochiKit.Selector.EXPORT = [
51 "Selector",
52 "findChildElements",
53 "findDocElements",
54 "$$"
55 ];
56
57 MochiKit.Selector.EXPORT_OK = [
58 ];
59
60 MochiKit.Selector.Selector = function (expression) {
61 this.params = {classNames: [], pseudoClassNames: []};
62 this.expression = expression.toString().replace(/(^\s+|\s+$)/g, '');
63 this.parseExpression();
64 this.compileMatcher();
65 };
66
67 MochiKit.Selector.Selector.prototype = {
68 /***
69
70 Selector class: convenient object to make CSS selections.
71
72 ***/
73 __class__: MochiKit.Selector.Selector,
74
75 /** @id MochiKit.Selector.Selector.prototype.parseExpression */
76 parseExpression: function () {
77 function abort(message) {
78 throw 'Parse error in selector: ' + message;
79 }
80
81 if (this.expression == '') {
82 abort('empty expression');
83 }
84
85 var repr = MochiKit.Base.repr;
86 var params = this.params;
87 var expr = this.expression;
88 var match, modifier, clause, rest;
89 while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!^$*]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
90 params.attributes = params.attributes || [];
91 params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
92 expr = match[1];
93 }
94
95 if (expr == '*') {
96 return this.params.wildcard = true;
97 }
98
99 while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+(?:\([^)]*\))?)(.*)/i)) {
100 modifier = match[1];
101 clause = match[2];
102 rest = match[3];
103 switch (modifier) {
104 case '#':
105 params.id = clause;
106 break;
107 case '.':
108 params.classNames.push(clause);
109 break;
110 case ':':
111 params.pseudoClassNames.push(clause);
112 break;
113 case '':
114 case undefined:
115 params.tagName = clause.toUpperCase();
116 break;
117 default:
118 abort(repr(expr));
119 }
120 expr = rest;
121 }
122
123 if (expr.length > 0) {
124 abort(repr(expr));
125 }
126 },
127
128 /** @id MochiKit.Selector.Selector.prototype.buildMatchExpression */
129 buildMatchExpression: function () {
130 var repr = MochiKit.Base.repr;
131 var params = this.params;
132 var conditions = [];
133 var clause, i;
134
135 function childElements(element) {
136 return "MochiKit.Base.filter(function (node) { return node.nodeType == 1; }, " + element + ".childNodes)";
137 }
138
139 if (params.wildcard) {
140 conditions.push('true');
141 }
142 if (clause = params.id) {
143 conditions.push('element.id == ' + repr(clause));
144 }
145 if (clause = params.tagName) {
146 conditions.push('element.tagName.toUpperCase() == ' + repr(clause));
147 }
148 if ((clause = params.classNames).length > 0) {
149 for (i = 0; i < clause.length; i++) {
150 conditions.push('MochiKit.DOM.hasElementClass(element, ' + repr(clause[i]) + ')');
151 }
152 }
153 if ((clause = params.pseudoClassNames).length > 0) {
154 for (i = 0; i < clause.length; i++) {
155 var match = clause[i].match(/^([^(]+)(?:\((.*)\))?$/);
156 var pseudoClass = match[1];
157 var pseudoClassArgument = match[2];
158 switch (pseudoClass) {
159 case 'root':
160 conditions.push('element.nodeType == 9 || element === element.ownerDocument.documentElement'); break;
161 case 'nth-child':
162 case 'nth-last-child':
163 case 'nth-of-type':
164 case 'nth-last-of-type':
165 match = pseudoClassArgument.match(/^((?:(\d+)n\+)?(\d+)|odd|even)$/);
166 if (!match) {
167 throw "Invalid argument to pseudo element nth-child: " + pseudoClassArgument;
168 }
169 var a, b;
170 if (match[0] == 'odd') {
171 a = 2;
172 b = 1;
173 } else if (match[0] == 'even') {
174 a = 2;
175 b = 0;
176 } else {
177 a = match[2] && parseInt(match) || null;
178 b = parseInt(match[3]);
179 }
180 conditions.push('this.nthChild(element,' + a + ',' + b
181 + ',' + !!pseudoClass.match('^nth-last') // Reverse
182 + ',' + !!pseudoClass.match('of-type$') // Restrict to same tagName
183 + ')');
184 break;
185 case 'first-child':
186 conditions.push('this.nthChild(element, null, 1)');
187 break;
188 case 'last-child':
189 conditions.push('this.nthChild(element, null, 1, true)');
190 break;
191 case 'first-of-type':
192 conditions.push('this.nthChild(element, null, 1, false, true)');
193 break;
194 case 'last-of-type':
195 conditions.push('this.nthChild(element, null, 1, true, true)');
196 break;
197 case 'only-child':
198 conditions.push(childElements('element.parentNode') + '.length == 1');
199 break;
200 case 'only-of-type':
201 conditions.push('MochiKit.Base.filter(function (node) { return node.tagName == element.tagName; }, ' + childElements('element.parentNode') + ').length == 1');
202 break;
203 case 'empty':
204 conditions.push('element.childNodes.length == 0');
205 break;
206 case 'enabled':
207 conditions.push('(this.isUIElement(element) && element.disabled === false)');
208 break;
209 case 'disabled':
210 conditions.push('(this.isUIElement(element) && element.disabled === true)');
211 break;
212 case 'checked':
213 conditions.push('(this.isUIElement(element) && element.checked === true)');
214 break;
215 case 'not':
216 var subselector = new MochiKit.Selector.Selector(pseudoClassArgument);
217 conditions.push('!( ' + subselector.buildMatchExpression() + ')')
218 break;
219 }
220 }
221 }
222 if (clause = params.attributes) {
223 MochiKit.Base.map(function (attribute) {
224 var value = 'MochiKit.DOM.getNodeAttribute(element, ' + repr(attribute.name) + ')';
225 var splitValueBy = function (delimiter) {
226 return value + '.split(' + repr(delimiter) + ')';
227 }
228
229 switch (attribute.operator) {
230 case '=':
231 conditions.push(value + ' == ' + repr(attribute.value));
232 break;
233 case '~=':
234 conditions.push(value + ' && MochiKit.Base.findValue(' + splitValueBy(' ') + ', ' + repr(attribute.value) + ') > -1');
235 break;
236 case '^=':
237 conditions.push(value + '.substring(0, ' + attribute.value.length + ') == ' + repr(attribute.value));
238 break;
239 case '$=':
240 conditions.push(value + '.substring(' + value + '.length - ' + attribute.value.length + ') == ' + repr(attribute.value));
241 break;
242 case '*=':
243 conditions.push(value + '.match(' + repr(attribute.value) + ')');
244 break;
245 case '|=':
246 conditions.push(
247 value + ' && ' + splitValueBy('-') + '[0].toUpperCase() == ' + repr(attribute.value.toUpperCase())
248 );
249 break;
250 case '!=':
251 conditions.push(value + ' != ' + repr(attribute.value));
252 break;
253 case '':
254 case undefined:
255 conditions.push(value + ' != null');
256 break;
257 default:
258 throw 'Unknown operator ' + attribute.operator + ' in selector';
259 }
260 }, clause);
261 }
262
263 return conditions.join(' && ');
264 },
265
266 /** @id MochiKit.Selector.Selector.prototype.compileMatcher */
267 compileMatcher: function () {
268 this.match = new Function('element', 'if (!element.tagName) return false; \
269 return ' + this.buildMatchExpression());
270 },
271
272 /** @id MochiKit.Selector.Selector.prototype.nthChild */
273 nthChild: function (element, a, b, reverse, sametag){
274 var siblings = MochiKit.Base.filter(function (node) {
275 return node.nodeType == 1;
276 }, element.parentNode.childNodes);
277 if (sametag) {
278 siblings = MochiKit.Base.filter(function (node) {
279 return node.tagName == element.tagName;
280 }, siblings);
281 }
282 if (reverse) {
283 siblings = MochiKit.Iter.reversed(siblings);
284 }
285 if (a) {
286 var actualIndex = MochiKit.Base.findIdentical(siblings, element);
287 return ((actualIndex + 1 - b) / a) % 1 == 0;
288 } else {
289 return b == MochiKit.Base.findIdentical(siblings, element) + 1;
290 }
291 },
292
293 /** @id MochiKit.Selector.Selector.prototype.isUIElement */
294 isUIElement: function (element) {
295 return MochiKit.Base.findValue(['input', 'button', 'select', 'option', 'textarea', 'object'],
296 element.tagName.toLowerCase()) > -1;
297 },
298
299 /** @id MochiKit.Selector.Selector.prototype.findElements */
300 findElements: function (scope, axis) {
301 var element;
302
303 if (axis == undefined) {
304 axis = "";
305 }
306
307 function inScope(element, scope) {
308 if (axis == "") {
309 return MochiKit.DOM.isChildNode(element, scope);
310 } else if (axis == ">") {
311 return element.parentNode == scope;
312 } else if (axis == "+") {
313 return element == nextSiblingElement(scope);
314 } else if (axis == "~") {
315 var sibling = scope;
316 while (sibling = nextSiblingElement(sibling)) {
317 if (element == sibling) {
318 return true;
319 }
320 }
321 return false;
322 } else {
323 throw "Invalid axis: " + axis;
324 }
325 }
326
327 if (element = MochiKit.DOM.getElement(this.params.id)) {
328 if (this.match(element)) {
329 if (!scope || inScope(element, scope)) {
330 return [element];
331 }
332 }
333 }
334
335 function nextSiblingElement(node) {
336 node = node.nextSibling;
337 while (node && node.nodeType != 1) {
338 node = node.nextSibling;
339 }
340 return node;
341 }
342
343 if (axis == "") {
344 scope = (scope || MochiKit.DOM.currentDocument()).getElementsByTagName(this.params.tagName || '*');
345 } else if (axis == ">") {
346 if (!scope) {
347 throw "> combinator not allowed without preceeding expression";
348 }
349 scope = MochiKit.Base.filter(function (node) {
350 return node.nodeType == 1;
351 }, scope.childNodes);
352 } else if (axis == "+") {
353 if (!scope) {
354 throw "+ combinator not allowed without preceeding expression";
355 }
356 scope = nextSiblingElement(scope) && [nextSiblingElement(scope)];
357 } else if (axis == "~") {
358 if (!scope) {
359 throw "~ combinator not allowed without preceeding expression";
360 }
361 var newscope = [];
362 while (nextSiblingElement(scope)) {
363 scope = nextSiblingElement(scope);
364 newscope.push(scope);
365 }
366 scope = newscope;
367 }
368
369 if (!scope) {
370 return [];
371 }
372
373 var results = MochiKit.Base.filter(MochiKit.Base.bind(function (scopeElt) {
374 return this.match(scopeElt);
375 }, this), scope);
376
377 return results;
378 },
379
380 /** @id MochiKit.Selector.Selector.prototype.repr */
381 repr: function () {
382 return 'Selector(' + this.expression + ')';
383 },
384
385 toString: MochiKit.Base.forwardCall("repr")
386 };
387
388 MochiKit.Base.update(MochiKit.Selector, {
389
390 /** @id MochiKit.Selector.findChildElements */
391 findChildElements: function (element, expressions) {
392 return MochiKit.Base.flattenArray(MochiKit.Base.map(function (expression) {
393 var nextScope = "";
394 return MochiKit.Iter.reduce(function (results, expr) {
395 if (match = expr.match(/^[>+~]$/)) {
396 nextScope = match[0];
397 return results;
398 } else {
399 var selector = new MochiKit.Selector.Selector(expr);
400 var elements = MochiKit.Iter.reduce(function (elements, result) {
401 return MochiKit.Base.extend(elements, selector.findElements(result || element, nextScope));
402 }, results, []);
403 nextScope = "";
404 return elements;
405 }
406 }, expression.replace(/(^\s+|\s+$)/g, '').split(/\s+/), [null]);
407 }, expressions));
408 },
409
410 findDocElements: function () {
411 return MochiKit.Selector.findChildElements(MochiKit.DOM.currentDocument(), arguments);
412 },
413
414 __new__: function () {
415 var m = MochiKit.Base;
416
417 this.$$ = this.findDocElements;
418
419 this.EXPORT_TAGS = {
420 ":common": this.EXPORT,
421 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
422 };
423
424 m.nameFunctions(this);
425 }
426 });
427
428 MochiKit.Selector.__new__();
429
430 MochiKit.Base._exportSymbols(this, MochiKit.Selector);
431
This diff has been collapsed as it changes many lines, (903 lines changed) Show them Hide them
@@ -0,0 +1,903 b''
1 /***
2
3 MochiKit.Signal 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2006 Jonathan Gardner, Beau Hartshorne, Bob Ippolito. All rights Reserved.
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide('MochiKit.Signal');
13 dojo.require('MochiKit.Base');
14 dojo.require('MochiKit.DOM');
15 dojo.require('MochiKit.Style');
16 }
17 if (typeof(JSAN) != 'undefined') {
18 JSAN.use('MochiKit.Base', []);
19 JSAN.use('MochiKit.DOM', []);
20 JSAN.use('MochiKit.Style', []);
21 }
22
23 try {
24 if (typeof(MochiKit.Base) == 'undefined') {
25 throw '';
26 }
27 } catch (e) {
28 throw 'MochiKit.Signal depends on MochiKit.Base!';
29 }
30
31 try {
32 if (typeof(MochiKit.DOM) == 'undefined') {
33 throw '';
34 }
35 } catch (e) {
36 throw 'MochiKit.Signal depends on MochiKit.DOM!';
37 }
38
39 try {
40 if (typeof(MochiKit.Style) == 'undefined') {
41 throw '';
42 }
43 } catch (e) {
44 throw 'MochiKit.Signal depends on MochiKit.Style!';
45 }
46
47 if (typeof(MochiKit.Signal) == 'undefined') {
48 MochiKit.Signal = {};
49 }
50
51 MochiKit.Signal.NAME = 'MochiKit.Signal';
52 MochiKit.Signal.VERSION = '1.4';
53
54 MochiKit.Signal._observers = [];
55
56 /** @id MochiKit.Signal.Event */
57 MochiKit.Signal.Event = function (src, e) {
58 this._event = e || window.event;
59 this._src = src;
60 };
61
62 MochiKit.Base.update(MochiKit.Signal.Event.prototype, {
63
64 __repr__: function () {
65 var repr = MochiKit.Base.repr;
66 var str = '{event(): ' + repr(this.event()) +
67 ', src(): ' + repr(this.src()) +
68 ', type(): ' + repr(this.type()) +
69 ', target(): ' + repr(this.target());
70
71 if (this.type() &&
72 this.type().indexOf('key') === 0 ||
73 this.type().indexOf('mouse') === 0 ||
74 this.type().indexOf('click') != -1 ||
75 this.type() == 'contextmenu') {
76 str += ', modifier(): ' + '{alt: ' + repr(this.modifier().alt) +
77 ', ctrl: ' + repr(this.modifier().ctrl) +
78 ', meta: ' + repr(this.modifier().meta) +
79 ', shift: ' + repr(this.modifier().shift) +
80 ', any: ' + repr(this.modifier().any) + '}';
81 }
82
83 if (this.type() && this.type().indexOf('key') === 0) {
84 str += ', key(): {code: ' + repr(this.key().code) +
85 ', string: ' + repr(this.key().string) + '}';
86 }
87
88 if (this.type() && (
89 this.type().indexOf('mouse') === 0 ||
90 this.type().indexOf('click') != -1 ||
91 this.type() == 'contextmenu')) {
92
93 str += ', mouse(): {page: ' + repr(this.mouse().page) +
94 ', client: ' + repr(this.mouse().client);
95
96 if (this.type() != 'mousemove') {
97 str += ', button: {left: ' + repr(this.mouse().button.left) +
98 ', middle: ' + repr(this.mouse().button.middle) +
99 ', right: ' + repr(this.mouse().button.right) + '}}';
100 } else {
101 str += '}';
102 }
103 }
104 if (this.type() == 'mouseover' || this.type() == 'mouseout') {
105 str += ', relatedTarget(): ' + repr(this.relatedTarget());
106 }
107 str += '}';
108 return str;
109 },
110
111 /** @id MochiKit.Signal.Event.prototype.toString */
112 toString: function () {
113 return this.__repr__();
114 },
115
116 /** @id MochiKit.Signal.Event.prototype.src */
117 src: function () {
118 return this._src;
119 },
120
121 /** @id MochiKit.Signal.Event.prototype.event */
122 event: function () {
123 return this._event;
124 },
125
126 /** @id MochiKit.Signal.Event.prototype.type */
127 type: function () {
128 return this._event.type || undefined;
129 },
130
131 /** @id MochiKit.Signal.Event.prototype.target */
132 target: function () {
133 return this._event.target || this._event.srcElement;
134 },
135
136 _relatedTarget: null,
137 /** @id MochiKit.Signal.Event.prototype.relatedTarget */
138 relatedTarget: function () {
139 if (this._relatedTarget !== null) {
140 return this._relatedTarget;
141 }
142
143 var elem = null;
144 if (this.type() == 'mouseover') {
145 elem = (this._event.relatedTarget ||
146 this._event.fromElement);
147 } else if (this.type() == 'mouseout') {
148 elem = (this._event.relatedTarget ||
149 this._event.toElement);
150 }
151 if (elem !== null) {
152 this._relatedTarget = elem;
153 return elem;
154 }
155
156 return undefined;
157 },
158
159 _modifier: null,
160 /** @id MochiKit.Signal.Event.prototype.modifier */
161 modifier: function () {
162 if (this._modifier !== null) {
163 return this._modifier;
164 }
165 var m = {};
166 m.alt = this._event.altKey;
167 m.ctrl = this._event.ctrlKey;
168 m.meta = this._event.metaKey || false; // IE and Opera punt here
169 m.shift = this._event.shiftKey;
170 m.any = m.alt || m.ctrl || m.shift || m.meta;
171 this._modifier = m;
172 return m;
173 },
174
175 _key: null,
176 /** @id MochiKit.Signal.Event.prototype.key */
177 key: function () {
178 if (this._key !== null) {
179 return this._key;
180 }
181 var k = {};
182 if (this.type() && this.type().indexOf('key') === 0) {
183
184 /*
185
186 If you're looking for a special key, look for it in keydown or
187 keyup, but never keypress. If you're looking for a Unicode
188 chracter, look for it with keypress, but never keyup or
189 keydown.
190
191 Notes:
192
193 FF key event behavior:
194 key event charCode keyCode
195 DOWN ku,kd 0 40
196 DOWN kp 0 40
197 ESC ku,kd 0 27
198 ESC kp 0 27
199 a ku,kd 0 65
200 a kp 97 0
201 shift+a ku,kd 0 65
202 shift+a kp 65 0
203 1 ku,kd 0 49
204 1 kp 49 0
205 shift+1 ku,kd 0 0
206 shift+1 kp 33 0
207
208 IE key event behavior:
209 (IE doesn't fire keypress events for special keys.)
210 key event keyCode
211 DOWN ku,kd 40
212 DOWN kp undefined
213 ESC ku,kd 27
214 ESC kp 27
215 a ku,kd 65
216 a kp 97
217 shift+a ku,kd 65
218 shift+a kp 65
219 1 ku,kd 49
220 1 kp 49
221 shift+1 ku,kd 49
222 shift+1 kp 33
223
224 Safari key event behavior:
225 (Safari sets charCode and keyCode to something crazy for
226 special keys.)
227 key event charCode keyCode
228 DOWN ku,kd 63233 40
229 DOWN kp 63233 63233
230 ESC ku,kd 27 27
231 ESC kp 27 27
232 a ku,kd 97 65
233 a kp 97 97
234 shift+a ku,kd 65 65
235 shift+a kp 65 65
236 1 ku,kd 49 49
237 1 kp 49 49
238 shift+1 ku,kd 33 49
239 shift+1 kp 33 33
240
241 */
242
243 /* look for special keys here */
244 if (this.type() == 'keydown' || this.type() == 'keyup') {
245 k.code = this._event.keyCode;
246 k.string = (MochiKit.Signal._specialKeys[k.code] ||
247 'KEY_UNKNOWN');
248 this._key = k;
249 return k;
250
251 /* look for characters here */
252 } else if (this.type() == 'keypress') {
253
254 /*
255
256 Special key behavior:
257
258 IE: does not fire keypress events for special keys
259 FF: sets charCode to 0, and sets the correct keyCode
260 Safari: sets keyCode and charCode to something stupid
261
262 */
263
264 k.code = 0;
265 k.string = '';
266
267 if (typeof(this._event.charCode) != 'undefined' &&
268 this._event.charCode !== 0 &&
269 !MochiKit.Signal._specialMacKeys[this._event.charCode]) {
270 k.code = this._event.charCode;
271 k.string = String.fromCharCode(k.code);
272 } else if (this._event.keyCode &&
273 typeof(this._event.charCode) == 'undefined') { // IE
274 k.code = this._event.keyCode;
275 k.string = String.fromCharCode(k.code);
276 }
277
278 this._key = k;
279 return k;
280 }
281 }
282 return undefined;
283 },
284
285 _mouse: null,
286 /** @id MochiKit.Signal.Event.prototype.mouse */
287 mouse: function () {
288 if (this._mouse !== null) {
289 return this._mouse;
290 }
291
292 var m = {};
293 var e = this._event;
294
295 if (this.type() && (
296 this.type().indexOf('mouse') === 0 ||
297 this.type().indexOf('click') != -1 ||
298 this.type() == 'contextmenu')) {
299
300 m.client = new MochiKit.Style.Coordinates(0, 0);
301 if (e.clientX || e.clientY) {
302 m.client.x = (!e.clientX || e.clientX < 0) ? 0 : e.clientX;
303 m.client.y = (!e.clientY || e.clientY < 0) ? 0 : e.clientY;
304 }
305
306 m.page = new MochiKit.Style.Coordinates(0, 0);
307 if (e.pageX || e.pageY) {
308 m.page.x = (!e.pageX || e.pageX < 0) ? 0 : e.pageX;
309 m.page.y = (!e.pageY || e.pageY < 0) ? 0 : e.pageY;
310 } else {
311 /*
312
313 The IE shortcut can be off by two. We fix it. See:
314 http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/getboundingclientrect.asp
315
316 This is similar to the method used in
317 MochiKit.Style.getElementPosition().
318
319 */
320 var de = MochiKit.DOM._document.documentElement;
321 var b = MochiKit.DOM._document.body;
322
323 m.page.x = e.clientX +
324 (de.scrollLeft || b.scrollLeft) -
325 (de.clientLeft || 0);
326
327 m.page.y = e.clientY +
328 (de.scrollTop || b.scrollTop) -
329 (de.clientTop || 0);
330
331 }
332 if (this.type() != 'mousemove') {
333 m.button = {};
334 m.button.left = false;
335 m.button.right = false;
336 m.button.middle = false;
337
338 /* we could check e.button, but which is more consistent */
339 if (e.which) {
340 m.button.left = (e.which == 1);
341 m.button.middle = (e.which == 2);
342 m.button.right = (e.which == 3);
343
344 /*
345
346 Mac browsers and right click:
347
348 - Safari doesn't fire any click events on a right
349 click:
350 http://bugs.webkit.org/show_bug.cgi?id=6595
351
352 - Firefox fires the event, and sets ctrlKey = true
353
354 - Opera fires the event, and sets metaKey = true
355
356 oncontextmenu is fired on right clicks between
357 browsers and across platforms.
358
359 */
360
361 } else {
362 m.button.left = !!(e.button & 1);
363 m.button.right = !!(e.button & 2);
364 m.button.middle = !!(e.button & 4);
365 }
366 }
367 this._mouse = m;
368 return m;
369 }
370 return undefined;
371 },
372
373 /** @id MochiKit.Signal.Event.prototype.stop */
374 stop: function () {
375 this.stopPropagation();
376 this.preventDefault();
377 },
378
379 /** @id MochiKit.Signal.Event.prototype.stopPropagation */
380 stopPropagation: function () {
381 if (this._event.stopPropagation) {
382 this._event.stopPropagation();
383 } else {
384 this._event.cancelBubble = true;
385 }
386 },
387
388 /** @id MochiKit.Signal.Event.prototype.preventDefault */
389 preventDefault: function () {
390 if (this._event.preventDefault) {
391 this._event.preventDefault();
392 } else if (this._confirmUnload === null) {
393 this._event.returnValue = false;
394 }
395 },
396
397 _confirmUnload: null,
398
399 /** @id MochiKit.Signal.Event.prototype.confirmUnload */
400 confirmUnload: function (msg) {
401 if (this.type() == 'beforeunload') {
402 this._confirmUnload = msg;
403 this._event.returnValue = msg;
404 }
405 }
406 });
407
408 /* Safari sets keyCode to these special values onkeypress. */
409 MochiKit.Signal._specialMacKeys = {
410 3: 'KEY_ENTER',
411 63289: 'KEY_NUM_PAD_CLEAR',
412 63276: 'KEY_PAGE_UP',
413 63277: 'KEY_PAGE_DOWN',
414 63275: 'KEY_END',
415 63273: 'KEY_HOME',
416 63234: 'KEY_ARROW_LEFT',
417 63232: 'KEY_ARROW_UP',
418 63235: 'KEY_ARROW_RIGHT',
419 63233: 'KEY_ARROW_DOWN',
420 63302: 'KEY_INSERT',
421 63272: 'KEY_DELETE'
422 };
423
424 /* for KEY_F1 - KEY_F12 */
425 (function () {
426 var _specialMacKeys = MochiKit.Signal._specialMacKeys;
427 for (i = 63236; i <= 63242; i++) {
428 // no F0
429 _specialMacKeys[i] = 'KEY_F' + (i - 63236 + 1);
430 }
431 })();
432
433 /* Standard keyboard key codes. */
434 MochiKit.Signal._specialKeys = {
435 8: 'KEY_BACKSPACE',
436 9: 'KEY_TAB',
437 12: 'KEY_NUM_PAD_CLEAR', // weird, for Safari and Mac FF only
438 13: 'KEY_ENTER',
439 16: 'KEY_SHIFT',
440 17: 'KEY_CTRL',
441 18: 'KEY_ALT',
442 19: 'KEY_PAUSE',
443 20: 'KEY_CAPS_LOCK',
444 27: 'KEY_ESCAPE',
445 32: 'KEY_SPACEBAR',
446 33: 'KEY_PAGE_UP',
447 34: 'KEY_PAGE_DOWN',
448 35: 'KEY_END',
449 36: 'KEY_HOME',
450 37: 'KEY_ARROW_LEFT',
451 38: 'KEY_ARROW_UP',
452 39: 'KEY_ARROW_RIGHT',
453 40: 'KEY_ARROW_DOWN',
454 44: 'KEY_PRINT_SCREEN',
455 45: 'KEY_INSERT',
456 46: 'KEY_DELETE',
457 59: 'KEY_SEMICOLON', // weird, for Safari and IE only
458 91: 'KEY_WINDOWS_LEFT',
459 92: 'KEY_WINDOWS_RIGHT',
460 93: 'KEY_SELECT',
461 106: 'KEY_NUM_PAD_ASTERISK',
462 107: 'KEY_NUM_PAD_PLUS_SIGN',
463 109: 'KEY_NUM_PAD_HYPHEN-MINUS',
464 110: 'KEY_NUM_PAD_FULL_STOP',
465 111: 'KEY_NUM_PAD_SOLIDUS',
466 144: 'KEY_NUM_LOCK',
467 145: 'KEY_SCROLL_LOCK',
468 186: 'KEY_SEMICOLON',
469 187: 'KEY_EQUALS_SIGN',
470 188: 'KEY_COMMA',
471 189: 'KEY_HYPHEN-MINUS',
472 190: 'KEY_FULL_STOP',
473 191: 'KEY_SOLIDUS',
474 192: 'KEY_GRAVE_ACCENT',
475 219: 'KEY_LEFT_SQUARE_BRACKET',
476 220: 'KEY_REVERSE_SOLIDUS',
477 221: 'KEY_RIGHT_SQUARE_BRACKET',
478 222: 'KEY_APOSTROPHE'
479 // undefined: 'KEY_UNKNOWN'
480 };
481
482 (function () {
483 /* for KEY_0 - KEY_9 */
484 var _specialKeys = MochiKit.Signal._specialKeys;
485 for (var i = 48; i <= 57; i++) {
486 _specialKeys[i] = 'KEY_' + (i - 48);
487 }
488
489 /* for KEY_A - KEY_Z */
490 for (i = 65; i <= 90; i++) {
491 _specialKeys[i] = 'KEY_' + String.fromCharCode(i);
492 }
493
494 /* for KEY_NUM_PAD_0 - KEY_NUM_PAD_9 */
495 for (i = 96; i <= 105; i++) {
496 _specialKeys[i] = 'KEY_NUM_PAD_' + (i - 96);
497 }
498
499 /* for KEY_F1 - KEY_F12 */
500 for (i = 112; i <= 123; i++) {
501 // no F0
502 _specialKeys[i] = 'KEY_F' + (i - 112 + 1);
503 }
504 })();
505
506 /* Internal object to keep track of created signals. */
507 MochiKit.Signal.Ident = function (ident) {
508 this.source = ident.source;
509 this.signal = ident.signal;
510 this.listener = ident.listener;
511 this.isDOM = ident.isDOM;
512 this.objOrFunc = ident.objOrFunc;
513 this.funcOrStr = ident.funcOrStr;
514 this.connected = ident.connected;
515 };
516
517 MochiKit.Signal.Ident.prototype = {};
518
519 MochiKit.Base.update(MochiKit.Signal, {
520
521 __repr__: function () {
522 return '[' + this.NAME + ' ' + this.VERSION + ']';
523 },
524
525 toString: function () {
526 return this.__repr__();
527 },
528
529 _unloadCache: function () {
530 var self = MochiKit.Signal;
531 var observers = self._observers;
532
533 for (var i = 0; i < observers.length; i++) {
534 if (observers[i].signal !== 'onload' && observers[i].signal !== 'onunload') {
535 self._disconnect(observers[i]);
536 }
537 }
538 },
539
540 _listener: function (src, sig, func, obj, isDOM) {
541 var self = MochiKit.Signal;
542 var E = self.Event;
543 if (!isDOM) {
544 /* We don't want to re-bind already bound methods */
545 if (typeof(func.im_self) == 'undefined') {
546 return MochiKit.Base.bind(func, obj);
547 } else {
548 return func;
549 }
550 }
551 obj = obj || src;
552 if (typeof(func) == "string") {
553 if (sig === 'onload' || sig === 'onunload') {
554 return function (nativeEvent) {
555 obj[func].apply(obj, [new E(src, nativeEvent)]);
556
557 var ident = new MochiKit.Signal.Ident({
558 source: src, signal: sig, objOrFunc: obj, funcOrStr: func});
559
560 MochiKit.Signal._disconnect(ident);
561 };
562 } else {
563 return function (nativeEvent) {
564 obj[func].apply(obj, [new E(src, nativeEvent)]);
565 };
566 }
567 } else {
568 if (sig === 'onload' || sig === 'onunload') {
569 return function (nativeEvent) {
570 func.apply(obj, [new E(src, nativeEvent)]);
571
572 var ident = new MochiKit.Signal.Ident({
573 source: src, signal: sig, objOrFunc: func});
574
575 MochiKit.Signal._disconnect(ident);
576 };
577 } else {
578 return function (nativeEvent) {
579 func.apply(obj, [new E(src, nativeEvent)]);
580 };
581 }
582 }
583 },
584
585 _browserAlreadyHasMouseEnterAndLeave: function () {
586 return /MSIE/.test(navigator.userAgent);
587 },
588
589 _mouseEnterListener: function (src, sig, func, obj) {
590 var E = MochiKit.Signal.Event;
591 return function (nativeEvent) {
592 var e = new E(src, nativeEvent);
593 try {
594 e.relatedTarget().nodeName;
595 } catch (err) {
596 /* probably hit a permission denied error; possibly one of
597 * firefox's screwy anonymous DIVs inside an input element.
598 * Allow this event to propogate up.
599 */
600 return;
601 }
602 e.stop();
603 if (MochiKit.DOM.isChildNode(e.relatedTarget(), src)) {
604 /* We've moved between our node and a child. Ignore. */
605 return;
606 }
607 e.type = function () { return sig; };
608 if (typeof(func) == "string") {
609 return obj[func].apply(obj, [e]);
610 } else {
611 return func.apply(obj, [e]);
612 }
613 };
614 },
615
616 _getDestPair: function (objOrFunc, funcOrStr) {
617 var obj = null;
618 var func = null;
619 if (typeof(funcOrStr) != 'undefined') {
620 obj = objOrFunc;
621 func = funcOrStr;
622 if (typeof(funcOrStr) == 'string') {
623 if (typeof(objOrFunc[funcOrStr]) != "function") {
624 throw new Error("'funcOrStr' must be a function on 'objOrFunc'");
625 }
626 } else if (typeof(funcOrStr) != 'function') {
627 throw new Error("'funcOrStr' must be a function or string");
628 }
629 } else if (typeof(objOrFunc) != "function") {
630 throw new Error("'objOrFunc' must be a function if 'funcOrStr' is not given");
631 } else {
632 func = objOrFunc;
633 }
634 return [obj, func];
635 },
636
637 /** @id MochiKit.Signal.connect */
638 connect: function (src, sig, objOrFunc/* optional */, funcOrStr) {
639 src = MochiKit.DOM.getElement(src);
640 var self = MochiKit.Signal;
641
642 if (typeof(sig) != 'string') {
643 throw new Error("'sig' must be a string");
644 }
645
646 var destPair = self._getDestPair(objOrFunc, funcOrStr);
647 var obj = destPair[0];
648 var func = destPair[1];
649 if (typeof(obj) == 'undefined' || obj === null) {
650 obj = src;
651 }
652
653 var isDOM = !!(src.addEventListener || src.attachEvent);
654 if (isDOM && (sig === "onmouseenter" || sig === "onmouseleave")
655 && !self._browserAlreadyHasMouseEnterAndLeave()) {
656 var listener = self._mouseEnterListener(src, sig.substr(2), func, obj);
657 if (sig === "onmouseenter") {
658 sig = "onmouseover";
659 } else {
660 sig = "onmouseout";
661 }
662 } else {
663 var listener = self._listener(src, sig, func, obj, isDOM);
664 }
665
666 if (src.addEventListener) {
667 src.addEventListener(sig.substr(2), listener, false);
668 } else if (src.attachEvent) {
669 src.attachEvent(sig, listener); // useCapture unsupported
670 }
671
672 var ident = new MochiKit.Signal.Ident({
673 source: src,
674 signal: sig,
675 listener: listener,
676 isDOM: isDOM,
677 objOrFunc: objOrFunc,
678 funcOrStr: funcOrStr,
679 connected: true
680 });
681 self._observers.push(ident);
682
683 if (!isDOM && typeof(src.__connect__) == 'function') {
684 var args = MochiKit.Base.extend([ident], arguments, 1);
685 src.__connect__.apply(src, args);
686 }
687
688 return ident;
689 },
690
691 _disconnect: function (ident) {
692 // already disconnected
693 if (!ident.connected) {
694 return;
695 }
696 ident.connected = false;
697 // check isDOM
698 if (!ident.isDOM) {
699 return;
700 }
701 var src = ident.source;
702 var sig = ident.signal;
703 var listener = ident.listener;
704
705 if (src.removeEventListener) {
706 src.removeEventListener(sig.substr(2), listener, false);
707 } else if (src.detachEvent) {
708 src.detachEvent(sig, listener); // useCapture unsupported
709 } else {
710 throw new Error("'src' must be a DOM element");
711 }
712 },
713
714 /** @id MochiKit.Signal.disconnect */
715 disconnect: function (ident) {
716 var self = MochiKit.Signal;
717 var observers = self._observers;
718 var m = MochiKit.Base;
719 if (arguments.length > 1) {
720 // compatibility API
721 var src = MochiKit.DOM.getElement(arguments[0]);
722 var sig = arguments[1];
723 var obj = arguments[2];
724 var func = arguments[3];
725 for (var i = observers.length - 1; i >= 0; i--) {
726 var o = observers[i];
727 if (o.source === src && o.signal === sig && o.objOrFunc === obj && o.funcOrStr === func) {
728 self._disconnect(o);
729 if (!self._lock) {
730 observers.splice(i, 1);
731 } else {
732 self._dirty = true;
733 }
734 return true;
735 }
736 }
737 } else {
738 var idx = m.findIdentical(observers, ident);
739 if (idx >= 0) {
740 self._disconnect(ident);
741 if (!self._lock) {
742 observers.splice(idx, 1);
743 } else {
744 self._dirty = true;
745 }
746 return true;
747 }
748 }
749 return false;
750 },
751
752 /** @id MochiKit.Signal.disconnectAllTo */
753 disconnectAllTo: function (objOrFunc, /* optional */funcOrStr) {
754 var self = MochiKit.Signal;
755 var observers = self._observers;
756 var disconnect = self._disconnect;
757 var locked = self._lock;
758 var dirty = self._dirty;
759 if (typeof(funcOrStr) === 'undefined') {
760 funcOrStr = null;
761 }
762 for (var i = observers.length - 1; i >= 0; i--) {
763 var ident = observers[i];
764 if (ident.objOrFunc === objOrFunc &&
765 (funcOrStr === null || ident.funcOrStr === funcOrStr)) {
766 disconnect(ident);
767 if (locked) {
768 dirty = true;
769 } else {
770 observers.splice(i, 1);
771 }
772 }
773 }
774 self._dirty = dirty;
775 },
776
777 /** @id MochiKit.Signal.disconnectAll */
778 disconnectAll: function (src/* optional */, sig) {
779 src = MochiKit.DOM.getElement(src);
780 var m = MochiKit.Base;
781 var signals = m.flattenArguments(m.extend(null, arguments, 1));
782 var self = MochiKit.Signal;
783 var disconnect = self._disconnect;
784 var observers = self._observers;
785 var i, ident;
786 var locked = self._lock;
787 var dirty = self._dirty;
788 if (signals.length === 0) {
789 // disconnect all
790 for (i = observers.length - 1; i >= 0; i--) {
791 ident = observers[i];
792 if (ident.source === src) {
793 disconnect(ident);
794 if (!locked) {
795 observers.splice(i, 1);
796 } else {
797 dirty = true;
798 }
799 }
800 }
801 } else {
802 var sigs = {};
803 for (i = 0; i < signals.length; i++) {
804 sigs[signals[i]] = true;
805 }
806 for (i = observers.length - 1; i >= 0; i--) {
807 ident = observers[i];
808 if (ident.source === src && ident.signal in sigs) {
809 disconnect(ident);
810 if (!locked) {
811 observers.splice(i, 1);
812 } else {
813 dirty = true;
814 }
815 }
816 }
817 }
818 self._dirty = dirty;
819 },
820
821 /** @id MochiKit.Signal.signal */
822 signal: function (src, sig) {
823 var self = MochiKit.Signal;
824 var observers = self._observers;
825 src = MochiKit.DOM.getElement(src);
826 var args = MochiKit.Base.extend(null, arguments, 2);
827 var errors = [];
828 self._lock = true;
829 for (var i = 0; i < observers.length; i++) {
830 var ident = observers[i];
831 if (ident.source === src && ident.signal === sig &&
832 ident.connected) {
833 try {
834 ident.listener.apply(src, args);
835 } catch (e) {
836 errors.push(e);
837 }
838 }
839 }
840 self._lock = false;
841 if (self._dirty) {
842 self._dirty = false;
843 for (var i = observers.length - 1; i >= 0; i--) {
844 if (!observers[i].connected) {
845 observers.splice(i, 1);
846 }
847 }
848 }
849 if (errors.length == 1) {
850 throw errors[0];
851 } else if (errors.length > 1) {
852 var e = new Error("Multiple errors thrown in handling 'sig', see errors property");
853 e.errors = errors;
854 throw e;
855 }
856 }
857
858 });
859
860 MochiKit.Signal.EXPORT_OK = [];
861
862 MochiKit.Signal.EXPORT = [
863 'connect',
864 'disconnect',
865 'signal',
866 'disconnectAll',
867 'disconnectAllTo'
868 ];
869
870 MochiKit.Signal.__new__ = function (win) {
871 var m = MochiKit.Base;
872 this._document = document;
873 this._window = win;
874 this._lock = false;
875 this._dirty = false;
876
877 try {
878 this.connect(window, 'onunload', this._unloadCache);
879 } catch (e) {
880 // pass: might not be a browser
881 }
882
883 this.EXPORT_TAGS = {
884 ':common': this.EXPORT,
885 ':all': m.concat(this.EXPORT, this.EXPORT_OK)
886 };
887
888 m.nameFunctions(this);
889 };
890
891 MochiKit.Signal.__new__(this);
892
893 //
894 // XXX: Internet Explorer blows
895 //
896 if (MochiKit.__export__) {
897 connect = MochiKit.Signal.connect;
898 disconnect = MochiKit.Signal.disconnect;
899 disconnectAll = MochiKit.Signal.disconnectAll;
900 signal = MochiKit.Signal.signal;
901 }
902
903 MochiKit.Base._exportSymbols(this, MochiKit.Signal);
This diff has been collapsed as it changes many lines, (589 lines changed) Show them Hide them
@@ -0,0 +1,589 b''
1 /***
2 Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
3 Mochi-ized By Thomas Herve (_firstname_@nimail.org)
4
5 See scriptaculous.js for full license.
6
7 ***/
8
9 if (typeof(dojo) != 'undefined') {
10 dojo.provide('MochiKit.Sortable');
11 dojo.require('MochiKit.Base');
12 dojo.require('MochiKit.DOM');
13 dojo.require('MochiKit.Iter');
14 }
15
16 if (typeof(JSAN) != 'undefined') {
17 JSAN.use("MochiKit.Base", []);
18 JSAN.use("MochiKit.DOM", []);
19 JSAN.use("MochiKit.Iter", []);
20 }
21
22 try {
23 if (typeof(MochiKit.Base) == 'undefined' ||
24 typeof(MochiKit.DOM) == 'undefined' ||
25 typeof(MochiKit.Iter) == 'undefined') {
26 throw "";
27 }
28 } catch (e) {
29 throw "MochiKit.DragAndDrop depends on MochiKit.Base, MochiKit.DOM and MochiKit.Iter!";
30 }
31
32 if (typeof(MochiKit.Sortable) == 'undefined') {
33 MochiKit.Sortable = {};
34 }
35
36 MochiKit.Sortable.NAME = 'MochiKit.Sortable';
37 MochiKit.Sortable.VERSION = '1.4';
38
39 MochiKit.Sortable.__repr__ = function () {
40 return '[' + this.NAME + ' ' + this.VERSION + ']';
41 };
42
43 MochiKit.Sortable.toString = function () {
44 return this.__repr__();
45 };
46
47 MochiKit.Sortable.EXPORT = [
48 ];
49
50 MochiKit.Sortable.EXPORT_OK = [
51 ];
52
53 MochiKit.Base.update(MochiKit.Sortable, {
54 /***
55
56 Manage sortables. Mainly use the create function to add a sortable.
57
58 ***/
59 sortables: {},
60
61 _findRootElement: function (element) {
62 while (element.tagName.toUpperCase() != "BODY") {
63 if (element.id && MochiKit.Sortable.sortables[element.id]) {
64 return element;
65 }
66 element = element.parentNode;
67 }
68 },
69
70 /** @id MochiKit.Sortable.options */
71 options: function (element) {
72 element = MochiKit.Sortable._findRootElement(MochiKit.DOM.getElement(element));
73 if (!element) {
74 return;
75 }
76 return MochiKit.Sortable.sortables[element.id];
77 },
78
79 /** @id MochiKit.Sortable.destroy */
80 destroy: function (element){
81 var s = MochiKit.Sortable.options(element);
82 var b = MochiKit.Base;
83 var d = MochiKit.DragAndDrop;
84
85 if (s) {
86 MochiKit.Signal.disconnect(s.startHandle);
87 MochiKit.Signal.disconnect(s.endHandle);
88 b.map(function (dr) {
89 d.Droppables.remove(dr);
90 }, s.droppables);
91 b.map(function (dr) {
92 dr.destroy();
93 }, s.draggables);
94
95 delete MochiKit.Sortable.sortables[s.element.id];
96 }
97 },
98
99 /** @id MochiKit.Sortable.create */
100 create: function (element, options) {
101 element = MochiKit.DOM.getElement(element);
102 var self = MochiKit.Sortable;
103
104 /** @id MochiKit.Sortable.options */
105 options = MochiKit.Base.update({
106
107 /** @id MochiKit.Sortable.element */
108 element: element,
109
110 /** @id MochiKit.Sortable.tag */
111 tag: 'li', // assumes li children, override with tag: 'tagname'
112
113 /** @id MochiKit.Sortable.dropOnEmpty */
114 dropOnEmpty: false,
115
116 /** @id MochiKit.Sortable.tree */
117 tree: false,
118
119 /** @id MochiKit.Sortable.treeTag */
120 treeTag: 'ul',
121
122 /** @id MochiKit.Sortable.overlap */
123 overlap: 'vertical', // one of 'vertical', 'horizontal'
124
125 /** @id MochiKit.Sortable.constraint */
126 constraint: 'vertical', // one of 'vertical', 'horizontal', false
127 // also takes array of elements (or ids); or false
128
129 /** @id MochiKit.Sortable.containment */
130 containment: [element],
131
132 /** @id MochiKit.Sortable.handle */
133 handle: false, // or a CSS class
134
135 /** @id MochiKit.Sortable.only */
136 only: false,
137
138 /** @id MochiKit.Sortable.hoverclass */
139 hoverclass: null,
140
141 /** @id MochiKit.Sortable.ghosting */
142 ghosting: false,
143
144 /** @id MochiKit.Sortable.scroll */
145 scroll: false,
146
147 /** @id MochiKit.Sortable.scrollSensitivity */
148 scrollSensitivity: 20,
149
150 /** @id MochiKit.Sortable.scrollSpeed */
151 scrollSpeed: 15,
152
153 /** @id MochiKit.Sortable.format */
154 format: /^[^_]*_(.*)$/,
155
156 /** @id MochiKit.Sortable.onChange */
157 onChange: MochiKit.Base.noop,
158
159 /** @id MochiKit.Sortable.onUpdate */
160 onUpdate: MochiKit.Base.noop,
161
162 /** @id MochiKit.Sortable.accept */
163 accept: null
164 }, options);
165
166 // clear any old sortable with same element
167 self.destroy(element);
168
169 // build options for the draggables
170 var options_for_draggable = {
171 revert: true,
172 ghosting: options.ghosting,
173 scroll: options.scroll,
174 scrollSensitivity: options.scrollSensitivity,
175 scrollSpeed: options.scrollSpeed,
176 constraint: options.constraint,
177 handle: options.handle
178 };
179
180 if (options.starteffect) {
181 options_for_draggable.starteffect = options.starteffect;
182 }
183
184 if (options.reverteffect) {
185 options_for_draggable.reverteffect = options.reverteffect;
186 } else if (options.ghosting) {
187 options_for_draggable.reverteffect = function (innerelement) {
188 innerelement.style.top = 0;
189 innerelement.style.left = 0;
190 };
191 }
192
193 if (options.endeffect) {
194 options_for_draggable.endeffect = options.endeffect;
195 }
196
197 if (options.zindex) {
198 options_for_draggable.zindex = options.zindex;
199 }
200
201 // build options for the droppables
202 var options_for_droppable = {
203 overlap: options.overlap,
204 containment: options.containment,
205 hoverclass: options.hoverclass,
206 onhover: self.onHover,
207 tree: options.tree,
208 accept: options.accept
209 }
210
211 var options_for_tree = {
212 onhover: self.onEmptyHover,
213 overlap: options.overlap,
214 containment: options.containment,
215 hoverclass: options.hoverclass,
216 accept: options.accept
217 }
218
219 // fix for gecko engine
220 MochiKit.DOM.removeEmptyTextNodes(element);
221
222 options.draggables = [];
223 options.droppables = [];
224
225 // drop on empty handling
226 if (options.dropOnEmpty || options.tree) {
227 new MochiKit.DragAndDrop.Droppable(element, options_for_tree);
228 options.droppables.push(element);
229 }
230 MochiKit.Base.map(function (e) {
231 // handles are per-draggable
232 var handle = options.handle ?
233 MochiKit.DOM.getFirstElementByTagAndClassName(null,
234 options.handle, e) : e;
235 options.draggables.push(
236 new MochiKit.DragAndDrop.Draggable(e,
237 MochiKit.Base.update(options_for_draggable,
238 {handle: handle})));
239 new MochiKit.DragAndDrop.Droppable(e, options_for_droppable);
240 if (options.tree) {
241 e.treeNode = element;
242 }
243 options.droppables.push(e);
244 }, (self.findElements(element, options) || []));
245
246 if (options.tree) {
247 MochiKit.Base.map(function (e) {
248 new MochiKit.DragAndDrop.Droppable(e, options_for_tree);
249 e.treeNode = element;
250 options.droppables.push(e);
251 }, (self.findTreeElements(element, options) || []));
252 }
253
254 // keep reference
255 self.sortables[element.id] = options;
256
257 options.lastValue = self.serialize(element);
258 options.startHandle = MochiKit.Signal.connect(MochiKit.DragAndDrop.Draggables, 'start',
259 MochiKit.Base.partial(self.onStart, element));
260 options.endHandle = MochiKit.Signal.connect(MochiKit.DragAndDrop.Draggables, 'end',
261 MochiKit.Base.partial(self.onEnd, element));
262 },
263
264 /** @id MochiKit.Sortable.onStart */
265 onStart: function (element, draggable) {
266 var self = MochiKit.Sortable;
267 var options = self.options(element);
268 options.lastValue = self.serialize(options.element);
269 },
270
271 /** @id MochiKit.Sortable.onEnd */
272 onEnd: function (element, draggable) {
273 var self = MochiKit.Sortable;
274 self.unmark();
275 var options = self.options(element);
276 if (options.lastValue != self.serialize(options.element)) {
277 options.onUpdate(options.element);
278 }
279 },
280
281 // return all suitable-for-sortable elements in a guaranteed order
282
283 /** @id MochiKit.Sortable.findElements */
284 findElements: function (element, options) {
285 return MochiKit.Sortable.findChildren(
286 element, options.only, options.tree ? true : false, options.tag);
287 },
288
289 /** @id MochiKit.Sortable.findTreeElements */
290 findTreeElements: function (element, options) {
291 return MochiKit.Sortable.findChildren(
292 element, options.only, options.tree ? true : false, options.treeTag);
293 },
294
295 /** @id MochiKit.Sortable.findChildren */
296 findChildren: function (element, only, recursive, tagName) {
297 if (!element.hasChildNodes()) {
298 return null;
299 }
300 tagName = tagName.toUpperCase();
301 if (only) {
302 only = MochiKit.Base.flattenArray([only]);
303 }
304 var elements = [];
305 MochiKit.Base.map(function (e) {
306 if (e.tagName &&
307 e.tagName.toUpperCase() == tagName &&
308 (!only ||
309 MochiKit.Iter.some(only, function (c) {
310 return MochiKit.DOM.hasElementClass(e, c);
311 }))) {
312 elements.push(e);
313 }
314 if (recursive) {
315 var grandchildren = MochiKit.Sortable.findChildren(e, only, recursive, tagName);
316 if (grandchildren && grandchildren.length > 0) {
317 elements = elements.concat(grandchildren);
318 }
319 }
320 }, element.childNodes);
321 return elements;
322 },
323
324 /** @id MochiKit.Sortable.onHover */
325 onHover: function (element, dropon, overlap) {
326 if (MochiKit.DOM.isParent(dropon, element)) {
327 return;
328 }
329 var self = MochiKit.Sortable;
330
331 if (overlap > .33 && overlap < .66 && self.options(dropon).tree) {
332 return;
333 } else if (overlap > 0.5) {
334 self.mark(dropon, 'before');
335 if (dropon.previousSibling != element) {
336 var oldParentNode = element.parentNode;
337 element.style.visibility = 'hidden'; // fix gecko rendering
338 dropon.parentNode.insertBefore(element, dropon);
339 if (dropon.parentNode != oldParentNode) {
340 self.options(oldParentNode).onChange(element);
341 }
342 self.options(dropon.parentNode).onChange(element);
343 }
344 } else {
345 self.mark(dropon, 'after');
346 var nextElement = dropon.nextSibling || null;
347 if (nextElement != element) {
348 var oldParentNode = element.parentNode;
349 element.style.visibility = 'hidden'; // fix gecko rendering
350 dropon.parentNode.insertBefore(element, nextElement);
351 if (dropon.parentNode != oldParentNode) {
352 self.options(oldParentNode).onChange(element);
353 }
354 self.options(dropon.parentNode).onChange(element);
355 }
356 }
357 },
358
359 _offsetSize: function (element, type) {
360 if (type == 'vertical' || type == 'height') {
361 return element.offsetHeight;
362 } else {
363 return element.offsetWidth;
364 }
365 },
366
367 /** @id MochiKit.Sortable.onEmptyHover */
368 onEmptyHover: function (element, dropon, overlap) {
369 var oldParentNode = element.parentNode;
370 var self = MochiKit.Sortable;
371 var droponOptions = self.options(dropon);
372
373 if (!MochiKit.DOM.isParent(dropon, element)) {
374 var index;
375
376 var children = self.findElements(dropon, {tag: droponOptions.tag,
377 only: droponOptions.only});
378 var child = null;
379
380 if (children) {
381 var offset = self._offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
382
383 for (index = 0; index < children.length; index += 1) {
384 if (offset - self._offsetSize(children[index], droponOptions.overlap) >= 0) {
385 offset -= self._offsetSize(children[index], droponOptions.overlap);
386 } else if (offset - (self._offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
387 child = index + 1 < children.length ? children[index + 1] : null;
388 break;
389 } else {
390 child = children[index];
391 break;
392 }
393 }
394 }
395
396 dropon.insertBefore(element, child);
397
398 self.options(oldParentNode).onChange(element);
399 droponOptions.onChange(element);
400 }
401 },
402
403 /** @id MochiKit.Sortable.unmark */
404 unmark: function () {
405 var m = MochiKit.Sortable._marker;
406 if (m) {
407 MochiKit.Style.hideElement(m);
408 }
409 },
410
411 /** @id MochiKit.Sortable.mark */
412 mark: function (dropon, position) {
413 // mark on ghosting only
414 var d = MochiKit.DOM;
415 var self = MochiKit.Sortable;
416 var sortable = self.options(dropon.parentNode);
417 if (sortable && !sortable.ghosting) {
418 return;
419 }
420
421 if (!self._marker) {
422 self._marker = d.getElement('dropmarker') ||
423 document.createElement('DIV');
424 MochiKit.Style.hideElement(self._marker);
425 d.addElementClass(self._marker, 'dropmarker');
426 self._marker.style.position = 'absolute';
427 document.getElementsByTagName('body').item(0).appendChild(self._marker);
428 }
429 var offsets = MochiKit.Position.cumulativeOffset(dropon);
430 self._marker.style.left = offsets.x + 'px';
431 self._marker.style.top = offsets.y + 'px';
432
433 if (position == 'after') {
434 if (sortable.overlap == 'horizontal') {
435 self._marker.style.left = (offsets.x + dropon.clientWidth) + 'px';
436 } else {
437 self._marker.style.top = (offsets.y + dropon.clientHeight) + 'px';
438 }
439 }
440 MochiKit.Style.showElement(self._marker);
441 },
442
443 _tree: function (element, options, parent) {
444 var self = MochiKit.Sortable;
445 var children = self.findElements(element, options) || [];
446
447 for (var i = 0; i < children.length; ++i) {
448 var match = children[i].id.match(options.format);
449
450 if (!match) {
451 continue;
452 }
453
454 var child = {
455 id: encodeURIComponent(match ? match[1] : null),
456 element: element,
457 parent: parent,
458 children: [],
459 position: parent.children.length,
460 container: self._findChildrenElement(children[i], options.treeTag.toUpperCase())
461 }
462
463 /* Get the element containing the children and recurse over it */
464 if (child.container) {
465 self._tree(child.container, options, child)
466 }
467
468 parent.children.push (child);
469 }
470
471 return parent;
472 },
473
474 /* Finds the first element of the given tag type within a parent element.
475 Used for finding the first LI[ST] within a L[IST]I[TEM].*/
476 _findChildrenElement: function (element, containerTag) {
477 if (element && element.hasChildNodes) {
478 containerTag = containerTag.toUpperCase();
479 for (var i = 0; i < element.childNodes.length; ++i) {
480 if (element.childNodes[i].tagName.toUpperCase() == containerTag) {
481 return element.childNodes[i];
482 }
483 }
484 }
485 return null;
486 },
487
488 /** @id MochiKit.Sortable.tree */
489 tree: function (element, options) {
490 element = MochiKit.DOM.getElement(element);
491 var sortableOptions = MochiKit.Sortable.options(element);
492 options = MochiKit.Base.update({
493 tag: sortableOptions.tag,
494 treeTag: sortableOptions.treeTag,
495 only: sortableOptions.only,
496 name: element.id,
497 format: sortableOptions.format
498 }, options || {});
499
500 var root = {
501 id: null,
502 parent: null,
503 children: new Array,
504 container: element,
505 position: 0
506 }
507
508 return MochiKit.Sortable._tree(element, options, root);
509 },
510
511 /**
512 * Specifies the sequence for the Sortable.
513 * @param {Node} element Element to use as the Sortable.
514 * @param {Object} newSequence New sequence to use.
515 * @param {Object} options Options to use fro the Sortable.
516 */
517 setSequence: function (element, newSequence, options) {
518 var self = MochiKit.Sortable;
519 var b = MochiKit.Base;
520 element = MochiKit.DOM.getElement(element);
521 options = b.update(self.options(element), options || {});
522
523 var nodeMap = {};
524 b.map(function (n) {
525 var m = n.id.match(options.format);
526 if (m) {
527 nodeMap[m[1]] = [n, n.parentNode];
528 }
529 n.parentNode.removeChild(n);
530 }, self.findElements(element, options));
531
532 b.map(function (ident) {
533 var n = nodeMap[ident];
534 if (n) {
535 n[1].appendChild(n[0]);
536 delete nodeMap[ident];
537 }
538 }, newSequence);
539 },
540
541 /* Construct a [i] index for a particular node */
542 _constructIndex: function (node) {
543 var index = '';
544 do {
545 if (node.id) {
546 index = '[' + node.position + ']' + index;
547 }
548 } while ((node = node.parent) != null);
549 return index;
550 },
551
552 /** @id MochiKit.Sortable.sequence */
553 sequence: function (element, options) {
554 element = MochiKit.DOM.getElement(element);
555 var self = MochiKit.Sortable;
556 var options = MochiKit.Base.update(self.options(element), options || {});
557
558 return MochiKit.Base.map(function (item) {
559 return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
560 }, MochiKit.DOM.getElement(self.findElements(element, options) || []));
561 },
562
563 /**
564 * Serializes the content of a Sortable. Useful to send this content through a XMLHTTPRequest.
565 * These options override the Sortable options for the serialization only.
566 * @param {Node} element Element to serialize.
567 * @param {Object} options Serialization options.
568 */
569 serialize: function (element, options) {
570 element = MochiKit.DOM.getElement(element);
571 var self = MochiKit.Sortable;
572 options = MochiKit.Base.update(self.options(element), options || {});
573 var name = encodeURIComponent(options.name || element.id);
574
575 if (options.tree) {
576 return MochiKit.Base.flattenArray(MochiKit.Base.map(function (item) {
577 return [name + self._constructIndex(item) + "[id]=" +
578 encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
579 }, self.tree(element, options).children)).join('&');
580 } else {
581 return MochiKit.Base.map(function (item) {
582 return name + "[]=" + encodeURIComponent(item);
583 }, self.sequence(element, options)).join('&');
584 }
585 }
586 });
587
588 // trunk compatibility
589 MochiKit.Sortable.Sortable = MochiKit.Sortable;
@@ -0,0 +1,445 b''
1 /***
2
3 MochiKit.Style 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005-2006 Bob Ippolito, Beau Hartshorne. All rights Reserved.
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide('MochiKit.Style');
13 dojo.require('MochiKit.Base');
14 dojo.require('MochiKit.DOM');
15 }
16 if (typeof(JSAN) != 'undefined') {
17 JSAN.use('MochiKit.Base', []);
18 JSAN.use('MochiKit.DOM', []);
19 }
20
21 try {
22 if (typeof(MochiKit.Base) == 'undefined') {
23 throw '';
24 }
25 } catch (e) {
26 throw 'MochiKit.Style depends on MochiKit.Base!';
27 }
28
29 try {
30 if (typeof(MochiKit.DOM) == 'undefined') {
31 throw '';
32 }
33 } catch (e) {
34 throw 'MochiKit.Style depends on MochiKit.DOM!';
35 }
36
37
38 if (typeof(MochiKit.Style) == 'undefined') {
39 MochiKit.Style = {};
40 }
41
42 MochiKit.Style.NAME = 'MochiKit.Style';
43 MochiKit.Style.VERSION = '1.4';
44 MochiKit.Style.__repr__ = function () {
45 return '[' + this.NAME + ' ' + this.VERSION + ']';
46 };
47 MochiKit.Style.toString = function () {
48 return this.__repr__();
49 };
50
51 MochiKit.Style.EXPORT_OK = [];
52
53 MochiKit.Style.EXPORT = [
54 'setStyle',
55 'setOpacity',
56 'getStyle',
57 'getElementDimensions',
58 'elementDimensions', // deprecated
59 'setElementDimensions',
60 'getElementPosition',
61 'elementPosition', // deprecated
62 'setElementPosition',
63 'setDisplayForElement',
64 'hideElement',
65 'showElement',
66 'getViewportDimensions',
67 'getViewportPosition',
68 'Dimensions',
69 'Coordinates'
70 ];
71
72
73 /*
74
75 Dimensions
76
77 */
78 /** @id MochiKit.Style.Dimensions */
79 MochiKit.Style.Dimensions = function (w, h) {
80 this.w = w;
81 this.h = h;
82 };
83
84 MochiKit.Style.Dimensions.prototype.__repr__ = function () {
85 var repr = MochiKit.Base.repr;
86 return '{w: ' + repr(this.w) + ', h: ' + repr(this.h) + '}';
87 };
88
89 MochiKit.Style.Dimensions.prototype.toString = function () {
90 return this.__repr__();
91 };
92
93
94 /*
95
96 Coordinates
97
98 */
99 /** @id MochiKit.Style.Coordinates */
100 MochiKit.Style.Coordinates = function (x, y) {
101 this.x = x;
102 this.y = y;
103 };
104
105 MochiKit.Style.Coordinates.prototype.__repr__ = function () {
106 var repr = MochiKit.Base.repr;
107 return '{x: ' + repr(this.x) + ', y: ' + repr(this.y) + '}';
108 };
109
110 MochiKit.Style.Coordinates.prototype.toString = function () {
111 return this.__repr__();
112 };
113
114
115 MochiKit.Base.update(MochiKit.Style, {
116
117 /** @id MochiKit.Style.getStyle */
118 getStyle: function (elem, cssProperty) {
119 var dom = MochiKit.DOM;
120 var d = dom._document;
121
122 elem = dom.getElement(elem);
123 cssProperty = MochiKit.Base.camelize(cssProperty);
124
125 if (!elem || elem == d) {
126 return undefined;
127 }
128 if (cssProperty == 'opacity' && elem.filters) {
129 var opacity = (MochiKit.Style.getStyle(elem, 'filter') || '').match(/alpha\(opacity=(.*)\)/);
130 if (opacity && opacity[1]) {
131 return parseFloat(opacity[1]) / 100;
132 }
133 return 1.0;
134 }
135 var value = elem.style ? elem.style[cssProperty] : null;
136 if (!value) {
137 if (d.defaultView && d.defaultView.getComputedStyle) {
138 var css = d.defaultView.getComputedStyle(elem, null);
139 cssProperty = cssProperty.replace(/([A-Z])/g, '-$1'
140 ).toLowerCase(); // from dojo.style.toSelectorCase
141 value = css ? css.getPropertyValue(cssProperty) : null;
142 } else if (elem.currentStyle) {
143 value = elem.currentStyle[cssProperty];
144 }
145 }
146 if (cssProperty == 'opacity') {
147 value = parseFloat(value);
148 }
149
150 if (/Opera/.test(navigator.userAgent) && (MochiKit.Base.find(['left', 'top', 'right', 'bottom'], cssProperty) != -1)) {
151 if (MochiKit.Style.getStyle(elem, 'position') == 'static') {
152 value = 'auto';
153 }
154 }
155
156 return value == 'auto' ? null : value;
157 },
158
159 /** @id MochiKit.Style.setStyle */
160 setStyle: function (elem, style) {
161 elem = MochiKit.DOM.getElement(elem);
162 for (var name in style) {
163 if (name == 'opacity') {
164 MochiKit.Style.setOpacity(elem, style[name]);
165 } else {
166 elem.style[MochiKit.Base.camelize(name)] = style[name];
167 }
168 }
169 },
170
171 /** @id MochiKit.Style.setOpacity */
172 setOpacity: function (elem, o) {
173 elem = MochiKit.DOM.getElement(elem);
174 var self = MochiKit.Style;
175 if (o == 1) {
176 var toSet = /Gecko/.test(navigator.userAgent) && !(/Konqueror|AppleWebKit|KHTML/.test(navigator.userAgent));
177 elem.style["opacity"] = toSet ? 0.999999 : 1.0;
178 if (/MSIE/.test(navigator.userAgent)) {
179 elem.style['filter'] =
180 self.getStyle(elem, 'filter').replace(/alpha\([^\)]*\)/gi, '');
181 }
182 } else {
183 if (o < 0.00001) {
184 o = 0;
185 }
186 elem.style["opacity"] = o;
187 if (/MSIE/.test(navigator.userAgent)) {
188 elem.style['filter'] =
189 self.getStyle(elem, 'filter').replace(/alpha\([^\)]*\)/gi, '') + 'alpha(opacity=' + o * 100 + ')';
190 }
191 }
192 },
193
194 /*
195
196 getElementPosition is adapted from YAHOO.util.Dom.getXY v0.9.0.
197 Copyright: Copyright (c) 2006, Yahoo! Inc. All rights reserved.
198 License: BSD, http://developer.yahoo.net/yui/license.txt
199
200 */
201
202 /** @id MochiKit.Style.getElementPosition */
203 getElementPosition: function (elem, /* optional */relativeTo) {
204 var self = MochiKit.Style;
205 var dom = MochiKit.DOM;
206 elem = dom.getElement(elem);
207
208 if (!elem ||
209 (!(elem.x && elem.y) &&
210 (!elem.parentNode === null ||
211 self.getStyle(elem, 'display') == 'none'))) {
212 return undefined;
213 }
214
215 var c = new self.Coordinates(0, 0);
216 var box = null;
217 var parent = null;
218
219 var d = MochiKit.DOM._document;
220 var de = d.documentElement;
221 var b = d.body;
222
223 if (!elem.parentNode && elem.x && elem.y) {
224 /* it's just a MochiKit.Style.Coordinates object */
225 c.x += elem.x || 0;
226 c.y += elem.y || 0;
227 } else if (elem.getBoundingClientRect) { // IE shortcut
228 /*
229
230 The IE shortcut can be off by two. We fix it. See:
231 http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/getboundingclientrect.asp
232
233 This is similar to the method used in
234 MochiKit.Signal.Event.mouse().
235
236 */
237 box = elem.getBoundingClientRect();
238
239 c.x += box.left +
240 (de.scrollLeft || b.scrollLeft) -
241 (de.clientLeft || 0);
242
243 c.y += box.top +
244 (de.scrollTop || b.scrollTop) -
245 (de.clientTop || 0);
246
247 } else if (elem.offsetParent) {
248 c.x += elem.offsetLeft;
249 c.y += elem.offsetTop;
250 parent = elem.offsetParent;
251
252 if (parent != elem) {
253 while (parent) {
254 c.x += parent.offsetLeft;
255 c.y += parent.offsetTop;
256 parent = parent.offsetParent;
257 }
258 }
259
260 /*
261
262 Opera < 9 and old Safari (absolute) incorrectly account for
263 body offsetTop and offsetLeft.
264
265 */
266 var ua = navigator.userAgent.toLowerCase();
267 if ((typeof(opera) != 'undefined' &&
268 parseFloat(opera.version()) < 9) ||
269 (ua.indexOf('AppleWebKit') != -1 &&
270 self.getStyle(elem, 'position') == 'absolute')) {
271
272 c.x -= b.offsetLeft;
273 c.y -= b.offsetTop;
274
275 }
276 }
277
278 if (typeof(relativeTo) != 'undefined') {
279 relativeTo = arguments.callee(relativeTo);
280 if (relativeTo) {
281 c.x -= (relativeTo.x || 0);
282 c.y -= (relativeTo.y || 0);
283 }
284 }
285
286 if (elem.parentNode) {
287 parent = elem.parentNode;
288 } else {
289 parent = null;
290 }
291
292 while (parent) {
293 var tagName = parent.tagName.toUpperCase();
294 if (tagName === 'BODY' || tagName === 'HTML') {
295 break;
296 }
297 var disp = self.getStyle(parent, 'display');
298 // Handle strange Opera bug for some display
299 if (disp != 'inline' && disp != 'table-row') {
300 c.x -= parent.scrollLeft;
301 c.y -= parent.scrollTop;
302 }
303 if (parent.parentNode) {
304 parent = parent.parentNode;
305 } else {
306 parent = null;
307 }
308 }
309
310 return c;
311 },
312
313 /** @id MochiKit.Style.setElementPosition */
314 setElementPosition: function (elem, newPos/* optional */, units) {
315 elem = MochiKit.DOM.getElement(elem);
316 if (typeof(units) == 'undefined') {
317 units = 'px';
318 }
319 var newStyle = {};
320 var isUndefNull = MochiKit.Base.isUndefinedOrNull;
321 if (!isUndefNull(newPos.x)) {
322 newStyle['left'] = newPos.x + units;
323 }
324 if (!isUndefNull(newPos.y)) {
325 newStyle['top'] = newPos.y + units;
326 }
327 MochiKit.DOM.updateNodeAttributes(elem, {'style': newStyle});
328 },
329
330 /** @id MochiKit.Style.getElementDimensions */
331 getElementDimensions: function (elem) {
332 var self = MochiKit.Style;
333 var dom = MochiKit.DOM;
334 if (typeof(elem.w) == 'number' || typeof(elem.h) == 'number') {
335 return new self.Dimensions(elem.w || 0, elem.h || 0);
336 }
337 elem = dom.getElement(elem);
338 if (!elem) {
339 return undefined;
340 }
341 var disp = self.getStyle(elem, 'display');
342 // display can be empty/undefined on WebKit/KHTML
343 if (disp != 'none' && disp !== '' && typeof(disp) != 'undefined') {
344 return new self.Dimensions(elem.offsetWidth || 0,
345 elem.offsetHeight || 0);
346 }
347 var s = elem.style;
348 var originalVisibility = s.visibility;
349 var originalPosition = s.position;
350 s.visibility = 'hidden';
351 s.position = 'absolute';
352 s.display = '';
353 var originalWidth = elem.offsetWidth;
354 var originalHeight = elem.offsetHeight;
355 s.display = 'none';
356 s.position = originalPosition;
357 s.visibility = originalVisibility;
358 return new self.Dimensions(originalWidth, originalHeight);
359 },
360
361 /** @id MochiKit.Style.setElementDimensions */
362 setElementDimensions: function (elem, newSize/* optional */, units) {
363 elem = MochiKit.DOM.getElement(elem);
364 if (typeof(units) == 'undefined') {
365 units = 'px';
366 }
367 var newStyle = {};
368 var isUndefNull = MochiKit.Base.isUndefinedOrNull;
369 if (!isUndefNull(newSize.w)) {
370 newStyle['width'] = newSize.w + units;
371 }
372 if (!isUndefNull(newSize.h)) {
373 newStyle['height'] = newSize.h + units;
374 }
375 MochiKit.DOM.updateNodeAttributes(elem, {'style': newStyle});
376 },
377
378 /** @id MochiKit.Style.setDisplayForElement */
379 setDisplayForElement: function (display, element/*, ...*/) {
380 var elements = MochiKit.Base.extend(null, arguments, 1);
381 var getElement = MochiKit.DOM.getElement;
382 for (var i = 0; i < elements.length; i++) {
383 element = getElement(elements[i]);
384 if (element) {
385 element.style.display = display;
386 }
387 }
388 },
389
390 /** @id MochiKit.Style.getViewportDimensions */
391 getViewportDimensions: function () {
392 var d = new MochiKit.Style.Dimensions();
393
394 var w = MochiKit.DOM._window;
395 var b = MochiKit.DOM._document.body;
396
397 if (w.innerWidth) {
398 d.w = w.innerWidth;
399 d.h = w.innerHeight;
400 } else if (b.parentElement.clientWidth) {
401 d.w = b.parentElement.clientWidth;
402 d.h = b.parentElement.clientHeight;
403 } else if (b && b.clientWidth) {
404 d.w = b.clientWidth;
405 d.h = b.clientHeight;
406 }
407 return d;
408 },
409
410 /** @id MochiKit.Style.getViewportPosition */
411 getViewportPosition: function () {
412 var c = new MochiKit.Style.Coordinates(0, 0);
413 var d = MochiKit.DOM._document;
414 var de = d.documentElement;
415 var db = d.body;
416 if (de && (de.scrollTop || de.scrollLeft)) {
417 c.x = de.scrollLeft;
418 c.y = de.scrollTop;
419 } else if (db) {
420 c.x = db.scrollLeft;
421 c.y = db.scrollTop;
422 }
423 return c;
424 },
425
426 __new__: function () {
427 var m = MochiKit.Base;
428
429 this.elementPosition = this.getElementPosition;
430 this.elementDimensions = this.getElementDimensions;
431
432 this.hideElement = m.partial(this.setDisplayForElement, 'none');
433 this.showElement = m.partial(this.setDisplayForElement, 'block');
434
435 this.EXPORT_TAGS = {
436 ':common': this.EXPORT,
437 ':all': m.concat(this.EXPORT, this.EXPORT_OK)
438 };
439
440 m.nameFunctions(this);
441 }
442 });
443
444 MochiKit.Style.__new__();
445 MochiKit.Base._exportSymbols(this, MochiKit.Style);
@@ -0,0 +1,181 b''
1 /***
2
3 MochiKit.Test 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005 Bob Ippolito. All rights Reserved.
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide('MochiKit.Test');
13 dojo.require('MochiKit.Base');
14 }
15
16 if (typeof(JSAN) != 'undefined') {
17 JSAN.use("MochiKit.Base", []);
18 }
19
20 try {
21 if (typeof(MochiKit.Base) == 'undefined') {
22 throw "";
23 }
24 } catch (e) {
25 throw "MochiKit.Test depends on MochiKit.Base!";
26 }
27
28 if (typeof(MochiKit.Test) == 'undefined') {
29 MochiKit.Test = {};
30 }
31
32 MochiKit.Test.NAME = "MochiKit.Test";
33 MochiKit.Test.VERSION = "1.4";
34 MochiKit.Test.__repr__ = function () {
35 return "[" + this.NAME + " " + this.VERSION + "]";
36 };
37
38 MochiKit.Test.toString = function () {
39 return this.__repr__();
40 };
41
42
43 MochiKit.Test.EXPORT = ["runTests"];
44 MochiKit.Test.EXPORT_OK = [];
45
46 MochiKit.Test.runTests = function (obj) {
47 if (typeof(obj) == "string") {
48 obj = JSAN.use(obj);
49 }
50 var suite = new MochiKit.Test.Suite();
51 suite.run(obj);
52 };
53
54 MochiKit.Test.Suite = function () {
55 this.testIndex = 0;
56 MochiKit.Base.bindMethods(this);
57 };
58
59 MochiKit.Test.Suite.prototype = {
60 run: function (obj) {
61 try {
62 obj(this);
63 } catch (e) {
64 this.traceback(e);
65 }
66 },
67 traceback: function (e) {
68 var items = MochiKit.Iter.sorted(MochiKit.Base.items(e));
69 print("not ok " + this.testIndex + " - Error thrown");
70 for (var i = 0; i < items.length; i++) {
71 var kv = items[i];
72 if (kv[0] == "stack") {
73 kv[1] = kv[1].split(/\n/)[0];
74 }
75 this.print("# " + kv.join(": "));
76 }
77 },
78 print: function (s) {
79 print(s);
80 },
81 is: function (got, expected, /* optional */message) {
82 var res = 1;
83 var msg = null;
84 try {
85 res = MochiKit.Base.compare(got, expected);
86 } catch (e) {
87 msg = "Can not compare " + typeof(got) + ":" + typeof(expected);
88 }
89 if (res) {
90 msg = "Expected value did not compare equal";
91 }
92 if (!res) {
93 return this.testResult(true, message);
94 }
95 return this.testResult(false, message,
96 [[msg], ["got:", got], ["expected:", expected]]);
97 },
98
99 testResult: function (pass, msg, failures) {
100 this.testIndex += 1;
101 if (pass) {
102 this.print("ok " + this.testIndex + " - " + msg);
103 return;
104 }
105 this.print("not ok " + this.testIndex + " - " + msg);
106 if (failures) {
107 for (var i = 0; i < failures.length; i++) {
108 this.print("# " + failures[i].join(" "));
109 }
110 }
111 },
112
113 isDeeply: function (got, expected, /* optional */message) {
114 var m = MochiKit.Base;
115 var res = 1;
116 try {
117 res = m.compare(got, expected);
118 } catch (e) {
119 // pass
120 }
121 if (res === 0) {
122 return this.ok(true, message);
123 }
124 var gk = m.keys(got);
125 var ek = m.keys(expected);
126 gk.sort();
127 ek.sort();
128 if (m.compare(gk, ek)) {
129 // differing keys
130 var cmp = {};
131 var i;
132 for (i = 0; i < gk.length; i++) {
133 cmp[gk[i]] = "got";
134 }
135 for (i = 0; i < ek.length; i++) {
136 if (ek[i] in cmp) {
137 delete cmp[ek[i]];
138 } else {
139 cmp[ek[i]] = "expected";
140 }
141 }
142 var diffkeys = m.keys(cmp);
143 diffkeys.sort();
144 var gotkeys = [];
145 var expkeys = [];
146 while (diffkeys.length) {
147 var k = diffkeys.shift();
148 if (k in Object.prototype) {
149 continue;
150 }
151 (cmp[k] == "got" ? gotkeys : expkeys).push(k);
152 }
153
154
155 }
156
157 return this.testResult((!res), msg,
158 (msg ? [["got:", got], ["expected:", expected]] : undefined)
159 );
160 },
161
162 ok: function (res, message) {
163 return this.testResult(res, message);
164 }
165 };
166
167 MochiKit.Test.__new__ = function () {
168 var m = MochiKit.Base;
169
170 this.EXPORT_TAGS = {
171 ":common": this.EXPORT,
172 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
173 };
174
175 m.nameFunctions(this);
176
177 };
178
179 MochiKit.Test.__new__();
180
181 MochiKit.Base._exportSymbols(this, MochiKit.Test);
This diff has been collapsed as it changes many lines, (1981 lines changed) Show them Hide them
@@ -0,0 +1,1981 b''
1 /***
2
3 MochiKit.Visual 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005 Bob Ippolito and others. All rights Reserved.
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide('MochiKit.Visual');
13 dojo.require('MochiKit.Base');
14 dojo.require('MochiKit.DOM');
15 dojo.require('MochiKit.Style');
16 dojo.require('MochiKit.Color');
17 dojo.require('MochiKit.Position');
18 }
19
20 if (typeof(JSAN) != 'undefined') {
21 JSAN.use("MochiKit.Base", []);
22 JSAN.use("MochiKit.DOM", []);
23 JSAN.use("MochiKit.Style", []);
24 JSAN.use("MochiKit.Color", []);
25 JSAN.use("MochiKit.Position", []);
26 }
27
28 try {
29 if (typeof(MochiKit.Base) === 'undefined' ||
30 typeof(MochiKit.DOM) === 'undefined' ||
31 typeof(MochiKit.Style) === 'undefined' ||
32 typeof(MochiKit.Position) === 'undefined' ||
33 typeof(MochiKit.Color) === 'undefined') {
34 throw "";
35 }
36 } catch (e) {
37 throw "MochiKit.Visual depends on MochiKit.Base, MochiKit.DOM, MochiKit.Style, MochiKit.Position and MochiKit.Color!";
38 }
39
40 if (typeof(MochiKit.Visual) == "undefined") {
41 MochiKit.Visual = {};
42 }
43
44 MochiKit.Visual.NAME = "MochiKit.Visual";
45 MochiKit.Visual.VERSION = "1.4";
46
47 MochiKit.Visual.__repr__ = function () {
48 return "[" + this.NAME + " " + this.VERSION + "]";
49 };
50
51 MochiKit.Visual.toString = function () {
52 return this.__repr__();
53 };
54
55 MochiKit.Visual._RoundCorners = function (e, options) {
56 e = MochiKit.DOM.getElement(e);
57 this._setOptions(options);
58 if (this.options.__unstable__wrapElement) {
59 e = this._doWrap(e);
60 }
61
62 var color = this.options.color;
63 var C = MochiKit.Color.Color;
64 if (this.options.color === "fromElement") {
65 color = C.fromBackground(e);
66 } else if (!(color instanceof C)) {
67 color = C.fromString(color);
68 }
69 this.isTransparent = (color.asRGB().a <= 0);
70
71 var bgColor = this.options.bgColor;
72 if (this.options.bgColor === "fromParent") {
73 bgColor = C.fromBackground(e.offsetParent);
74 } else if (!(bgColor instanceof C)) {
75 bgColor = C.fromString(bgColor);
76 }
77
78 this._roundCornersImpl(e, color, bgColor);
79 };
80
81 MochiKit.Visual._RoundCorners.prototype = {
82 _doWrap: function (e) {
83 var parent = e.parentNode;
84 var doc = MochiKit.DOM.currentDocument();
85 if (typeof(doc.defaultView) === "undefined"
86 || doc.defaultView === null) {
87 return e;
88 }
89 var style = doc.defaultView.getComputedStyle(e, null);
90 if (typeof(style) === "undefined" || style === null) {
91 return e;
92 }
93 var wrapper = MochiKit.DOM.DIV({"style": {
94 display: "block",
95 // convert padding to margin
96 marginTop: style.getPropertyValue("padding-top"),
97 marginRight: style.getPropertyValue("padding-right"),
98 marginBottom: style.getPropertyValue("padding-bottom"),
99 marginLeft: style.getPropertyValue("padding-left"),
100 // remove padding so the rounding looks right
101 padding: "0px"
102 /*
103 paddingRight: "0px",
104 paddingLeft: "0px"
105 */
106 }});
107 wrapper.innerHTML = e.innerHTML;
108 e.innerHTML = "";
109 e.appendChild(wrapper);
110 return e;
111 },
112
113 _roundCornersImpl: function (e, color, bgColor) {
114 if (this.options.border) {
115 this._renderBorder(e, bgColor);
116 }
117 if (this._isTopRounded()) {
118 this._roundTopCorners(e, color, bgColor);
119 }
120 if (this._isBottomRounded()) {
121 this._roundBottomCorners(e, color, bgColor);
122 }
123 },
124
125 _renderBorder: function (el, bgColor) {
126 var borderValue = "1px solid " + this._borderColor(bgColor);
127 var borderL = "border-left: " + borderValue;
128 var borderR = "border-right: " + borderValue;
129 var style = "style='" + borderL + ";" + borderR + "'";
130 el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>";
131 },
132
133 _roundTopCorners: function (el, color, bgColor) {
134 var corner = this._createCorner(bgColor);
135 for (var i = 0; i < this.options.numSlices; i++) {
136 corner.appendChild(
137 this._createCornerSlice(color, bgColor, i, "top")
138 );
139 }
140 el.style.paddingTop = 0;
141 el.insertBefore(corner, el.firstChild);
142 },
143
144 _roundBottomCorners: function (el, color, bgColor) {
145 var corner = this._createCorner(bgColor);
146 for (var i = (this.options.numSlices - 1); i >= 0; i--) {
147 corner.appendChild(
148 this._createCornerSlice(color, bgColor, i, "bottom")
149 );
150 }
151 el.style.paddingBottom = 0;
152 el.appendChild(corner);
153 },
154
155 _createCorner: function (bgColor) {
156 var dom = MochiKit.DOM;
157 return dom.DIV({style: {backgroundColor: bgColor.toString()}});
158 },
159
160 _createCornerSlice: function (color, bgColor, n, position) {
161 var slice = MochiKit.DOM.SPAN();
162
163 var inStyle = slice.style;
164 inStyle.backgroundColor = color.toString();
165 inStyle.display = "block";
166 inStyle.height = "1px";
167 inStyle.overflow = "hidden";
168 inStyle.fontSize = "1px";
169
170 var borderColor = this._borderColor(color, bgColor);
171 if (this.options.border && n === 0) {
172 inStyle.borderTopStyle = "solid";
173 inStyle.borderTopWidth = "1px";
174 inStyle.borderLeftWidth = "0px";
175 inStyle.borderRightWidth = "0px";
176 inStyle.borderBottomWidth = "0px";
177 // assumes css compliant box model
178 inStyle.height = "0px";
179 inStyle.borderColor = borderColor.toString();
180 } else if (borderColor) {
181 inStyle.borderColor = borderColor.toString();
182 inStyle.borderStyle = "solid";
183 inStyle.borderWidth = "0px 1px";
184 }
185
186 if (!this.options.compact && (n == (this.options.numSlices - 1))) {
187 inStyle.height = "2px";
188 }
189
190 this._setMargin(slice, n, position);
191 this._setBorder(slice, n, position);
192
193 return slice;
194 },
195
196 _setOptions: function (options) {
197 this.options = {
198 corners: "all",
199 color: "fromElement",
200 bgColor: "fromParent",
201 blend: true,
202 border: false,
203 compact: false,
204 __unstable__wrapElement: false
205 };
206 MochiKit.Base.update(this.options, options);
207
208 this.options.numSlices = (this.options.compact ? 2 : 4);
209 },
210
211 _whichSideTop: function () {
212 var corners = this.options.corners;
213 if (this._hasString(corners, "all", "top")) {
214 return "";
215 }
216
217 var has_tl = (corners.indexOf("tl") != -1);
218 var has_tr = (corners.indexOf("tr") != -1);
219 if (has_tl && has_tr) {
220 return "";
221 }
222 if (has_tl) {
223 return "left";
224 }
225 if (has_tr) {
226 return "right";
227 }
228 return "";
229 },
230
231 _whichSideBottom: function () {
232 var corners = this.options.corners;
233 if (this._hasString(corners, "all", "bottom")) {
234 return "";
235 }
236
237 var has_bl = (corners.indexOf('bl') != -1);
238 var has_br = (corners.indexOf('br') != -1);
239 if (has_bl && has_br) {
240 return "";
241 }
242 if (has_bl) {
243 return "left";
244 }
245 if (has_br) {
246 return "right";
247 }
248 return "";
249 },
250
251 _borderColor: function (color, bgColor) {
252 if (color == "transparent") {
253 return bgColor;
254 } else if (this.options.border) {
255 return this.options.border;
256 } else if (this.options.blend) {
257 return bgColor.blendedColor(color);
258 }
259 return "";
260 },
261
262
263 _setMargin: function (el, n, corners) {
264 var marginSize = this._marginSize(n) + "px";
265 var whichSide = (
266 corners == "top" ? this._whichSideTop() : this._whichSideBottom()
267 );
268 var style = el.style;
269
270 if (whichSide == "left") {
271 style.marginLeft = marginSize;
272 style.marginRight = "0px";
273 } else if (whichSide == "right") {
274 style.marginRight = marginSize;
275 style.marginLeft = "0px";
276 } else {
277 style.marginLeft = marginSize;
278 style.marginRight = marginSize;
279 }
280 },
281
282 _setBorder: function (el, n, corners) {
283 var borderSize = this._borderSize(n) + "px";
284 var whichSide = (
285 corners == "top" ? this._whichSideTop() : this._whichSideBottom()
286 );
287
288 var style = el.style;
289 if (whichSide == "left") {
290 style.borderLeftWidth = borderSize;
291 style.borderRightWidth = "0px";
292 } else if (whichSide == "right") {
293 style.borderRightWidth = borderSize;
294 style.borderLeftWidth = "0px";
295 } else {
296 style.borderLeftWidth = borderSize;
297 style.borderRightWidth = borderSize;
298 }
299 },
300
301 _marginSize: function (n) {
302 if (this.isTransparent) {
303 return 0;
304 }
305
306 var o = this.options;
307 if (o.compact && o.blend) {
308 var smBlendedMarginSizes = [1, 0];
309 return smBlendedMarginSizes[n];
310 } else if (o.compact) {
311 var compactMarginSizes = [2, 1];
312 return compactMarginSizes[n];
313 } else if (o.blend) {
314 var blendedMarginSizes = [3, 2, 1, 0];
315 return blendedMarginSizes[n];
316 } else {
317 var marginSizes = [5, 3, 2, 1];
318 return marginSizes[n];
319 }
320 },
321
322 _borderSize: function (n) {
323 var o = this.options;
324 var borderSizes;
325 if (o.compact && (o.blend || this.isTransparent)) {
326 return 1;
327 } else if (o.compact) {
328 borderSizes = [1, 0];
329 } else if (o.blend) {
330 borderSizes = [2, 1, 1, 1];
331 } else if (o.border) {
332 borderSizes = [0, 2, 0, 0];
333 } else if (this.isTransparent) {
334 borderSizes = [5, 3, 2, 1];
335 } else {
336 return 0;
337 }
338 return borderSizes[n];
339 },
340
341 _hasString: function (str) {
342 for (var i = 1; i< arguments.length; i++) {
343 if (str.indexOf(arguments[i]) != -1) {
344 return true;
345 }
346 }
347 return false;
348 },
349
350 _isTopRounded: function () {
351 return this._hasString(this.options.corners,
352 "all", "top", "tl", "tr"
353 );
354 },
355
356 _isBottomRounded: function () {
357 return this._hasString(this.options.corners,
358 "all", "bottom", "bl", "br"
359 );
360 },
361
362 _hasSingleTextChild: function (el) {
363 return (el.childNodes.length == 1 && el.childNodes[0].nodeType == 3);
364 }
365 };
366
367 /** @id MochiKit.Visual.roundElement */
368 MochiKit.Visual.roundElement = function (e, options) {
369 new MochiKit.Visual._RoundCorners(e, options);
370 };
371
372 /** @id MochiKit.Visual.roundClass */
373 MochiKit.Visual.roundClass = function (tagName, className, options) {
374 var elements = MochiKit.DOM.getElementsByTagAndClassName(
375 tagName, className
376 );
377 for (var i = 0; i < elements.length; i++) {
378 MochiKit.Visual.roundElement(elements[i], options);
379 }
380 };
381
382 /** @id MochiKit.Visual.tagifyText */
383 MochiKit.Visual.tagifyText = function (element, /* optional */tagifyStyle) {
384 /***
385
386 Change a node text to character in tags.
387
388 @param tagifyStyle: the style to apply to character nodes, default to
389 'position: relative'.
390
391 ***/
392 tagifyStyle = tagifyStyle || 'position:relative';
393 if (/MSIE/.test(navigator.userAgent)) {
394 tagifyStyle += ';zoom:1';
395 }
396 element = MochiKit.DOM.getElement(element);
397 var ma = MochiKit.Base.map;
398 ma(function (child) {
399 if (child.nodeType == 3) {
400 ma(function (character) {
401 element.insertBefore(
402 MochiKit.DOM.SPAN({style: tagifyStyle},
403 character == ' ' ? String.fromCharCode(160) : character), child);
404 }, child.nodeValue.split(''));
405 MochiKit.DOM.removeElement(child);
406 }
407 }, element.childNodes);
408 };
409
410 /** @id MochiKit.Visual.forceRerendering */
411 MochiKit.Visual.forceRerendering = function (element) {
412 try {
413 element = MochiKit.DOM.getElement(element);
414 var n = document.createTextNode(' ');
415 element.appendChild(n);
416 element.removeChild(n);
417 } catch(e) {
418 }
419 };
420
421 /** @id MochiKit.Visual.multiple */
422 MochiKit.Visual.multiple = function (elements, effect, /* optional */options) {
423 /***
424
425 Launch the same effect subsequently on given elements.
426
427 ***/
428 options = MochiKit.Base.update({
429 speed: 0.1, delay: 0.0
430 }, options);
431 var masterDelay = options.delay;
432 var index = 0;
433 MochiKit.Base.map(function (innerelement) {
434 options.delay = index * options.speed + masterDelay;
435 new effect(innerelement, options);
436 index += 1;
437 }, elements);
438 };
439
440 MochiKit.Visual.PAIRS = {
441 'slide': ['slideDown', 'slideUp'],
442 'blind': ['blindDown', 'blindUp'],
443 'appear': ['appear', 'fade'],
444 'size': ['grow', 'shrink']
445 };
446
447 /** @id MochiKit.Visual.toggle */
448 MochiKit.Visual.toggle = function (element, /* optional */effect, /* optional */options) {
449 /***
450
451 Toggle an item between two state depending of its visibility, making
452 a effect between these states. Default effect is 'appear', can be
453 'slide' or 'blind'.
454
455 ***/
456 element = MochiKit.DOM.getElement(element);
457 effect = (effect || 'appear').toLowerCase();
458 options = MochiKit.Base.update({
459 queue: {position: 'end', scope: (element.id || 'global'), limit: 1}
460 }, options);
461 var v = MochiKit.Visual;
462 v[MochiKit.Style.getStyle(element, 'display') != 'none' ?
463 v.PAIRS[effect][1] : v.PAIRS[effect][0]](element, options);
464 };
465
466 /***
467
468 Transitions: define functions calculating variations depending of a position.
469
470 ***/
471
472 MochiKit.Visual.Transitions = {};
473
474 /** @id MochiKit.Visual.Transitions.linear */
475 MochiKit.Visual.Transitions.linear = function (pos) {
476 return pos;
477 };
478
479 /** @id MochiKit.Visual.Transitions.sinoidal */
480 MochiKit.Visual.Transitions.sinoidal = function (pos) {
481 return (-Math.cos(pos*Math.PI)/2) + 0.5;
482 };
483
484 /** @id MochiKit.Visual.Transitions.reverse */
485 MochiKit.Visual.Transitions.reverse = function (pos) {
486 return 1 - pos;
487 };
488
489 /** @id MochiKit.Visual.Transitions.flicker */
490 MochiKit.Visual.Transitions.flicker = function (pos) {
491 return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
492 };
493
494 /** @id MochiKit.Visual.Transitions.wobble */
495 MochiKit.Visual.Transitions.wobble = function (pos) {
496 return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
497 };
498
499 /** @id MochiKit.Visual.Transitions.pulse */
500 MochiKit.Visual.Transitions.pulse = function (pos, pulses) {
501 if (!pulses) {
502 return (Math.floor(pos*10) % 2 === 0 ?
503 (pos*10 - Math.floor(pos*10)) : 1 - (pos*10 - Math.floor(pos*10)));
504 }
505 return (Math.round((pos % (1/pulses)) * pulses) == 0 ?
506 ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) :
507 1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2)));
508 };
509
510 /** @id MochiKit.Visual.Transitions.none */
511 MochiKit.Visual.Transitions.none = function (pos) {
512 return 0;
513 };
514
515 /** @id MochiKit.Visual.Transitions.full */
516 MochiKit.Visual.Transitions.full = function (pos) {
517 return 1;
518 };
519
520 /***
521
522 Core effects
523
524 ***/
525
526 MochiKit.Visual.ScopedQueue = function () {
527 var cls = arguments.callee;
528 if (!(this instanceof cls)) {
529 return new cls();
530 }
531 this.__init__();
532 };
533
534 MochiKit.Base.update(MochiKit.Visual.ScopedQueue.prototype, {
535 __init__: function () {
536 this.effects = [];
537 this.interval = null;
538 },
539
540 /** @id MochiKit.Visual.ScopedQueue.prototype.add */
541 add: function (effect) {
542 var timestamp = new Date().getTime();
543
544 var position = (typeof(effect.options.queue) == 'string') ?
545 effect.options.queue : effect.options.queue.position;
546
547 var ma = MochiKit.Base.map;
548 switch (position) {
549 case 'front':
550 // move unstarted effects after this effect
551 ma(function (e) {
552 if (e.state == 'idle') {
553 e.startOn += effect.finishOn;
554 e.finishOn += effect.finishOn;
555 }
556 }, this.effects);
557 break;
558 case 'end':
559 var finish;
560 // start effect after last queued effect has finished
561 ma(function (e) {
562 var i = e.finishOn;
563 if (i >= (finish || i)) {
564 finish = i;
565 }
566 }, this.effects);
567 timestamp = finish || timestamp;
568 break;
569 case 'break':
570 ma(function (e) {
571 e.finalize();
572 }, this.effects);
573 break;
574 }
575
576 effect.startOn += timestamp;
577 effect.finishOn += timestamp;
578 if (!effect.options.queue.limit ||
579 this.effects.length < effect.options.queue.limit) {
580 this.effects.push(effect);
581 }
582
583 if (!this.interval) {
584 this.interval = this.startLoop(MochiKit.Base.bind(this.loop, this),
585 40);
586 }
587 },
588
589 /** @id MochiKit.Visual.ScopedQueue.prototype.startLoop */
590 startLoop: function (func, interval) {
591 return setInterval(func, interval);
592 },
593
594 /** @id MochiKit.Visual.ScopedQueue.prototype.remove */
595 remove: function (effect) {
596 this.effects = MochiKit.Base.filter(function (e) {
597 return e != effect;
598 }, this.effects);
599 if (!this.effects.length) {
600 this.stopLoop(this.interval);
601 this.interval = null;
602 }
603 },
604
605 /** @id MochiKit.Visual.ScopedQueue.prototype.stopLoop */
606 stopLoop: function (interval) {
607 clearInterval(interval);
608 },
609
610 /** @id MochiKit.Visual.ScopedQueue.prototype.loop */
611 loop: function () {
612 var timePos = new Date().getTime();
613 MochiKit.Base.map(function (effect) {
614 effect.loop(timePos);
615 }, this.effects);
616 }
617 });
618
619 MochiKit.Visual.Queues = {
620 instances: {},
621
622 get: function (queueName) {
623 if (typeof(queueName) != 'string') {
624 return queueName;
625 }
626
627 if (!this.instances[queueName]) {
628 this.instances[queueName] = new MochiKit.Visual.ScopedQueue();
629 }
630 return this.instances[queueName];
631 }
632 };
633
634 MochiKit.Visual.Queue = MochiKit.Visual.Queues.get('global');
635
636 MochiKit.Visual.DefaultOptions = {
637 transition: MochiKit.Visual.Transitions.sinoidal,
638 duration: 1.0, // seconds
639 fps: 25.0, // max. 25fps due to MochiKit.Visual.Queue implementation
640 sync: false, // true for combining
641 from: 0.0,
642 to: 1.0,
643 delay: 0.0,
644 queue: 'parallel'
645 };
646
647 MochiKit.Visual.Base = function () {};
648
649 MochiKit.Visual.Base.prototype = {
650 /***
651
652 Basic class for all Effects. Define a looping mechanism called for each step
653 of an effect. Don't instantiate it, only subclass it.
654
655 ***/
656
657 __class__ : MochiKit.Visual.Base,
658
659 /** @id MochiKit.Visual.Base.prototype.start */
660 start: function (options) {
661 var v = MochiKit.Visual;
662 this.options = MochiKit.Base.setdefault(options,
663 v.DefaultOptions);
664 this.currentFrame = 0;
665 this.state = 'idle';
666 this.startOn = this.options.delay*1000;
667 this.finishOn = this.startOn + (this.options.duration*1000);
668 this.event('beforeStart');
669 if (!this.options.sync) {
670 v.Queues.get(typeof(this.options.queue) == 'string' ?
671 'global' : this.options.queue.scope).add(this);
672 }
673 },
674
675 /** @id MochiKit.Visual.Base.prototype.loop */
676 loop: function (timePos) {
677 if (timePos >= this.startOn) {
678 if (timePos >= this.finishOn) {
679 return this.finalize();
680 }
681 var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
682 var frame =
683 Math.round(pos * this.options.fps * this.options.duration);
684 if (frame > this.currentFrame) {
685 this.render(pos);
686 this.currentFrame = frame;
687 }
688 }
689 },
690
691 /** @id MochiKit.Visual.Base.prototype.render */
692 render: function (pos) {
693 if (this.state == 'idle') {
694 this.state = 'running';
695 this.event('beforeSetup');
696 this.setup();
697 this.event('afterSetup');
698 }
699 if (this.state == 'running') {
700 if (this.options.transition) {
701 pos = this.options.transition(pos);
702 }
703 pos *= (this.options.to - this.options.from);
704 pos += this.options.from;
705 this.event('beforeUpdate');
706 this.update(pos);
707 this.event('afterUpdate');
708 }
709 },
710
711 /** @id MochiKit.Visual.Base.prototype.cancel */
712 cancel: function () {
713 if (!this.options.sync) {
714 MochiKit.Visual.Queues.get(typeof(this.options.queue) == 'string' ?
715 'global' : this.options.queue.scope).remove(this);
716 }
717 this.state = 'finished';
718 },
719
720 /** @id MochiKit.Visual.Base.prototype.finalize */
721 finalize: function () {
722 this.render(1.0);
723 this.cancel();
724 this.event('beforeFinish');
725 this.finish();
726 this.event('afterFinish');
727 },
728
729 setup: function () {
730 },
731
732 finish: function () {
733 },
734
735 update: function (position) {
736 },
737
738 /** @id MochiKit.Visual.Base.prototype.event */
739 event: function (eventName) {
740 if (this.options[eventName + 'Internal']) {
741 this.options[eventName + 'Internal'](this);
742 }
743 if (this.options[eventName]) {
744 this.options[eventName](this);
745 }
746 },
747
748 /** @id MochiKit.Visual.Base.prototype.repr */
749 repr: function () {
750 return '[' + this.__class__.NAME + ', options:' +
751 MochiKit.Base.repr(this.options) + ']';
752 }
753 };
754
755 /** @id MochiKit.Visual.Parallel */
756 MochiKit.Visual.Parallel = function (effects, options) {
757 var cls = arguments.callee;
758 if (!(this instanceof cls)) {
759 return new cls(effects, options);
760 }
761
762 this.__init__(effects, options);
763 };
764
765 MochiKit.Visual.Parallel.prototype = new MochiKit.Visual.Base();
766
767 MochiKit.Base.update(MochiKit.Visual.Parallel.prototype, {
768 /***
769
770 Run multiple effects at the same time.
771
772 ***/
773
774 __class__ : MochiKit.Visual.Parallel,
775
776 __init__: function (effects, options) {
777 this.effects = effects || [];
778 this.start(options);
779 },
780
781 /** @id MochiKit.Visual.Parallel.prototype.update */
782 update: function (position) {
783 MochiKit.Base.map(function (effect) {
784 effect.render(position);
785 }, this.effects);
786 },
787
788 /** @id MochiKit.Visual.Parallel.prototype.finish */
789 finish: function () {
790 MochiKit.Base.map(function (effect) {
791 effect.finalize();
792 }, this.effects);
793 }
794 });
795
796 /** @id MochiKit.Visual.Opacity */
797 MochiKit.Visual.Opacity = function (element, options) {
798 var cls = arguments.callee;
799 if (!(this instanceof cls)) {
800 return new cls(element, options);
801 }
802 this.__init__(element, options);
803 };
804
805 MochiKit.Visual.Opacity.prototype = new MochiKit.Visual.Base();
806
807 MochiKit.Base.update(MochiKit.Visual.Opacity.prototype, {
808 /***
809
810 Change the opacity of an element.
811
812 @param options: 'from' and 'to' change the starting and ending opacities.
813 Must be between 0.0 and 1.0. Default to current opacity and 1.0.
814
815 ***/
816
817 __class__ : MochiKit.Visual.Opacity,
818
819 __init__: function (element, /* optional */options) {
820 var b = MochiKit.Base;
821 var s = MochiKit.Style;
822 this.element = MochiKit.DOM.getElement(element);
823 // make this work on IE on elements without 'layout'
824 if (this.element.currentStyle &&
825 (!this.element.currentStyle.hasLayout)) {
826 s.setStyle(this.element, {zoom: 1});
827 }
828 options = b.update({
829 from: s.getStyle(this.element, 'opacity') || 0.0,
830 to: 1.0
831 }, options);
832 this.start(options);
833 },
834
835 /** @id MochiKit.Visual.Opacity.prototype.update */
836 update: function (position) {
837 MochiKit.Style.setStyle(this.element, {'opacity': position});
838 }
839 });
840
841 /** @id MochiKit.Visual.Move.prototype */
842 MochiKit.Visual.Move = function (element, options) {
843 var cls = arguments.callee;
844 if (!(this instanceof cls)) {
845 return new cls(element, options);
846 }
847 this.__init__(element, options);
848 };
849
850 MochiKit.Visual.Move.prototype = new MochiKit.Visual.Base();
851
852 MochiKit.Base.update(MochiKit.Visual.Move.prototype, {
853 /***
854
855 Move an element between its current position to a defined position
856
857 @param options: 'x' and 'y' for final positions, default to 0, 0.
858
859 ***/
860
861 __class__ : MochiKit.Visual.Move,
862
863 __init__: function (element, /* optional */options) {
864 this.element = MochiKit.DOM.getElement(element);
865 options = MochiKit.Base.update({
866 x: 0,
867 y: 0,
868 mode: 'relative'
869 }, options);
870 this.start(options);
871 },
872
873 /** @id MochiKit.Visual.Move.prototype.setup */
874 setup: function () {
875 // Bug in Opera: Opera returns the 'real' position of a static element
876 // or relative element that does not have top/left explicitly set.
877 // ==> Always set top and left for position relative elements in your
878 // stylesheets (to 0 if you do not need them)
879 MochiKit.DOM.makePositioned(this.element);
880
881 var s = this.element.style;
882 var originalVisibility = s.visibility;
883 var originalDisplay = s.display;
884 if (originalDisplay == 'none') {
885 s.visibility = 'hidden';
886 s.display = '';
887 }
888
889 this.originalLeft = parseFloat(MochiKit.Style.getStyle(this.element, 'left') || '0');
890 this.originalTop = parseFloat(MochiKit.Style.getStyle(this.element, 'top') || '0');
891
892 if (this.options.mode == 'absolute') {
893 // absolute movement, so we need to calc deltaX and deltaY
894 this.options.x -= this.originalLeft;
895 this.options.y -= this.originalTop;
896 }
897 if (originalDisplay == 'none') {
898 s.visibility = originalVisibility;
899 s.display = originalDisplay;
900 }
901 },
902
903 /** @id MochiKit.Visual.Move.prototype.update */
904 update: function (position) {
905 MochiKit.Style.setStyle(this.element, {
906 left: Math.round(this.options.x * position + this.originalLeft) + 'px',
907 top: Math.round(this.options.y * position + this.originalTop) + 'px'
908 });
909 }
910 });
911
912 /** @id MochiKit.Visual.Scale */
913 MochiKit.Visual.Scale = function (element, percent, options) {
914 var cls = arguments.callee;
915 if (!(this instanceof cls)) {
916 return new cls(element, percent, options);
917 }
918 this.__init__(element, percent, options);
919 };
920
921 MochiKit.Visual.Scale.prototype = new MochiKit.Visual.Base();
922
923 MochiKit.Base.update(MochiKit.Visual.Scale.prototype, {
924 /***
925
926 Change the size of an element.
927
928 @param percent: final_size = percent*original_size
929
930 @param options: several options changing scale behaviour
931
932 ***/
933
934 __class__ : MochiKit.Visual.Scale,
935
936 __init__: function (element, percent, /* optional */options) {
937 this.element = MochiKit.DOM.getElement(element);
938 options = MochiKit.Base.update({
939 scaleX: true,
940 scaleY: true,
941 scaleContent: true,
942 scaleFromCenter: false,
943 scaleMode: 'box', // 'box' or 'contents' or {} with provided values
944 scaleFrom: 100.0,
945 scaleTo: percent
946 }, options);
947 this.start(options);
948 },
949
950 /** @id MochiKit.Visual.Scale.prototype.setup */
951 setup: function () {
952 this.restoreAfterFinish = this.options.restoreAfterFinish || false;
953 this.elementPositioning = MochiKit.Style.getStyle(this.element,
954 'position');
955
956 var ma = MochiKit.Base.map;
957 var b = MochiKit.Base.bind;
958 this.originalStyle = {};
959 ma(b(function (k) {
960 this.originalStyle[k] = this.element.style[k];
961 }, this), ['top', 'left', 'width', 'height', 'fontSize']);
962
963 this.originalTop = this.element.offsetTop;
964 this.originalLeft = this.element.offsetLeft;
965
966 var fontSize = MochiKit.Style.getStyle(this.element,
967 'font-size') || '100%';
968 ma(b(function (fontSizeType) {
969 if (fontSize.indexOf(fontSizeType) > 0) {
970 this.fontSize = parseFloat(fontSize);
971 this.fontSizeType = fontSizeType;
972 }
973 }, this), ['em', 'px', '%']);
974
975 this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
976
977 if (/^content/.test(this.options.scaleMode)) {
978 this.dims = [this.element.scrollHeight, this.element.scrollWidth];
979 } else if (this.options.scaleMode == 'box') {
980 this.dims = [this.element.offsetHeight, this.element.offsetWidth];
981 } else {
982 this.dims = [this.options.scaleMode.originalHeight,
983 this.options.scaleMode.originalWidth];
984 }
985 },
986
987 /** @id MochiKit.Visual.Scale.prototype.update */
988 update: function (position) {
989 var currentScale = (this.options.scaleFrom/100.0) +
990 (this.factor * position);
991 if (this.options.scaleContent && this.fontSize) {
992 MochiKit.Style.setStyle(this.element, {
993 fontSize: this.fontSize * currentScale + this.fontSizeType
994 });
995 }
996 this.setDimensions(this.dims[0] * currentScale,
997 this.dims[1] * currentScale);
998 },
999
1000 /** @id MochiKit.Visual.Scale.prototype.finish */
1001 finish: function () {
1002 if (this.restoreAfterFinish) {
1003 MochiKit.Style.setStyle(this.element, this.originalStyle);
1004 }
1005 },
1006
1007 /** @id MochiKit.Visual.Scale.prototype.setDimensions */
1008 setDimensions: function (height, width) {
1009 var d = {};
1010 var r = Math.round;
1011 if (/MSIE/.test(navigator.userAgent)) {
1012 r = Math.ceil;
1013 }
1014 if (this.options.scaleX) {
1015 d.width = r(width) + 'px';
1016 }
1017 if (this.options.scaleY) {
1018 d.height = r(height) + 'px';
1019 }
1020 if (this.options.scaleFromCenter) {
1021 var topd = (height - this.dims[0])/2;
1022 var leftd = (width - this.dims[1])/2;
1023 if (this.elementPositioning == 'absolute') {
1024 if (this.options.scaleY) {
1025 d.top = this.originalTop - topd + 'px';
1026 }
1027 if (this.options.scaleX) {
1028 d.left = this.originalLeft - leftd + 'px';
1029 }
1030 } else {
1031 if (this.options.scaleY) {
1032 d.top = -topd + 'px';
1033 }
1034 if (this.options.scaleX) {
1035 d.left = -leftd + 'px';
1036 }
1037 }
1038 }
1039 MochiKit.Style.setStyle(this.element, d);
1040 }
1041 });
1042
1043 /** @id MochiKit.Visual.Highlight */
1044 MochiKit.Visual.Highlight = function (element, options) {
1045 var cls = arguments.callee;
1046 if (!(this instanceof cls)) {
1047 return new cls(element, options);
1048 }
1049 this.__init__(element, options);
1050 };
1051
1052 MochiKit.Visual.Highlight.prototype = new MochiKit.Visual.Base();
1053
1054 MochiKit.Base.update(MochiKit.Visual.Highlight.prototype, {
1055 /***
1056
1057 Highlight an item of the page.
1058
1059 @param options: 'startcolor' for choosing highlighting color, default
1060 to '#ffff99'.
1061
1062 ***/
1063
1064 __class__ : MochiKit.Visual.Highlight,
1065
1066 __init__: function (element, /* optional */options) {
1067 this.element = MochiKit.DOM.getElement(element);
1068 options = MochiKit.Base.update({
1069 startcolor: '#ffff99'
1070 }, options);
1071 this.start(options);
1072 },
1073
1074 /** @id MochiKit.Visual.Highlight.prototype.setup */
1075 setup: function () {
1076 var b = MochiKit.Base;
1077 var s = MochiKit.Style;
1078 // Prevent executing on elements not in the layout flow
1079 if (s.getStyle(this.element, 'display') == 'none') {
1080 this.cancel();
1081 return;
1082 }
1083 // Disable background image during the effect
1084 this.oldStyle = {
1085 backgroundImage: s.getStyle(this.element, 'background-image')
1086 };
1087 s.setStyle(this.element, {
1088 backgroundImage: 'none'
1089 });
1090
1091 if (!this.options.endcolor) {
1092 this.options.endcolor =
1093 MochiKit.Color.Color.fromBackground(this.element).toHexString();
1094 }
1095 if (b.isUndefinedOrNull(this.options.restorecolor)) {
1096 this.options.restorecolor = s.getStyle(this.element,
1097 'background-color');
1098 }
1099 // init color calculations
1100 this._base = b.map(b.bind(function (i) {
1101 return parseInt(
1102 this.options.startcolor.slice(i*2 + 1, i*2 + 3), 16);
1103 }, this), [0, 1, 2]);
1104 this._delta = b.map(b.bind(function (i) {
1105 return parseInt(this.options.endcolor.slice(i*2 + 1, i*2 + 3), 16)
1106 - this._base[i];
1107 }, this), [0, 1, 2]);
1108 },
1109
1110 /** @id MochiKit.Visual.Highlight.prototype.update */
1111 update: function (position) {
1112 var m = '#';
1113 MochiKit.Base.map(MochiKit.Base.bind(function (i) {
1114 m += MochiKit.Color.toColorPart(Math.round(this._base[i] +
1115 this._delta[i]*position));
1116 }, this), [0, 1, 2]);
1117 MochiKit.Style.setStyle(this.element, {
1118 backgroundColor: m
1119 });
1120 },
1121
1122 /** @id MochiKit.Visual.Highlight.prototype.finish */
1123 finish: function () {
1124 MochiKit.Style.setStyle(this.element,
1125 MochiKit.Base.update(this.oldStyle, {
1126 backgroundColor: this.options.restorecolor
1127 }));
1128 }
1129 });
1130
1131 /** @id MochiKit.Visual.ScrollTo */
1132 MochiKit.Visual.ScrollTo = function (element, options) {
1133 var cls = arguments.callee;
1134 if (!(this instanceof cls)) {
1135 return new cls(element, options);
1136 }
1137 this.__init__(element, options);
1138 };
1139
1140 MochiKit.Visual.ScrollTo.prototype = new MochiKit.Visual.Base();
1141
1142 MochiKit.Base.update(MochiKit.Visual.ScrollTo.prototype, {
1143 /***
1144
1145 Scroll to an element in the page.
1146
1147 ***/
1148
1149 __class__ : MochiKit.Visual.ScrollTo,
1150
1151 __init__: function (element, /* optional */options) {
1152 this.element = MochiKit.DOM.getElement(element);
1153 this.start(options);
1154 },
1155
1156 /** @id MochiKit.Visual.ScrollTo.prototype.setup */
1157 setup: function () {
1158 var p = MochiKit.Position;
1159 p.prepare();
1160 var offsets = p.cumulativeOffset(this.element);
1161 if (this.options.offset) {
1162 offsets.y += this.options.offset;
1163 }
1164 var max;
1165 if (window.innerHeight) {
1166 max = window.innerHeight - window.height;
1167 } else if (document.documentElement &&
1168 document.documentElement.clientHeight) {
1169 max = document.documentElement.clientHeight -
1170 document.body.scrollHeight;
1171 } else if (document.body) {
1172 max = document.body.clientHeight - document.body.scrollHeight;
1173 }
1174 this.scrollStart = p.windowOffset.y;
1175 this.delta = (offsets.y > max ? max : offsets.y) - this.scrollStart;
1176 },
1177
1178 /** @id MochiKit.Visual.ScrollTo.prototype.update */
1179 update: function (position) {
1180 var p = MochiKit.Position;
1181 p.prepare();
1182 window.scrollTo(p.windowOffset.x, this.scrollStart + (position * this.delta));
1183 }
1184 });
1185
1186 MochiKit.Visual.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
1187
1188 MochiKit.Visual.Morph = function (element, options) {
1189 var cls = arguments.callee;
1190 if (!(this instanceof cls)) {
1191 return new cls(element, options);
1192 }
1193 this.__init__(element, options);
1194 };
1195
1196 MochiKit.Visual.Morph.prototype = new MochiKit.Visual.Base();
1197
1198 MochiKit.Base.update(MochiKit.Visual.Morph.prototype, {
1199 /***
1200
1201 Morph effect: make a transformation from current style to the given style,
1202 automatically making a transition between the two.
1203
1204 ***/
1205
1206 __class__ : MochiKit.Visual.Morph,
1207
1208 __init__: function (element, /* optional */options) {
1209 this.element = MochiKit.DOM.getElement(element);
1210 this.start(options);
1211 },
1212
1213 /** @id MochiKit.Visual.Morph.prototype.setup */
1214 setup: function () {
1215 var b = MochiKit.Base;
1216 var style = this.options.style;
1217 this.styleStart = {};
1218 this.styleEnd = {};
1219 this.units = {};
1220 var value, unit;
1221 for (var s in style) {
1222 value = style[s];
1223 s = b.camelize(s);
1224 if (MochiKit.Visual.CSS_LENGTH.test(value)) {
1225 var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
1226 value = parseFloat(components[1]);
1227 unit = (components.length == 3) ? components[2] : null;
1228 this.styleEnd[s] = value;
1229 this.units[s] = unit;
1230 value = MochiKit.Style.getStyle(this.element, s);
1231 components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
1232 value = parseFloat(components[1]);
1233 this.styleStart[s] = value;
1234 } else {
1235 var c = MochiKit.Color.Color;
1236 value = c.fromString(value);
1237 if (value) {
1238 this.units[s] = "color";
1239 this.styleEnd[s] = value.toHexString();
1240 value = MochiKit.Style.getStyle(this.element, s);
1241 this.styleStart[s] = c.fromString(value).toHexString();
1242
1243 this.styleStart[s] = b.map(b.bind(function (i) {
1244 return parseInt(
1245 this.styleStart[s].slice(i*2 + 1, i*2 + 3), 16);
1246 }, this), [0, 1, 2]);
1247 this.styleEnd[s] = b.map(b.bind(function (i) {
1248 return parseInt(
1249 this.styleEnd[s].slice(i*2 + 1, i*2 + 3), 16);
1250 }, this), [0, 1, 2]);
1251 }
1252 }
1253 }
1254 },
1255
1256 /** @id MochiKit.Visual.Morph.prototype.update */
1257 update: function (position) {
1258 var value;
1259 for (var s in this.styleStart) {
1260 if (this.units[s] == "color") {
1261 var m = '#';
1262 var start = this.styleStart[s];
1263 var end = this.styleEnd[s];
1264 MochiKit.Base.map(MochiKit.Base.bind(function (i) {
1265 m += MochiKit.Color.toColorPart(Math.round(start[i] +
1266 (end[i] - start[i])*position));
1267 }, this), [0, 1, 2]);
1268 this.element.style[s] = m;
1269 } else {
1270 value = this.styleStart[s] + Math.round((this.styleEnd[s] - this.styleStart[s]) * position * 1000) / 1000 + this.units[s];
1271 this.element.style[s] = value;
1272 }
1273 }
1274 }
1275 });
1276
1277 /***
1278
1279 Combination effects.
1280
1281 ***/
1282
1283 /** @id MochiKit.Visual.fade */
1284 MochiKit.Visual.fade = function (element, /* optional */ options) {
1285 /***
1286
1287 Fade a given element: change its opacity and hide it in the end.
1288
1289 @param options: 'to' and 'from' to change opacity.
1290
1291 ***/
1292 var s = MochiKit.Style;
1293 var oldOpacity = s.getStyle(element, 'opacity');
1294 options = MochiKit.Base.update({
1295 from: s.getStyle(element, 'opacity') || 1.0,
1296 to: 0.0,
1297 afterFinishInternal: function (effect) {
1298 if (effect.options.to !== 0) {
1299 return;
1300 }
1301 s.hideElement(effect.element);
1302 s.setStyle(effect.element, {'opacity': oldOpacity});
1303 }
1304 }, options);
1305 return new MochiKit.Visual.Opacity(element, options);
1306 };
1307
1308 /** @id MochiKit.Visual.appear */
1309 MochiKit.Visual.appear = function (element, /* optional */ options) {
1310 /***
1311
1312 Make an element appear.
1313
1314 @param options: 'to' and 'from' to change opacity.
1315
1316 ***/
1317 var s = MochiKit.Style;
1318 var v = MochiKit.Visual;
1319 options = MochiKit.Base.update({
1320 from: (s.getStyle(element, 'display') == 'none' ? 0.0 :
1321 s.getStyle(element, 'opacity') || 0.0),
1322 to: 1.0,
1323 // force Safari to render floated elements properly
1324 afterFinishInternal: function (effect) {
1325 v.forceRerendering(effect.element);
1326 },
1327 beforeSetupInternal: function (effect) {
1328 s.setStyle(effect.element, {'opacity': effect.options.from});
1329 s.showElement(effect.element);
1330 }
1331 }, options);
1332 return new v.Opacity(element, options);
1333 };
1334
1335 /** @id MochiKit.Visual.puff */
1336 MochiKit.Visual.puff = function (element, /* optional */ options) {
1337 /***
1338
1339 'Puff' an element: grow it to double size, fading it and make it hidden.
1340
1341 ***/
1342 var s = MochiKit.Style;
1343 var v = MochiKit.Visual;
1344 element = MochiKit.DOM.getElement(element);
1345 var oldStyle = {
1346 position: s.getStyle(element, 'position'),
1347 top: element.style.top,
1348 left: element.style.left,
1349 width: element.style.width,
1350 height: element.style.height,
1351 opacity: s.getStyle(element, 'opacity')
1352 };
1353 options = MochiKit.Base.update({
1354 beforeSetupInternal: function (effect) {
1355 MochiKit.Position.absolutize(effect.effects[0].element);
1356 },
1357 afterFinishInternal: function (effect) {
1358 s.hideElement(effect.effects[0].element);
1359 s.setStyle(effect.effects[0].element, oldStyle);
1360 },
1361 scaleContent: true,
1362 scaleFromCenter: true
1363 }, options);
1364 return new v.Parallel(
1365 [new v.Scale(element, 200,
1366 {sync: true, scaleFromCenter: options.scaleFromCenter,
1367 scaleContent: options.scaleContent, restoreAfterFinish: true}),
1368 new v.Opacity(element, {sync: true, to: 0.0 })],
1369 options);
1370 };
1371
1372 /** @id MochiKit.Visual.blindUp */
1373 MochiKit.Visual.blindUp = function (element, /* optional */ options) {
1374 /***
1375
1376 Blind an element up: change its vertical size to 0.
1377
1378 ***/
1379 var d = MochiKit.DOM;
1380 element = d.getElement(element);
1381 var elemClip = d.makeClipping(element);
1382 options = MochiKit.Base.update({
1383 scaleContent: false,
1384 scaleX: false,
1385 restoreAfterFinish: true,
1386 afterFinishInternal: function (effect) {
1387 MochiKit.Style.hideElement(effect.element);
1388 d.undoClipping(effect.element, elemClip);
1389 }
1390 }, options);
1391
1392 return new MochiKit.Visual.Scale(element, 0, options);
1393 };
1394
1395 /** @id MochiKit.Visual.blindDown */
1396 MochiKit.Visual.blindDown = function (element, /* optional */ options) {
1397 /***
1398
1399 Blind an element down: restore its vertical size.
1400
1401 ***/
1402 var d = MochiKit.DOM;
1403 var s = MochiKit.Style;
1404 element = d.getElement(element);
1405 var elementDimensions = s.getElementDimensions(element);
1406 var elemClip;
1407 options = MochiKit.Base.update({
1408 scaleContent: false,
1409 scaleX: false,
1410 scaleFrom: 0,
1411 scaleMode: {originalHeight: elementDimensions.h,
1412 originalWidth: elementDimensions.w},
1413 restoreAfterFinish: true,
1414 afterSetupInternal: function (effect) {
1415 elemClip = d.makeClipping(effect.element);
1416 s.setStyle(effect.element, {height: '0px'});
1417 s.showElement(effect.element);
1418 },
1419 afterFinishInternal: function (effect) {
1420 d.undoClipping(effect.element, elemClip);
1421 }
1422 }, options);
1423 return new MochiKit.Visual.Scale(element, 100, options);
1424 };
1425
1426 /** @id MochiKit.Visual.switchOff */
1427 MochiKit.Visual.switchOff = function (element, /* optional */ options) {
1428 /***
1429
1430 Apply a switch-off-like effect.
1431
1432 ***/
1433 var d = MochiKit.DOM;
1434 element = d.getElement(element);
1435 var oldOpacity = MochiKit.Style.getStyle(element, 'opacity');
1436 var elemClip;
1437 options = MochiKit.Base.update({
1438 duration: 0.3,
1439 scaleFromCenter: true,
1440 scaleX: false,
1441 scaleContent: false,
1442 restoreAfterFinish: true,
1443 beforeSetupInternal: function (effect) {
1444 d.makePositioned(effect.element);
1445 elemClip = d.makeClipping(effect.element);
1446 },
1447 afterFinishInternal: function (effect) {
1448 MochiKit.Style.hideElement(effect.element);
1449 d.undoClipping(effect.element, elemClip);
1450 d.undoPositioned(effect.element);
1451 MochiKit.Style.setStyle(effect.element, {'opacity': oldOpacity});
1452 }
1453 }, options);
1454 var v = MochiKit.Visual;
1455 return new v.appear(element, {
1456 duration: 0.4,
1457 from: 0,
1458 transition: v.Transitions.flicker,
1459 afterFinishInternal: function (effect) {
1460 new v.Scale(effect.element, 1, options);
1461 }
1462 });
1463 };
1464
1465 /** @id MochiKit.Visual.dropOut */
1466 MochiKit.Visual.dropOut = function (element, /* optional */ options) {
1467 /***
1468
1469 Make an element fall and disappear.
1470
1471 ***/
1472 var d = MochiKit.DOM;
1473 var s = MochiKit.Style;
1474 element = d.getElement(element);
1475 var oldStyle = {
1476 top: s.getStyle(element, 'top'),
1477 left: s.getStyle(element, 'left'),
1478 opacity: s.getStyle(element, 'opacity')
1479 };
1480
1481 options = MochiKit.Base.update({
1482 duration: 0.5,
1483 distance: 100,
1484 beforeSetupInternal: function (effect) {
1485 d.makePositioned(effect.effects[0].element);
1486 },
1487 afterFinishInternal: function (effect) {
1488 s.hideElement(effect.effects[0].element);
1489 d.undoPositioned(effect.effects[0].element);
1490 s.setStyle(effect.effects[0].element, oldStyle);
1491 }
1492 }, options);
1493 var v = MochiKit.Visual;
1494 return new v.Parallel(
1495 [new v.Move(element, {x: 0, y: options.distance, sync: true}),
1496 new v.Opacity(element, {sync: true, to: 0.0})],
1497 options);
1498 };
1499
1500 /** @id MochiKit.Visual.shake */
1501 MochiKit.Visual.shake = function (element, /* optional */ options) {
1502 /***
1503
1504 Move an element from left to right several times.
1505
1506 ***/
1507 var d = MochiKit.DOM;
1508 var v = MochiKit.Visual;
1509 var s = MochiKit.Style;
1510 element = d.getElement(element);
1511 options = MochiKit.Base.update({
1512 x: -20,
1513 y: 0,
1514 duration: 0.05,
1515 afterFinishInternal: function (effect) {
1516 d.undoPositioned(effect.element);
1517 s.setStyle(effect.element, oldStyle);
1518 }
1519 }, options);
1520 var oldStyle = {
1521 top: s.getStyle(element, 'top'),
1522 left: s.getStyle(element, 'left') };
1523 return new v.Move(element,
1524 {x: 20, y: 0, duration: 0.05, afterFinishInternal: function (effect) {
1525 new v.Move(effect.element,
1526 {x: -40, y: 0, duration: 0.1, afterFinishInternal: function (effect) {
1527 new v.Move(effect.element,
1528 {x: 40, y: 0, duration: 0.1, afterFinishInternal: function (effect) {
1529 new v.Move(effect.element,
1530 {x: -40, y: 0, duration: 0.1, afterFinishInternal: function (effect) {
1531 new v.Move(effect.element,
1532 {x: 40, y: 0, duration: 0.1, afterFinishInternal: function (effect) {
1533 new v.Move(effect.element, options
1534 ) }}) }}) }}) }}) }});
1535 };
1536
1537 /** @id MochiKit.Visual.slideDown */
1538 MochiKit.Visual.slideDown = function (element, /* optional */ options) {
1539 /***
1540
1541 Slide an element down.
1542 It needs to have the content of the element wrapped in a container
1543 element with fixed height.
1544
1545 ***/
1546 var d = MochiKit.DOM;
1547 var b = MochiKit.Base;
1548 var s = MochiKit.Style;
1549 element = d.getElement(element);
1550 if (!element.firstChild) {
1551 throw "MochiKit.Visual.slideDown must be used on a element with a child";
1552 }
1553 d.removeEmptyTextNodes(element);
1554 var oldInnerBottom = s.getStyle(element.firstChild, 'bottom') || 0;
1555 var elementDimensions = s.getElementDimensions(element);
1556 var elemClip;
1557 options = b.update({
1558 scaleContent: false,
1559 scaleX: false,
1560 scaleFrom: 0,
1561 scaleMode: {originalHeight: elementDimensions.h,
1562 originalWidth: elementDimensions.w},
1563 restoreAfterFinish: true,
1564 afterSetupInternal: function (effect) {
1565 d.makePositioned(effect.element);
1566 d.makePositioned(effect.element.firstChild);
1567 if (/Opera/.test(navigator.userAgent)) {
1568 s.setStyle(effect.element, {top: ''});
1569 }
1570 elemClip = d.makeClipping(effect.element);
1571 s.setStyle(effect.element, {height: '0px'});
1572 s.showElement(effect.element);
1573 },
1574 afterUpdateInternal: function (effect) {
1575 s.setStyle(effect.element.firstChild,
1576 {bottom: (effect.dims[0] - effect.element.clientHeight) + 'px'});
1577 },
1578 afterFinishInternal: function (effect) {
1579 d.undoClipping(effect.element, elemClip);
1580 // IE will crash if child is undoPositioned first
1581 if (/MSIE/.test(navigator.userAgent)) {
1582 d.undoPositioned(effect.element);
1583 d.undoPositioned(effect.element.firstChild);
1584 } else {
1585 d.undoPositioned(effect.element.firstChild);
1586 d.undoPositioned(effect.element);
1587 }
1588 s.setStyle(effect.element.firstChild,
1589 {bottom: oldInnerBottom});
1590 }
1591 }, options);
1592
1593 return new MochiKit.Visual.Scale(element, 100, options);
1594 };
1595
1596 /** @id MochiKit.Visual.slideUp */
1597 MochiKit.Visual.slideUp = function (element, /* optional */ options) {
1598 /***
1599
1600 Slide an element up.
1601 It needs to have the content of the element wrapped in a container
1602 element with fixed height.
1603
1604 ***/
1605 var d = MochiKit.DOM;
1606 var b = MochiKit.Base;
1607 var s = MochiKit.Style;
1608 element = d.getElement(element);
1609 if (!element.firstChild) {
1610 throw "MochiKit.Visual.slideUp must be used on a element with a child";
1611 }
1612 d.removeEmptyTextNodes(element);
1613 var oldInnerBottom = s.getStyle(element.firstChild, 'bottom');
1614 var elemClip;
1615 options = b.update({
1616 scaleContent: false,
1617 scaleX: false,
1618 scaleMode: 'box',
1619 scaleFrom: 100,
1620 restoreAfterFinish: true,
1621 beforeStartInternal: function (effect) {
1622 d.makePositioned(effect.element);
1623 d.makePositioned(effect.element.firstChild);
1624 if (/Opera/.test(navigator.userAgent)) {
1625 s.setStyle(effect.element, {top: ''});
1626 }
1627 elemClip = d.makeClipping(effect.element);
1628 s.showElement(effect.element);
1629 },
1630 afterUpdateInternal: function (effect) {
1631 s.setStyle(effect.element.firstChild,
1632 {bottom: (effect.dims[0] - effect.element.clientHeight) + 'px'});
1633 },
1634 afterFinishInternal: function (effect) {
1635 s.hideElement(effect.element);
1636 d.undoClipping(effect.element, elemClip);
1637 d.undoPositioned(effect.element.firstChild);
1638 d.undoPositioned(effect.element);
1639 s.setStyle(effect.element.firstChild, {bottom: oldInnerBottom});
1640 }
1641 }, options);
1642 return new MochiKit.Visual.Scale(element, 0, options);
1643 };
1644
1645 // Bug in opera makes the TD containing this element expand for a instance
1646 // after finish
1647 /** @id MochiKit.Visual.squish */
1648 MochiKit.Visual.squish = function (element, /* optional */ options) {
1649 /***
1650
1651 Reduce an element and make it disappear.
1652
1653 ***/
1654 var d = MochiKit.DOM;
1655 var b = MochiKit.Base;
1656 var elemClip;
1657 options = b.update({
1658 restoreAfterFinish: true,
1659 beforeSetupInternal: function (effect) {
1660 elemClip = d.makeClipping(effect.element);
1661 },
1662 afterFinishInternal: function (effect) {
1663 MochiKit.Style.hideElement(effect.element);
1664 d.undoClipping(effect.element, elemClip);
1665 }
1666 }, options);
1667
1668 return new MochiKit.Visual.Scale(element, /Opera/.test(navigator.userAgent) ? 1 : 0, options);
1669 };
1670
1671 /** @id MochiKit.Visual.grow */
1672 MochiKit.Visual.grow = function (element, /* optional */ options) {
1673 /***
1674
1675 Grow an element to its original size. Make it zero-sized before
1676 if necessary.
1677
1678 ***/
1679 var d = MochiKit.DOM;
1680 var v = MochiKit.Visual;
1681 var s = MochiKit.Style;
1682 element = d.getElement(element);
1683 options = MochiKit.Base.update({
1684 direction: 'center',
1685 moveTransition: v.Transitions.sinoidal,
1686 scaleTransition: v.Transitions.sinoidal,
1687 opacityTransition: v.Transitions.full,
1688 scaleContent: true,
1689 scaleFromCenter: false
1690 }, options);
1691 var oldStyle = {
1692 top: element.style.top,
1693 left: element.style.left,
1694 height: element.style.height,
1695 width: element.style.width,
1696 opacity: s.getStyle(element, 'opacity')
1697 };
1698
1699 var dims = s.getElementDimensions(element);
1700 var initialMoveX, initialMoveY;
1701 var moveX, moveY;
1702
1703 switch (options.direction) {
1704 case 'top-left':
1705 initialMoveX = initialMoveY = moveX = moveY = 0;
1706 break;
1707 case 'top-right':
1708 initialMoveX = dims.w;
1709 initialMoveY = moveY = 0;
1710 moveX = -dims.w;
1711 break;
1712 case 'bottom-left':
1713 initialMoveX = moveX = 0;
1714 initialMoveY = dims.h;
1715 moveY = -dims.h;
1716 break;
1717 case 'bottom-right':
1718 initialMoveX = dims.w;
1719 initialMoveY = dims.h;
1720 moveX = -dims.w;
1721 moveY = -dims.h;
1722 break;
1723 case 'center':
1724 initialMoveX = dims.w / 2;
1725 initialMoveY = dims.h / 2;
1726 moveX = -dims.w / 2;
1727 moveY = -dims.h / 2;
1728 break;
1729 }
1730
1731 var optionsParallel = MochiKit.Base.update({
1732 beforeSetupInternal: function (effect) {
1733 s.setStyle(effect.effects[0].element, {height: '0px'});
1734 s.showElement(effect.effects[0].element);
1735 },
1736 afterFinishInternal: function (effect) {
1737 d.undoClipping(effect.effects[0].element);
1738 d.undoPositioned(effect.effects[0].element);
1739 s.setStyle(effect.effects[0].element, oldStyle);
1740 }
1741 }, options);
1742
1743 return new v.Move(element, {
1744 x: initialMoveX,
1745 y: initialMoveY,
1746 duration: 0.01,
1747 beforeSetupInternal: function (effect) {
1748 s.hideElement(effect.element);
1749 d.makeClipping(effect.element);
1750 d.makePositioned(effect.element);
1751 },
1752 afterFinishInternal: function (effect) {
1753 new v.Parallel(
1754 [new v.Opacity(effect.element, {
1755 sync: true, to: 1.0, from: 0.0,
1756 transition: options.opacityTransition
1757 }),
1758 new v.Move(effect.element, {
1759 x: moveX, y: moveY, sync: true,
1760 transition: options.moveTransition
1761 }),
1762 new v.Scale(effect.element, 100, {
1763 scaleMode: {originalHeight: dims.h,
1764 originalWidth: dims.w},
1765 sync: true,
1766 scaleFrom: /Opera/.test(navigator.userAgent) ? 1 : 0,
1767 transition: options.scaleTransition,
1768 scaleContent: options.scaleContent,
1769 scaleFromCenter: options.scaleFromCenter,
1770 restoreAfterFinish: true
1771 })
1772 ], optionsParallel
1773 );
1774 }
1775 });
1776 };
1777
1778 /** @id MochiKit.Visual.shrink */
1779 MochiKit.Visual.shrink = function (element, /* optional */ options) {
1780 /***
1781
1782 Shrink an element and make it disappear.
1783
1784 ***/
1785 var d = MochiKit.DOM;
1786 var v = MochiKit.Visual;
1787 var s = MochiKit.Style;
1788 element = d.getElement(element);
1789 options = MochiKit.Base.update({
1790 direction: 'center',
1791 moveTransition: v.Transitions.sinoidal,
1792 scaleTransition: v.Transitions.sinoidal,
1793 opacityTransition: v.Transitions.none,
1794 scaleContent: true,
1795 scaleFromCenter: false
1796 }, options);
1797 var oldStyle = {
1798 top: element.style.top,
1799 left: element.style.left,
1800 height: element.style.height,
1801 width: element.style.width,
1802 opacity: s.getStyle(element, 'opacity')
1803 };
1804
1805 var dims = s.getElementDimensions(element);
1806 var moveX, moveY;
1807
1808 switch (options.direction) {
1809 case 'top-left':
1810 moveX = moveY = 0;
1811 break;
1812 case 'top-right':
1813 moveX = dims.w;
1814 moveY = 0;
1815 break;
1816 case 'bottom-left':
1817 moveX = 0;
1818 moveY = dims.h;
1819 break;
1820 case 'bottom-right':
1821 moveX = dims.w;
1822 moveY = dims.h;
1823 break;
1824 case 'center':
1825 moveX = dims.w / 2;
1826 moveY = dims.h / 2;
1827 break;
1828 }
1829 var elemClip;
1830
1831 var optionsParallel = MochiKit.Base.update({
1832 beforeStartInternal: function (effect) {
1833 elemClip = d.makePositioned(effect.effects[0].element);
1834 d.makeClipping(effect.effects[0].element);
1835 },
1836 afterFinishInternal: function (effect) {
1837 s.hideElement(effect.effects[0].element);
1838 d.undoClipping(effect.effects[0].element, elemClip);
1839 d.undoPositioned(effect.effects[0].element);
1840 s.setStyle(effect.effects[0].element, oldStyle);
1841 }
1842 }, options);
1843
1844 return new v.Parallel(
1845 [new v.Opacity(element, {
1846 sync: true, to: 0.0, from: 1.0,
1847 transition: options.opacityTransition
1848 }),
1849 new v.Scale(element, /Opera/.test(navigator.userAgent) ? 1 : 0, {
1850 sync: true, transition: options.scaleTransition,
1851 scaleContent: options.scaleContent,
1852 scaleFromCenter: options.scaleFromCenter,
1853 restoreAfterFinish: true
1854 }),
1855 new v.Move(element, {
1856 x: moveX, y: moveY, sync: true, transition: options.moveTransition
1857 })
1858 ], optionsParallel
1859 );
1860 };
1861
1862 /** @id MochiKit.Visual.pulsate */
1863 MochiKit.Visual.pulsate = function (element, /* optional */ options) {
1864 /***
1865
1866 Pulse an element between appear/fade.
1867
1868 ***/
1869 var d = MochiKit.DOM;
1870 var v = MochiKit.Visual;
1871 var b = MochiKit.Base;
1872 var oldOpacity = MochiKit.Style.getStyle(element, 'opacity');
1873 options = b.update({
1874 duration: 3.0,
1875 from: 0,
1876 afterFinishInternal: function (effect) {
1877 MochiKit.Style.setStyle(effect.element, {'opacity': oldOpacity});
1878 }
1879 }, options);
1880 var transition = options.transition || v.Transitions.sinoidal;
1881 var reverser = b.bind(function (pos) {
1882 return transition(1 - v.Transitions.pulse(pos, options.pulses));
1883 }, transition);
1884 b.bind(reverser, transition);
1885 return new v.Opacity(element, b.update({
1886 transition: reverser}, options));
1887 };
1888
1889 /** @id MochiKit.Visual.fold */
1890 MochiKit.Visual.fold = function (element, /* optional */ options) {
1891 /***
1892
1893 Fold an element, first vertically, then horizontally.
1894
1895 ***/
1896 var d = MochiKit.DOM;
1897 var v = MochiKit.Visual;
1898 var s = MochiKit.Style;
1899 element = d.getElement(element);
1900 var oldStyle = {
1901 top: element.style.top,
1902 left: element.style.left,
1903 width: element.style.width,
1904 height: element.style.height
1905 };
1906 var elemClip = d.makeClipping(element);
1907 options = MochiKit.Base.update({
1908 scaleContent: false,
1909 scaleX: false,
1910 afterFinishInternal: function (effect) {
1911 new v.Scale(element, 1, {
1912 scaleContent: false,
1913 scaleY: false,
1914 afterFinishInternal: function (effect) {
1915 s.hideElement(effect.element);
1916 d.undoClipping(effect.element, elemClip);
1917 s.setStyle(effect.element, oldStyle);
1918 }
1919 });
1920 }
1921 }, options);
1922 return new v.Scale(element, 5, options);
1923 };
1924
1925
1926 // Compatibility with MochiKit 1.0
1927 MochiKit.Visual.Color = MochiKit.Color.Color;
1928 MochiKit.Visual.getElementsComputedStyle = MochiKit.DOM.computedStyle;
1929
1930 /* end of Rico adaptation */
1931
1932 MochiKit.Visual.__new__ = function () {
1933 var m = MochiKit.Base;
1934
1935 m.nameFunctions(this);
1936
1937 this.EXPORT_TAGS = {
1938 ":common": this.EXPORT,
1939 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
1940 };
1941
1942 };
1943
1944 MochiKit.Visual.EXPORT = [
1945 "roundElement",
1946 "roundClass",
1947 "tagifyText",
1948 "multiple",
1949 "toggle",
1950 "Parallel",
1951 "Opacity",
1952 "Move",
1953 "Scale",
1954 "Highlight",
1955 "ScrollTo",
1956 "Morph",
1957 "fade",
1958 "appear",
1959 "puff",
1960 "blindUp",
1961 "blindDown",
1962 "switchOff",
1963 "dropOut",
1964 "shake",
1965 "slideDown",
1966 "slideUp",
1967 "squish",
1968 "grow",
1969 "shrink",
1970 "pulsate",
1971 "fold"
1972 ];
1973
1974 MochiKit.Visual.EXPORT_OK = [
1975 "Base",
1976 "PAIRS"
1977 ];
1978
1979 MochiKit.Visual.__new__();
1980
1981 MochiKit.Base._exportSymbols(this, MochiKit.Visual);
@@ -0,0 +1,10 b''
1 # encoding: utf-8
2 __docformat__ = "restructuredtext en"
3 #-------------------------------------------------------------------------------
4 # Copyright (C) 2005 Fernando Perez <fperez@colorado.edu>
5 # Brian E Granger <ellisonbg@gmail.com>
6 # Benjamin Ragan-Kelley <benjaminrk@gmail.com>
7 #
8 # Distributed under the terms of the BSD License. The full license is in
9 # the file COPYING, distributed as part of this software.
10 #-------------------------------------------------------------------------------
@@ -0,0 +1,18 b''
1 dojo.kwCompoundRequire({
2 "common": [
3 "MochiKit.Base",
4 "MochiKit.Iter",
5 "MochiKit.Logging",
6 "MochiKit.DateTime",
7 "MochiKit.Format",
8 "MochiKit.Async",
9 "MochiKit.DOM",
10 "MochiKit.Style",
11 "MochiKit.LoggingPane",
12 "MochiKit.Color",
13 "MochiKit.Signal",
14 "MochiKit.Position",
15 "MochiKit.Visual"
16 ]
17 });
18 dojo.provide("MochiKit.*");
@@ -0,0 +1,375 b''
1 // This is from the MochiKit ajax_tables example
2 /*
3
4 On page load, the SortableManager:
5
6 - Rips out all of the elements with the mochi-example class.
7 - Finds the elements with the mochi-template class and saves them for
8 later parsing with "MochiTAL".
9 - Finds the anchor tags with the mochi:dataformat attribute and gives them
10 onclick behvaiors to load new data, using their href as the data source.
11 This makes your XML or JSON look like a normal link to a search engine
12 (or javascript-disabled browser).
13 - Clones the thead element from the table because it will be replaced on each
14 sort.
15 - Sets up a default sort key of "object_name" and queues a load of the json
16 document.
17
18
19 On data load, the SortableManager:
20
21 - Parses the table data from the document (columns list, rows list-of-lists)
22 and turns them into a list of [{column:value, ...}] objects for easy sorting
23 and column order stability.
24 - Chooses the default (or previous) sort state and triggers a sort request
25
26
27 On sort request:
28
29 - Replaces the cloned thead element with a copy of it that has the sort
30 indicator (&uarr; or &darr;) for the most recently sorted column (matched up
31 to the first field in the th's mochi:sortcolumn attribute), and attaches
32 onclick, onmousedown, onmouseover, onmouseout behaviors to them. The second
33 field of mochi:sortcolumn attribute is used to perform a non-string sort.
34 - Performs the sort on the objects list. If the second field of
35 mochi:sortcolumn was not "str", then a custom function is used and the
36 results are stored away in a __sort__ key, which is then used to perform the
37 sort (read: shwartzian transform).
38 - Calls processMochiTAL on the page, which finds the mochi-template sections
39 and then looks for mochi:repeat and mochi:content attributes on them, using
40 the data object.
41
42 */
43
44 processMochiTAL = function (dom, data) {
45 /***
46
47 A TAL-esque template attribute language processor,
48 including content replacement and repeat
49
50 ***/
51
52 // nodeType == 1 is an element, we're leaving
53 // text nodes alone.
54 if (dom.nodeType != 1) {
55 return;
56 }
57 var attr;
58 // duplicate this element for each item in the
59 // given list, and then process the duplicated
60 // element again (sans mochi:repeat tag)
61 attr = getAttribute(dom, "mochi:repeat");
62 if (attr) {
63 dom.removeAttribute("mochi:repeat");
64 var parent = dom.parentNode;
65 attr = attr.split(" ");
66 var name = attr[0];
67 var lst = valueForKeyPath(data, attr[1]);
68 if (!lst) {
69 return;
70 }
71 for (var i = 0; i < lst.length; i++) {
72 data[name] = lst[i];
73 var newDOM = dom.cloneNode(true);
74 processMochiTAL(newDOM, data);
75 parent.insertBefore(newDOM, dom);
76 }
77 parent.removeChild(dom);
78 return;
79 }
80 // do content replacement if there's a mochi:content attribute
81 // on the element
82 attr = getAttribute(dom, "mochi:content");
83 if (attr) {
84 dom.removeAttribute("mochi:content");
85 replaceChildNodes(dom, valueForKeyPath(data, attr));
86 return;
87 }
88 // we make a shallow copy of the current list of child nodes
89 // because it *will* change if there's a mochi:repeat in there!
90 var nodes = list(dom.childNodes);
91 for (var i = 0; i < nodes.length; i++) {
92 processMochiTAL(nodes[i], data);
93 }
94 };
95
96 mouseOverFunc = function () {
97 addElementClass(this, "over");
98 };
99
100 mouseOutFunc = function () {
101 removeElementClass(this, "over");
102 };
103
104 ignoreEvent = function (ev) {
105 if (ev && ev.preventDefault) {
106 ev.preventDefault();
107 ev.stopPropagation();
108 } else if (typeof(event) != 'undefined') {
109 event.cancelBubble = false;
110 event.returnValue = false;
111 }
112 };
113
114 SortTransforms = {
115 "str": operator.identity,
116 "istr": function (s) { return s.toLowerCase(); },
117 /* "isoDate": isoDate*/
118 };
119
120 getAttribute = function (dom, key) {
121 try {
122 return dom.getAttribute(key);
123 } catch (e) {
124 return null;
125 }
126 };
127
128 loadFromDataAnchor = function (ev) {
129 ignoreEvent(ev);
130 var format = this.getAttribute("mochi:dataformat");
131 var href = this.href;
132 sortableManager.loadFromURL(format, href);
133 };
134
135 valueForKeyPath = function (data, keyPath) {
136 var chunks = keyPath.split(".");
137 while (chunks.length && data) {
138 data = data[chunks.shift()];
139 }
140 return data;
141 };
142
143
144 SortableManager = function () {
145 this.thead = null;
146 this.thead_proto = null;
147 this.tbody = null;
148 this.deferred = null;
149 this.columns = [];
150 this.rows = [];
151 this.templates = [];
152 this.sortState = {};
153 bindMethods(this);
154 };
155
156 SortableManager.prototype = {
157
158 "initialize": function (prefix, sortkey) {
159 // just rip all mochi-examples out of the DOM
160 var examples = getElementsByTagAndClassName(null, prefix+"-example");
161 while (examples.length) {
162 swapDOM(examples.pop(), null);
163 }
164 // make a template list
165 var templates = getElementsByTagAndClassName(null, prefix+"-template");
166 for (var i = 0; i < templates.length; i++) {
167 var template = templates[i];
168 var proto = template.cloneNode(true);
169 removeElementClass(proto, prefix+"-template");
170 this.templates.push({
171 "template": proto,
172 "node": template
173 });
174 }
175 // set up the data anchors to do loads
176 var anchors = getElementsByTagAndClassName("a", null);
177 for (var i = 0; i < anchors.length; i++) {
178 var node = anchors[i];
179 var format = getAttribute(node, "mochi:dataformat");
180 if (format) {
181 node.onclick = loadFromDataAnchor;
182 }
183 }
184
185 // to find sort columns
186 this.thead = getElementsByTagAndClassName("thead", prefix)[0];
187 this.thead_proto = this.thead.cloneNode(true);
188
189 this.sortkey = sortkey;
190 /* this.loadFromURL("json", "objects.json");*/
191 },
192
193 "loadFromURL": function (format, url) {
194 log('loadFromURL', format, url);
195 var d;
196 if (this.deferred) {
197 this.deferred.cancel();
198 }
199 if (format == "xml") {
200 var d = doXHR(url, {
201 mimeType: 'text/xml',
202 headers: {Accept: 'text/xml'}
203 });
204 d.addCallback(datatableFromXMLRequest);
205 } else if (format == "json") {
206 d = loadJSONDoc(url);
207 } else {
208 throw new TypeError("format " + repr(format) + " not supported");
209 }
210 // keep track of the current deferred, so that we can cancel it
211 this.deferred = d;
212 var self = this;
213 // on success or error, remove the current deferred because it has
214 // completed, and pass through the result or error
215 d.addBoth(function (res) {
216 self.deferred = null;
217 log('loadFromURL success');
218 return res;
219 });
220 // on success, tag the result with the format used so we can display
221 // it
222 d.addCallback(function (res) {
223 res.format = format;
224 return res;
225 });
226 // call this.initWithData(data) once it's ready
227 d.addCallback(this.initWithData);
228 // if anything goes wrong, except for a simple cancellation,
229 // then log the error and show the logger
230 d.addErrback(function (err) {
231 if (err instanceof CancelledError) {
232 return;
233 }
234 logError(err);
235 logger.debuggingBookmarklet();
236 });
237 return d;
238 },
239
240 "initWithData": function (data) {
241 /***
242
243 Initialize the SortableManager with a table object
244
245 ***/
246
247 // reformat to [{column:value, ...}, ...] style as the objects key
248 var objects = [];
249 var rows = data.rows;
250 var cols = data.columns;
251 for (var i = 0; i < rows.length; i++) {
252 var row = rows[i];
253 var object = {};
254 for (var j = 0; j < cols.length; j++) {
255 object[cols[j]] = row[j];
256 }
257 objects.push(object);
258 }
259 data.objects = objects;
260 this.data = data;
261 // perform a sort and display based upon the previous sort state,
262 // defaulting to an ascending sort if this is the first sort
263 var order = this.sortState[this.sortkey];
264 if (typeof(order) == 'undefined') {
265 order = true;
266 }
267 this.drawSortedRows(this.sortkey, order, false);
268
269 },
270
271 "onSortClick": function (name) {
272 /***
273
274 Return a sort function for click events
275
276 ***/
277 // save ourselves from doing a bind
278 var self = this;
279 // on click, flip the last sort order of that column and sort
280 return function () {
281 log('onSortClick', name);
282 var order = self.sortState[name];
283 if (typeof(order) == 'undefined') {
284 // if it's never been sorted by this column, sort ascending
285 order = true;
286 } else if (self.sortkey == name) {
287 // if this column was sorted most recently, flip the sort order
288 order = !((typeof(order) == 'undefined') ? false : order);
289 }
290 self.drawSortedRows(name, order, true);
291 };
292 },
293
294 "drawSortedRows": function (key, forward, clicked) {
295 /***
296
297 Draw the new sorted table body, and modify the column headers
298 if appropriate
299
300 ***/
301 log('drawSortedRows', key, forward);
302
303 // save it so we can flip next time
304 this.sortState[key] = forward;
305 this.sortkey = key;
306 var sortstyle;
307
308 // setup the sort columns
309 var thead = this.thead_proto.cloneNode(true);
310 var cols = thead.getElementsByTagName("th");
311 for (var i = 0; i < cols.length; i++) {
312 var col = cols[i];
313 var sortinfo = getAttribute(col, "mochi:sortcolumn").split(" ");
314 var sortkey = sortinfo[0];
315 col.onclick = this.onSortClick(sortkey);
316 col.onmousedown = ignoreEvent;
317 col.onmouseover = mouseOverFunc;
318 col.onmouseout = mouseOutFunc;
319 // if this is the sorted column
320 if (sortkey == key) {
321 sortstyle = sortinfo[1];
322 // \u2193 is down arrow, \u2191 is up arrow
323 // forward sorts mean the rows get bigger going down
324 var arrow = (forward ? "\u2193" : "\u2191");
325 // add the character to the column header
326 col.appendChild(SPAN(null, arrow));
327 if (clicked) {
328 col.onmouseover();
329 }
330 }
331 }
332 this.thead = swapDOM(this.thead, thead);
333
334 // apply a sort transform to a temporary column named __sort__,
335 // and do the sort based on that column
336 if (!sortstyle) {
337 sortstyle = "str";
338 }
339 var sortfunc = SortTransforms[sortstyle];
340 if (!sortfunc) {
341 throw new TypeError("unsupported sort style " + repr(sortstyle));
342 }
343 var objects = this.data.objects;
344 for (var i = 0; i < objects.length; i++) {
345 var object = objects[i];
346 object.__sort__ = sortfunc(object[key]);
347 }
348
349 // perform the sort based on the state given (forward or reverse)
350 var cmp = (forward ? keyComparator : reverseKeyComparator);
351 objects.sort(cmp("__sort__"));
352
353 // process every template with the given data
354 // and put the processed templates in the DOM
355 for (var i = 0; i < this.templates.length; i++) {
356 log('template', i, template);
357 var template = this.templates[i];
358 var dom = template.template.cloneNode(true);
359 processMochiTAL(dom, this.data);
360 template.node = swapDOM(template.node, dom);
361 }
362 //permission based coloring
363
364
365
366 }
367
368 };
369
370 // create the global SortableManager and initialize it on page load
371 sortableManager = new SortableManager();
372 sortableManager2 = new SortableManager();
373
374 addLoadEvent(function() {sortableManager.initialize("notebook", "dateModified")});
375 addLoadEvent(function() {sortableManager2.initialize("user", "username")});
@@ -0,0 +1,14 b''
1 ===================
2 External Packages
3 ===================
4
5 This directory contains external packages which we ship inside IPython for ease
6 of distributions for users.
7
8 Currently we include:
9
10 - configobj
11 - Itpl
12 - MochiKit
13 - simplegeneric
14 - validate
@@ -0,0 +1,16 b''
1 # encoding: utf-8
2
3 """
4 This package contains all third-party modules bundled with IPython.
5 """
6
7 __docformat__ = "restructuredtext en"
8
9 #-------------------------------------------------------------------------------
10 # Copyright (C) 2008 The IPython Development Team
11 #
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
14 #-------------------------------------------------------------------------------
15
16 __all__ = ['simplegeneric','twisted','Itpl', 'MochiKit']
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100755
NO CONTENT: new file 100755
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now