some bug fixes to CAst IR generation, especially in handling exceptions
cleanup of how JDT analysis engines work, to make using the JDT front end more modular bug foxes to JavaScript handling, most notably fixing scoping of functions git-svn-id: https://wala.svn.sourceforge.net/svnroot/wala/trunk@4123 f5eafffb-2e1d-0410-98e4-8ec43c5233c4
This commit is contained in:
parent
2f2c6a2555
commit
9006fce690
|
@ -17,10 +17,10 @@ import org.junit.AfterClass;
|
|||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.ibm.wala.cast.java.client.JDTJavaSourceAnalysisEngine;
|
||||
import com.ibm.wala.cast.java.client.JavaSourceAnalysisEngine;
|
||||
import com.ibm.wala.cast.java.ipa.callgraph.JavaSourceAnalysisScope;
|
||||
import com.ibm.wala.cast.java.test.ide.IDEIRTestUtil;
|
||||
import com.ibm.wala.cast.java.translator.jdt.JDTJavaSourceAnalysisEngine;
|
||||
import com.ibm.wala.core.tests.callGraph.CallGraphTestUtil;
|
||||
import com.ibm.wala.core.tests.plugin.CoreTestsPlugin;
|
||||
import com.ibm.wala.ide.tests.util.EclipseTestUtil;
|
||||
|
@ -36,8 +36,9 @@ public class JDTJava15IRTests extends IRTests {
|
|||
super(JDTJavaIRTests.PROJECT_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populateScope(JavaSourceAnalysisEngine engine, Collection<String> sources, List<String> libs) throws IOException {
|
||||
IDEIRTestUtil.populateScope(projectName, engine, sources, libs);
|
||||
IDEIRTestUtil.populateScope(projectName, (JDTJavaSourceAnalysisEngine)engine, sources, libs);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
|
@ -53,7 +54,8 @@ public class JDTJava15IRTests extends IRTests {
|
|||
|
||||
@Override
|
||||
protected JavaSourceAnalysisEngine getAnalysisEngine(final String[] mainClassDescriptors) {
|
||||
JavaSourceAnalysisEngine engine = new JDTJavaSourceAnalysisEngine() {
|
||||
JavaSourceAnalysisEngine engine = new JDTJavaSourceAnalysisEngine(JDTJavaIRTests.PROJECT_NAME) {
|
||||
@Override
|
||||
protected Iterable<Entrypoint> makeDefaultEntrypoints(AnalysisScope scope, IClassHierarchy cha) {
|
||||
return Util.makeMainEntrypoints(JavaSourceAnalysisScope.SOURCE, cha, mainClassDescriptors);
|
||||
}
|
||||
|
@ -69,11 +71,6 @@ public class JDTJava15IRTests extends IRTests {
|
|||
return engine;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void runSimple15Test(List<? extends IRAssertion> assertions) {
|
||||
runTest(singlePkgTestSrc("javaonepointfive"), rtJar, simplePkgTestEntryPoint("javaonepointfive"), assertions, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnonGeneNullarySimple() {
|
||||
runTest(singlePkgTestSrc("javaonepointfive"), rtJar, simplePkgTestEntryPoint("javaonepointfive"), emptyList, true);
|
||||
|
|
|
@ -47,10 +47,10 @@ import org.eclipse.core.runtime.NullProgressMonitor;
|
|||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
import com.ibm.wala.cast.java.client.JDTJavaSourceAnalysisEngine;
|
||||
import com.ibm.wala.cast.java.client.JavaSourceAnalysisEngine;
|
||||
import com.ibm.wala.cast.java.ipa.callgraph.JavaSourceAnalysisScope;
|
||||
import com.ibm.wala.cast.java.test.ide.IDEIRTestUtil;
|
||||
import com.ibm.wala.cast.java.translator.jdt.JDTJavaSourceAnalysisEngine;
|
||||
import com.ibm.wala.core.tests.callGraph.CallGraphTestUtil;
|
||||
import com.ibm.wala.core.tests.plugin.CoreTestsPlugin;
|
||||
import com.ibm.wala.ide.tests.util.EclipseTestUtil;
|
||||
|
@ -70,8 +70,9 @@ public class JDTJavaIRTests extends JavaIRTests {
|
|||
super(PROJECT_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populateScope(JavaSourceAnalysisEngine engine, Collection<String> sources, List<String> libs) throws IOException {
|
||||
IDEIRTestUtil.populateScope(projectName, engine, sources, libs);
|
||||
IDEIRTestUtil.populateScope(projectName, (JDTJavaSourceAnalysisEngine)engine, sources, libs);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
|
@ -87,7 +88,8 @@ public class JDTJavaIRTests extends JavaIRTests {
|
|||
|
||||
@Override
|
||||
protected JavaSourceAnalysisEngine getAnalysisEngine(final String[] mainClassDescriptors) {
|
||||
JavaSourceAnalysisEngine engine = new JDTJavaSourceAnalysisEngine() {
|
||||
JavaSourceAnalysisEngine engine = new JDTJavaSourceAnalysisEngine(PROJECT_NAME) {
|
||||
@Override
|
||||
protected Iterable<Entrypoint> makeDefaultEntrypoints(AnalysisScope scope, IClassHierarchy cha) {
|
||||
return Util.makeMainEntrypoints(JavaSourceAnalysisScope.SOURCE, cha, mainClassDescriptors);
|
||||
}
|
||||
|
|
|
@ -19,24 +19,14 @@ import java.util.Collection;
|
|||
import java.util.List;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IWorkspace;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
|
||||
import com.ibm.wala.cast.java.client.JavaSourceAnalysisEngine;
|
||||
import com.ibm.wala.cast.java.client.JDTJavaSourceAnalysisEngine;
|
||||
import com.ibm.wala.classLoader.JarFileModule;
|
||||
import com.ibm.wala.classLoader.SourceDirectoryTreeModule;
|
||||
import com.ibm.wala.classLoader.SourceFileModule;
|
||||
import com.ibm.wala.ide.classloader.EclipseSourceFileModule;
|
||||
import com.ibm.wala.ide.tests.util.EclipseTestUtil;
|
||||
|
||||
public class IDEIRTestUtil {
|
||||
|
||||
public static void populateScope(String projectName, JavaSourceAnalysisEngine engine, Collection<String> sources, List<String> libs) throws IOException {
|
||||
boolean foundLib = false;
|
||||
public static void populateScope(String projectName, JDTJavaSourceAnalysisEngine engine, Collection<String> sources, List<String> libs) throws IOException {
|
||||
|
||||
boolean foundLib = false;
|
||||
for (String lib : libs) {
|
||||
File libFile = new File(lib);
|
||||
if (libFile.exists()) {
|
||||
|
@ -46,37 +36,8 @@ public class IDEIRTestUtil {
|
|||
}
|
||||
assert foundLib : "couldn't find library file from " + libs;
|
||||
|
||||
IWorkspace w = null;
|
||||
IJavaProject project = null;
|
||||
try {
|
||||
if (projectName != null) {
|
||||
w = ResourcesPlugin.getWorkspace();
|
||||
project = EclipseTestUtil.getNamedProject(projectName);
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
// use Workspace only if it exists
|
||||
}
|
||||
|
||||
for (String srcFilePath : sources) {
|
||||
|
||||
if (w != null) {
|
||||
IFile file = project.getProject().getFile(srcFilePath);
|
||||
try {
|
||||
engine.addSourceModule(EclipseSourceFileModule.createEclipseSourceFileModule(file));
|
||||
} catch (IllegalArgumentException e) {
|
||||
Assert.assertTrue(e.getMessage(), false);
|
||||
}
|
||||
|
||||
} else {
|
||||
String srcFileName = srcFilePath.substring(srcFilePath.lastIndexOf(File.separator) + 1);
|
||||
File f = new File(srcFilePath);
|
||||
Assert.assertTrue("couldn't find " + srcFilePath, f.exists());
|
||||
if (f.isDirectory()) {
|
||||
engine.addSourceModule(new SourceDirectoryTreeModule(f));
|
||||
} else {
|
||||
engine.addSourceModule(new SourceFileModule(f, srcFileName));
|
||||
}
|
||||
}
|
||||
engine.addSourceModule(srcFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,16 +35,60 @@
|
|||
* IS". REGENTS HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
|
||||
* UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*/
|
||||
package com.ibm.wala.cast.java.translator.jdt;
|
||||
package com.ibm.wala.cast.java.client;
|
||||
|
||||
import com.ibm.wala.cast.java.client.JavaSourceAnalysisEngine;
|
||||
import org.eclipse.core.resources.IFolder;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
|
||||
import com.ibm.wala.cast.java.translator.jdt.JDTClassLoaderFactory;
|
||||
import com.ibm.wala.classLoader.ClassLoaderFactory;
|
||||
import com.ibm.wala.ide.classloader.EclipseSourceFileModule;
|
||||
import com.ibm.wala.ide.util.JdtUtil;
|
||||
import com.ibm.wala.ipa.callgraph.impl.SetOfClasses;
|
||||
|
||||
public class JDTJavaSourceAnalysisEngine extends JavaSourceAnalysisEngine {
|
||||
protected final IJavaProject project;
|
||||
|
||||
public JDTJavaSourceAnalysisEngine(IJavaProject project) {
|
||||
super();
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
public JDTJavaSourceAnalysisEngine(String projectName) {
|
||||
this(JdtUtil.getNamedProject(projectName));
|
||||
}
|
||||
|
||||
protected ClassLoaderFactory getClassLoaderFactory(SetOfClasses exclusions) {
|
||||
return new JDTClassLoaderFactory(exclusions);
|
||||
}
|
||||
|
||||
public void addSourceModule(IResource file) {
|
||||
IProject proj = project.getProject();
|
||||
IPath path = file.getProjectRelativePath();
|
||||
if (file.getType() == IResource.FILE) {
|
||||
addSourceModule(EclipseSourceFileModule.createEclipseSourceFileModule(proj.getFile(path)));
|
||||
} else {
|
||||
assert file.getType() == IResource.FOLDER;
|
||||
IFolder dir = proj.getFolder(path);
|
||||
try {
|
||||
for(IResource x : dir.members()) {
|
||||
assert x.getType() == IResource.FILE || x.getType() == IResource.FOLDER;
|
||||
addSourceModule(x);
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
throw new RuntimeException("trouble with " + file, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addSourceModule(String fileName) {
|
||||
IResource file = project.getProject().findMember(fileName);
|
||||
assert file != null;
|
||||
addSourceModule(file);
|
||||
}
|
||||
|
||||
}
|
|
@ -63,11 +63,16 @@ public class JDTClassLoaderFactory extends ClassLoaderFactoryImpl {
|
|||
protected IClassLoader makeNewClassLoader(ClassLoaderReference classLoaderReference, IClassHierarchy cha, IClassLoader parent,
|
||||
AnalysisScope scope) throws IOException {
|
||||
if (classLoaderReference.equals(JavaSourceAnalysisScope.SOURCE)) {
|
||||
ClassLoaderImpl cl = new JDTSourceLoaderImpl(classLoaderReference, parent, getExclusions(), cha);
|
||||
ClassLoaderImpl cl = makeSourceLoader(classLoaderReference, cha, parent);
|
||||
cl.init(scope.getModules(classLoaderReference));
|
||||
return cl;
|
||||
} else {
|
||||
return super.makeNewClassLoader(classLoaderReference, cha, parent, scope);
|
||||
}
|
||||
}
|
||||
|
||||
protected JDTSourceLoaderImpl makeSourceLoader(ClassLoaderReference classLoaderReference, IClassHierarchy cha, IClassLoader parent)
|
||||
throws IOException {
|
||||
return new JDTSourceLoaderImpl(classLoaderReference, parent, getExclusions(), cha);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,10 +38,12 @@
|
|||
package com.ibm.wala.cast.java.translator.jdt;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import com.ibm.wala.cast.java.loader.JavaSourceLoaderImpl;
|
||||
import com.ibm.wala.cast.java.translator.SourceModuleTranslator;
|
||||
import com.ibm.wala.classLoader.IClassLoader;
|
||||
import com.ibm.wala.classLoader.Module;
|
||||
import com.ibm.wala.ipa.callgraph.impl.SetOfClasses;
|
||||
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
||||
import com.ibm.wala.types.ClassLoaderReference;
|
||||
|
@ -56,4 +58,4 @@ public class JDTSourceLoaderImpl extends JavaSourceLoaderImpl {
|
|||
protected SourceModuleTranslator getTranslator() {
|
||||
return new JDTSourceModuleTranslator(cha.getScope(), this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ import com.ibm.wala.util.debug.Assertions;
|
|||
*/
|
||||
// remove me comment: Jdt little-case = not OK, upper case = OK
|
||||
public class JDTSourceModuleTranslator implements SourceModuleTranslator {
|
||||
private JDTSourceLoaderImpl sourceLoader;
|
||||
protected JDTSourceLoaderImpl sourceLoader;
|
||||
|
||||
public JDTSourceModuleTranslator(AnalysisScope scope, JDTSourceLoaderImpl sourceLoader) {
|
||||
computeClassPath(scope);
|
||||
|
@ -111,12 +111,11 @@ public class JDTSourceModuleTranslator implements SourceModuleTranslator {
|
|||
* Project -> AST code from org.eclipse.jdt.core.tests.performance
|
||||
*/
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void loadAllSources(Set modules) {
|
||||
// TODO: we might need one AST (-> "Object" class) for all files.
|
||||
// TODO: group by project and send 'em in
|
||||
JDTJava2CAstTranslator jdt2cast = new JDTJava2CAstTranslator(sourceLoader);
|
||||
final Java2IRTranslator java2ir = new Java2IRTranslator(jdt2cast, sourceLoader);
|
||||
JDTJava2CAstTranslator jdt2cast = makeCAstTranslator();
|
||||
final Java2IRTranslator java2ir = makeIRTranslator(jdt2cast);
|
||||
|
||||
System.out.println(modules);
|
||||
|
||||
|
@ -163,4 +162,12 @@ public class JDTSourceModuleTranslator implements SourceModuleTranslator {
|
|||
}
|
||||
}
|
||||
|
||||
protected Java2IRTranslator makeIRTranslator(JDTJava2CAstTranslator jdt2cast) {
|
||||
return new Java2IRTranslator(jdt2cast, sourceLoader);
|
||||
}
|
||||
|
||||
protected JDTJava2CAstTranslator makeCAstTranslator() {
|
||||
return new JDTJava2CAstTranslator(sourceLoader);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ import com.ibm.wala.classLoader.IClass;
|
|||
import com.ibm.wala.classLoader.IClassLoader;
|
||||
import com.ibm.wala.classLoader.IField;
|
||||
import com.ibm.wala.classLoader.IMethod;
|
||||
import com.ibm.wala.classLoader.Module;
|
||||
import com.ibm.wala.classLoader.JavaLanguage.JavaInstructionFactory;
|
||||
import com.ibm.wala.classLoader.Language;
|
||||
import com.ibm.wala.classLoader.ModuleEntry;
|
||||
|
@ -426,11 +427,15 @@ public abstract class JavaSourceLoaderImpl extends ClassLoaderImpl {
|
|||
|
||||
protected void loadAllSources(Set<ModuleEntry> modules) {
|
||||
getTranslator().loadAllSources(modules);
|
||||
fTypeMap = null;
|
||||
}
|
||||
|
||||
protected abstract SourceModuleTranslator getTranslator();
|
||||
|
||||
|
||||
public void init(List<Module> modules) throws IOException {
|
||||
super.init(modules);
|
||||
fTypeMap = null;
|
||||
}
|
||||
|
||||
public void defineFunction(CAstEntity n, IClass owner, AbstractCFG cfg, SymbolTable symtab, boolean hasCatchBlock,
|
||||
TypeReference[][] catchTypes, boolean hasMonitorOp, AstLexicalInformation lexicalInfo, DebuggingInformation debugInfo) {
|
||||
((JavaClass) owner).addMethod(n, owner, cfg, symtab, hasCatchBlock, catchTypes, hasMonitorOp, lexicalInfo, debugInfo);
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package com.ibm.wala.cast.js.test;
|
||||
|
||||
import org.junit.Before;
|
||||
|
||||
import com.ibm.wala.cast.js.translator.CAstRhinoTranslatorFactory;
|
||||
|
||||
public class TestMozillaBugPagesRhino extends TestMozillaBugPages {
|
||||
|
||||
public static void main(String[] args) {
|
||||
justThisTest(TestMozillaBugPagesRhino.class);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
com.ibm.wala.cast.js.ipa.callgraph.Util.setTranslatorFactory(new CAstRhinoTranslatorFactory());
|
||||
}
|
||||
|
||||
}
|
|
@ -10,9 +10,15 @@
|
|||
*****************************************************************************/
|
||||
package com.ibm.wala.cast.js.test;
|
||||
|
||||
import org.junit.Before;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.ibm.wala.cast.js.ipa.callgraph.JSCFABuilder;
|
||||
import com.ibm.wala.cast.js.translator.CAstRhinoTranslatorFactory;
|
||||
import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
|
||||
import com.ibm.wala.util.CancelException;
|
||||
|
||||
public class TestSimpleCallGraphShapeRhino extends TestSimpleCallGraphShape {
|
||||
|
||||
|
@ -20,9 +26,25 @@ public class TestSimpleCallGraphShapeRhino extends TestSimpleCallGraphShape {
|
|||
justThisTest(TestSimpleCallGraphShapeRhino.class);
|
||||
}
|
||||
|
||||
@Before
|
||||
@Before
|
||||
public void setUp() {
|
||||
com.ibm.wala.cast.js.ipa.callgraph.Util.setTranslatorFactory(new CAstRhinoTranslatorFactory());
|
||||
}
|
||||
|
||||
@Test public void test214631() throws IOException, IllegalArgumentException, CancelException {
|
||||
JSCFABuilder b = Util.makeScriptCGBuilder("tests", "214631.js");
|
||||
b.makeCallGraph(b.getOptions());
|
||||
PointerAnalysis PA = b.getPointerAnalysis();
|
||||
// just make sure this does not crash
|
||||
computeIkIdToVns(PA);
|
||||
}
|
||||
|
||||
@Test public void testRewriterDoesNotChangeLablesBug() throws IOException, IllegalArgumentException, CancelException {
|
||||
Util.makeScriptCG("tests", "rewrite_does_not_change_lables_bug.js");
|
||||
// all we need is for it to finish building CG successfully.
|
||||
}
|
||||
|
||||
@Test public void testRepr() throws IllegalArgumentException, IOException, CancelException {
|
||||
Util.makeScriptCG("tests", "repr.js");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,29 +16,39 @@ import java.net.URL;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.ibm.wala.cast.js.html.IHtmlParser;
|
||||
import com.ibm.wala.cast.js.html.IHtmlParserFactory;
|
||||
import com.ibm.wala.cast.js.html.WebUtil;
|
||||
import com.ibm.wala.cast.js.translator.CAstRhinoTranslatorFactory;
|
||||
import com.ibm.wala.ipa.callgraph.CallGraph;
|
||||
import com.ibm.wala.util.CancelException;
|
||||
|
||||
public class TestSimplePageCallGraphShapeRhino extends TestSimplePageCallGraphShape {
|
||||
public abstract class TestSimplePageCallGraphShapeRhino extends TestSimplePageCallGraphShape {
|
||||
|
||||
private static final Object[][] assertionsForPage3 = new Object[][] {
|
||||
new Object[] { ROOT, new String[] { "page3.html" } },
|
||||
new Object[] { "page3.html", new String[] { "page3.html/__WINDOW_MAIN__" } }
|
||||
};
|
||||
|
||||
@Test public void testPage3() throws IOException, IllegalArgumentException, CancelException {
|
||||
URL url = getClass().getClassLoader().getResource("pages/page3.html");
|
||||
CallGraph CG = Util.makeHTMLCG(url);
|
||||
verifyGraphAssertions(CG, assertionsForPage3);
|
||||
}
|
||||
private static final Object[][] assertionsForPage3 = new Object[][] {
|
||||
new Object[] { ROOT, new String[] { "page3.html" } },
|
||||
new Object[] { "page3.html", new String[] { "page3.html/__WINDOW_MAIN__" } }
|
||||
};
|
||||
|
||||
public static void main(String[] args) {
|
||||
justThisTest(TestSimplePageCallGraphShapeRhino.class);
|
||||
}
|
||||
@Test public void testPage3() throws IOException, IllegalArgumentException, CancelException {
|
||||
URL url = getClass().getClassLoader().getResource("pages/page3.html");
|
||||
CallGraph CG = Util.makeHTMLCG(url);
|
||||
verifyGraphAssertions(CG, assertionsForPage3);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
com.ibm.wala.cast.js.ipa.callgraph.Util.setTranslatorFactory(new CAstRhinoTranslatorFactory());
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
justThisTest(TestSimplePageCallGraphShapeRhino.class);
|
||||
}
|
||||
|
||||
protected abstract IHtmlParser getParser();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
com.ibm.wala.cast.js.ipa.callgraph.Util.setTranslatorFactory(new CAstRhinoTranslatorFactory());
|
||||
WebUtil.setFactory(new IHtmlParserFactory() {
|
||||
public IHtmlParser getParser() {
|
||||
return TestSimplePageCallGraphShapeRhino.this.getParser();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package com.ibm.wala.cast.js.test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.ibm.wala.cast.js.html.IHtmlParser;
|
||||
import com.ibm.wala.cast.js.html.jericho.JerichoHtmlParser;
|
||||
import com.ibm.wala.ipa.callgraph.CallGraph;
|
||||
import com.ibm.wala.util.CancelException;
|
||||
|
||||
|
||||
public class TestSimplePageCallGraphShapeRhinoJericho extends TestSimplePageCallGraphShapeRhino {
|
||||
|
||||
@Test public void testCrawl() throws IOException, IllegalArgumentException, CancelException {
|
||||
URL url = getClass().getClassLoader().getResource("pages/crawl.html");
|
||||
CallGraph CG = Util.makeHTMLCG(url);
|
||||
verifyGraphAssertions(CG, null);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
justThisTest(TestSimplePageCallGraphShapeRhinoJericho.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IHtmlParser getParser() {
|
||||
return new JerichoHtmlParser();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +1,6 @@
|
|||
package com.ibm.wala.cast.js.test;
|
||||
|
||||
import org.junit.Before;
|
||||
|
||||
import com.ibm.wala.cast.js.html.IHtmlParser;
|
||||
import com.ibm.wala.cast.js.html.IHtmlParserFactory;
|
||||
import com.ibm.wala.cast.js.html.WebUtil;
|
||||
import com.ibm.wala.cast.js.html.nu_validator.NuValidatorHtmlParser;
|
||||
|
||||
public class TestSimplePageCallGraphShapeRhinoNu extends TestSimplePageCallGraphShapeRhino {
|
||||
|
@ -13,13 +9,8 @@ public class TestSimplePageCallGraphShapeRhinoNu extends TestSimplePageCallGraph
|
|||
justThisTest(TestSimplePageCallGraphShapeRhinoNu.class);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
WebUtil.setFactory(new IHtmlParserFactory() {
|
||||
public IHtmlParser getParser() {
|
||||
return new NuValidatorHtmlParser();
|
||||
}
|
||||
});
|
||||
@Override
|
||||
protected IHtmlParser getParser() {
|
||||
return new NuValidatorHtmlParser();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Netscape Communications Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 1998
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = testing/mochitest
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
DIRS = MochiKit \
|
||||
static \
|
||||
tests \
|
||||
chrome \
|
||||
ssltunnel \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
# files that get copied into $objdir/_tests/
|
||||
_SERV_FILES = \
|
||||
runtests.pl \
|
||||
runtests.py \
|
||||
automation.py \
|
||||
gen_template.pl \
|
||||
server.js \
|
||||
harness-a11y.xul \
|
||||
harness-overlay.xul \
|
||||
harness.xul \
|
||||
browser-test-overlay.xul \
|
||||
browser-test.js \
|
||||
browser-harness.xul \
|
||||
redirect-a11y.html \
|
||||
redirect.html \
|
||||
redirect.js \
|
||||
$(topsrcdir)/netwerk/test/httpserver/httpd.js \
|
||||
$(NULL)
|
||||
|
||||
|
||||
_DEST_DIR = $(DEPTH)/_tests/$(relativesrcdir)
|
||||
|
||||
ifeq ($(USE_SHORT_LIBNAME), 1)
|
||||
PROGRAM = $(MOZ_APP_NAME)$(BIN_SUFFIX)
|
||||
else
|
||||
PROGRAM = $(MOZ_APP_NAME)-bin$(BIN_SUFFIX)
|
||||
endif
|
||||
|
||||
ifeq ($(MOZ_BUILD_APP),camino)
|
||||
browser_path = \"../$(DIST)/Camino.app/Contents/MacOS/Camino\"
|
||||
else
|
||||
ifeq ($(OS_ARCH),Darwin)
|
||||
ifdef MOZ_DEBUG
|
||||
browser_path = \"../$(DIST)/$(MOZ_APP_DISPLAYNAME)Debug.app/Contents/MacOS/$(PROGRAM)\"
|
||||
else
|
||||
browser_path = \"../$(DIST)/$(MOZ_APP_DISPLAYNAME).app/Contents/MacOS/$(PROGRAM)\"
|
||||
endif
|
||||
else
|
||||
browser_path = \"../$(DIST)/bin/$(PROGRAM)\"
|
||||
endif
|
||||
endif
|
||||
|
||||
# These go in _tests/ so they need to go up an extra path segement
|
||||
TEST_DRIVER_PPARGS = \
|
||||
-DBROWSER_PATH=$(browser_path) \
|
||||
-DXPC_BIN_PATH=\"../$(DIST)/bin\" \
|
||||
$(NULL)
|
||||
|
||||
ifeq ($(OS_ARCH),Darwin)
|
||||
TEST_DRIVER_PPARGS += -DIS_MAC=1
|
||||
else
|
||||
TEST_DRIVER_PPARGS += -DIS_MAC=0
|
||||
endif
|
||||
|
||||
ifeq ($(MOZ_BUILD_APP),camino)
|
||||
TEST_DRIVER_PPARGS += -DIS_CAMINO=1
|
||||
else
|
||||
TEST_DRIVER_PPARGS += -DIS_CAMINO=0
|
||||
endif
|
||||
|
||||
ifeq ($(host_os), cygwin)
|
||||
TEST_DRIVER_PPARGS += -DIS_CYGWIN=1
|
||||
endif
|
||||
|
||||
runtests.pl: runtests.pl.in
|
||||
$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
|
||||
$(TEST_DRIVER_PPARGS) $(DEFINES) $(ACDEFINES) $^ > $@
|
||||
|
||||
runtests.py: runtests.py.in
|
||||
$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
|
||||
$(TEST_DRIVER_PPARGS) $(DEFINES) $(ACDEFINES) $^ > $@
|
||||
|
||||
automation.py: $(topsrcdir)/build/pgo/automation.py.in
|
||||
$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
|
||||
$(TEST_DRIVER_PPARGS) $(DEFINES) $(ACDEFINES) $^ > $@
|
||||
|
||||
GARBAGE += runtests.pl runtests.py automation.py
|
||||
|
||||
libs:: $(_SERV_FILES)
|
||||
$(INSTALL) $^ $(_DEST_DIR)
|
|
@ -0,0 +1,681 @@
|
|||
/***
|
||||
|
||||
MochiKit.Async 1.4
|
||||
|
||||
See <http://mochikit.com/> for documentation, downloads, license, etc.
|
||||
|
||||
(c) 2005 Bob Ippolito. All rights Reserved.
|
||||
|
||||
***/
|
||||
|
||||
if (typeof(dojo) != 'undefined') {
|
||||
dojo.provide("MochiKit.Async");
|
||||
dojo.require("MochiKit.Base");
|
||||
}
|
||||
if (typeof(JSAN) != 'undefined') {
|
||||
JSAN.use("MochiKit.Base", []);
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof(MochiKit.Base) == 'undefined') {
|
||||
throw "";
|
||||
}
|
||||
} catch (e) {
|
||||
throw "MochiKit.Async depends on MochiKit.Base!";
|
||||
}
|
||||
|
||||
if (typeof(MochiKit.Async) == 'undefined') {
|
||||
MochiKit.Async = {};
|
||||
}
|
||||
|
||||
MochiKit.Async.NAME = "MochiKit.Async";
|
||||
MochiKit.Async.VERSION = "1.4";
|
||||
MochiKit.Async.__repr__ = function () {
|
||||
return "[" + this.NAME + " " + this.VERSION + "]";
|
||||
};
|
||||
MochiKit.Async.toString = function () {
|
||||
return this.__repr__();
|
||||
};
|
||||
|
||||
/** @id MochiKit.Async.Deferred */
|
||||
MochiKit.Async.Deferred = function (/* optional */ canceller) {
|
||||
this.chain = [];
|
||||
this.id = this._nextId();
|
||||
this.fired = -1;
|
||||
this.paused = 0;
|
||||
this.results = [null, null];
|
||||
this.canceller = canceller;
|
||||
this.silentlyCancelled = false;
|
||||
this.chained = false;
|
||||
};
|
||||
|
||||
MochiKit.Async.Deferred.prototype = {
|
||||
/** @id MochiKit.Async.Deferred.prototype.repr */
|
||||
repr: function () {
|
||||
var state;
|
||||
if (this.fired == -1) {
|
||||
state = 'unfired';
|
||||
} else if (this.fired === 0) {
|
||||
state = 'success';
|
||||
} else {
|
||||
state = 'error';
|
||||
}
|
||||
return 'Deferred(' + this.id + ', ' + state + ')';
|
||||
},
|
||||
|
||||
toString: MochiKit.Base.forwardCall("repr"),
|
||||
|
||||
_nextId: MochiKit.Base.counter(),
|
||||
|
||||
/** @id MochiKit.Async.Deferred.prototype.cancel */
|
||||
cancel: function () {
|
||||
var self = MochiKit.Async;
|
||||
if (this.fired == -1) {
|
||||
if (this.canceller) {
|
||||
this.canceller(this);
|
||||
} else {
|
||||
this.silentlyCancelled = true;
|
||||
}
|
||||
if (this.fired == -1) {
|
||||
this.errback(new self.CancelledError(this));
|
||||
}
|
||||
} else if ((this.fired === 0) && (this.results[0] instanceof self.Deferred)) {
|
||||
this.results[0].cancel();
|
||||
}
|
||||
},
|
||||
|
||||
_resback: function (res) {
|
||||
/***
|
||||
|
||||
The primitive that means either callback or errback
|
||||
|
||||
***/
|
||||
this.fired = ((res instanceof Error) ? 1 : 0);
|
||||
this.results[this.fired] = res;
|
||||
this._fire();
|
||||
},
|
||||
|
||||
_check: function () {
|
||||
if (this.fired != -1) {
|
||||
if (!this.silentlyCancelled) {
|
||||
throw new MochiKit.Async.AlreadyCalledError(this);
|
||||
}
|
||||
this.silentlyCancelled = false;
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Async.Deferred.prototype.callback */
|
||||
callback: function (res) {
|
||||
this._check();
|
||||
if (res instanceof MochiKit.Async.Deferred) {
|
||||
throw new Error("Deferred instances can only be chained if they are the result of a callback");
|
||||
}
|
||||
this._resback(res);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Async.Deferred.prototype.errback */
|
||||
errback: function (res) {
|
||||
this._check();
|
||||
var self = MochiKit.Async;
|
||||
if (res instanceof self.Deferred) {
|
||||
throw new Error("Deferred instances can only be chained if they are the result of a callback");
|
||||
}
|
||||
if (!(res instanceof Error)) {
|
||||
res = new self.GenericError(res);
|
||||
}
|
||||
this._resback(res);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Async.Deferred.prototype.addBoth */
|
||||
addBoth: function (fn) {
|
||||
if (arguments.length > 1) {
|
||||
fn = MochiKit.Base.partial.apply(null, arguments);
|
||||
}
|
||||
return this.addCallbacks(fn, fn);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Async.Deferred.prototype.addCallback */
|
||||
addCallback: function (fn) {
|
||||
if (arguments.length > 1) {
|
||||
fn = MochiKit.Base.partial.apply(null, arguments);
|
||||
}
|
||||
return this.addCallbacks(fn, null);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Async.Deferred.prototype.addErrback */
|
||||
addErrback: function (fn) {
|
||||
if (arguments.length > 1) {
|
||||
fn = MochiKit.Base.partial.apply(null, arguments);
|
||||
}
|
||||
return this.addCallbacks(null, fn);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Async.Deferred.prototype.addCallbacks */
|
||||
addCallbacks: function (cb, eb) {
|
||||
if (this.chained) {
|
||||
throw new Error("Chained Deferreds can not be re-used");
|
||||
}
|
||||
this.chain.push([cb, eb]);
|
||||
if (this.fired >= 0) {
|
||||
this._fire();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
_fire: function () {
|
||||
/***
|
||||
|
||||
Used internally to exhaust the callback sequence when a result
|
||||
is available.
|
||||
|
||||
***/
|
||||
var chain = this.chain;
|
||||
var fired = this.fired;
|
||||
var res = this.results[fired];
|
||||
var self = this;
|
||||
var cb = null;
|
||||
while (chain.length > 0 && this.paused === 0) {
|
||||
// Array
|
||||
var pair = chain.shift();
|
||||
var f = pair[fired];
|
||||
if (f === null) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
res = f(res);
|
||||
fired = ((res instanceof Error) ? 1 : 0);
|
||||
if (res instanceof MochiKit.Async.Deferred) {
|
||||
cb = function (res) {
|
||||
self._resback(res);
|
||||
self.paused--;
|
||||
if ((self.paused === 0) && (self.fired >= 0)) {
|
||||
self._fire();
|
||||
}
|
||||
};
|
||||
this.paused++;
|
||||
}
|
||||
} catch (err) {
|
||||
fired = 1;
|
||||
if (!(err instanceof Error)) {
|
||||
err = new MochiKit.Async.GenericError(err);
|
||||
}
|
||||
res = err;
|
||||
}
|
||||
}
|
||||
this.fired = fired;
|
||||
this.results[fired] = res;
|
||||
if (cb && this.paused) {
|
||||
// this is for "tail recursion" in case the dependent deferred
|
||||
// is already fired
|
||||
res.addBoth(cb);
|
||||
res.chained = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MochiKit.Base.update(MochiKit.Async, {
|
||||
/** @id MochiKit.Async.evalJSONRequest */
|
||||
evalJSONRequest: function (/* req */) {
|
||||
return eval('(' + arguments[0].responseText + ')');
|
||||
},
|
||||
|
||||
/** @id MochiKit.Async.succeed */
|
||||
succeed: function (/* optional */result) {
|
||||
var d = new MochiKit.Async.Deferred();
|
||||
d.callback.apply(d, arguments);
|
||||
return d;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Async.fail */
|
||||
fail: function (/* optional */result) {
|
||||
var d = new MochiKit.Async.Deferred();
|
||||
d.errback.apply(d, arguments);
|
||||
return d;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Async.getXMLHttpRequest */
|
||||
getXMLHttpRequest: function () {
|
||||
var self = arguments.callee;
|
||||
if (!self.XMLHttpRequest) {
|
||||
var tryThese = [
|
||||
function () { return new XMLHttpRequest(); },
|
||||
function () { return new ActiveXObject('Msxml2.XMLHTTP'); },
|
||||
function () { return new ActiveXObject('Microsoft.XMLHTTP'); },
|
||||
function () { return new ActiveXObject('Msxml2.XMLHTTP.4.0'); },
|
||||
function () {
|
||||
throw new MochiKit.Async.BrowserComplianceError("Browser does not support XMLHttpRequest");
|
||||
}
|
||||
];
|
||||
for (var i = 0; i < tryThese.length; i++) {
|
||||
var func = tryThese[i];
|
||||
try {
|
||||
self.XMLHttpRequest = func;
|
||||
return func();
|
||||
} catch (e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
return self.XMLHttpRequest();
|
||||
},
|
||||
|
||||
_xhr_onreadystatechange: function (d) {
|
||||
// MochiKit.Logging.logDebug('this.readyState', this.readyState);
|
||||
var m = MochiKit.Base;
|
||||
if (this.readyState == 4) {
|
||||
// IE SUCKS
|
||||
try {
|
||||
this.onreadystatechange = null;
|
||||
} catch (e) {
|
||||
try {
|
||||
this.onreadystatechange = m.noop;
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
var status = null;
|
||||
try {
|
||||
status = this.status;
|
||||
if (!status && m.isNotEmpty(this.responseText)) {
|
||||
// 0 or undefined seems to mean cached or local
|
||||
status = 304;
|
||||
}
|
||||
} catch (e) {
|
||||
// pass
|
||||
// MochiKit.Logging.logDebug('error getting status?', repr(items(e)));
|
||||
}
|
||||
// 200 is OK, 304 is NOT_MODIFIED
|
||||
if (status == 200 || status == 304) { // OK
|
||||
d.callback(this);
|
||||
} else {
|
||||
var err = new MochiKit.Async.XMLHttpRequestError(this, "Request failed");
|
||||
if (err.number) {
|
||||
// XXX: This seems to happen on page change
|
||||
d.errback(err);
|
||||
} else {
|
||||
// XXX: this seems to happen when the server is unreachable
|
||||
d.errback(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_xhr_canceller: function (req) {
|
||||
// IE SUCKS
|
||||
try {
|
||||
req.onreadystatechange = null;
|
||||
} catch (e) {
|
||||
try {
|
||||
req.onreadystatechange = MochiKit.Base.noop;
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
req.abort();
|
||||
},
|
||||
|
||||
|
||||
/** @id MochiKit.Async.sendXMLHttpRequest */
|
||||
sendXMLHttpRequest: function (req, /* optional */ sendContent) {
|
||||
if (typeof(sendContent) == "undefined" || sendContent === null) {
|
||||
sendContent = "";
|
||||
}
|
||||
|
||||
var m = MochiKit.Base;
|
||||
var self = MochiKit.Async;
|
||||
var d = new self.Deferred(m.partial(self._xhr_canceller, req));
|
||||
|
||||
try {
|
||||
req.onreadystatechange = m.bind(self._xhr_onreadystatechange,
|
||||
req, d);
|
||||
req.send(sendContent);
|
||||
} catch (e) {
|
||||
try {
|
||||
req.onreadystatechange = null;
|
||||
} catch (ignore) {
|
||||
// pass
|
||||
}
|
||||
d.errback(e);
|
||||
}
|
||||
|
||||
return d;
|
||||
|
||||
},
|
||||
|
||||
/** @id MochiKit.Async.doXHR */
|
||||
doXHR: function (url, opts) {
|
||||
var m = MochiKit.Base;
|
||||
opts = m.update({
|
||||
method: 'GET',
|
||||
sendContent: ''
|
||||
/*
|
||||
queryString: undefined,
|
||||
username: undefined,
|
||||
password: undefined,
|
||||
headers: undefined,
|
||||
mimeType: undefined
|
||||
*/
|
||||
}, opts);
|
||||
var self = MochiKit.Async;
|
||||
var req = self.getXMLHttpRequest();
|
||||
if (opts.queryString) {
|
||||
var qs = m.queryString(opts.queryString);
|
||||
if (qs) {
|
||||
url += "?" + qs;
|
||||
}
|
||||
}
|
||||
req.open(opts.method, url, true, opts.username, opts.password);
|
||||
if (req.overrideMimeType && opts.mimeType) {
|
||||
req.overrideMimeType(opts.mimeType);
|
||||
}
|
||||
if (opts.headers) {
|
||||
var headers = opts.headers;
|
||||
if (!m.isArrayLike(headers)) {
|
||||
headers = m.items(headers);
|
||||
}
|
||||
for (var i = 0; i < headers.length; i++) {
|
||||
var header = headers[i];
|
||||
var name = header[0];
|
||||
var value = header[1];
|
||||
req.setRequestHeader(name, value);
|
||||
}
|
||||
}
|
||||
return self.sendXMLHttpRequest(req, opts.sendContent);
|
||||
},
|
||||
|
||||
_buildURL: function (url/*, ...*/) {
|
||||
if (arguments.length > 1) {
|
||||
var m = MochiKit.Base;
|
||||
var qs = m.queryString.apply(null, m.extend(null, arguments, 1));
|
||||
if (qs) {
|
||||
return url + "?" + qs;
|
||||
}
|
||||
}
|
||||
return url;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Async.doSimpleXMLHttpRequest */
|
||||
doSimpleXMLHttpRequest: function (url/*, ...*/) {
|
||||
var self = MochiKit.Async;
|
||||
url = self._buildURL.apply(self, arguments);
|
||||
return self.doXHR(url);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Async.loadJSONDoc */
|
||||
loadJSONDoc: function (url/*, ...*/) {
|
||||
var self = MochiKit.Async;
|
||||
url = self._buildURL.apply(self, arguments);
|
||||
var d = self.doXHR(url, {
|
||||
'mimeType': 'text/plain',
|
||||
'headers': [['Accept', 'application/json']]
|
||||
});
|
||||
d = d.addCallback(self.evalJSONRequest);
|
||||
return d;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Async.wait */
|
||||
wait: function (seconds, /* optional */value) {
|
||||
var d = new MochiKit.Async.Deferred();
|
||||
var m = MochiKit.Base;
|
||||
if (typeof(value) != 'undefined') {
|
||||
d.addCallback(function () { return value; });
|
||||
}
|
||||
var timeout = setTimeout(
|
||||
m.bind("callback", d),
|
||||
Math.floor(seconds * 1000));
|
||||
d.canceller = function () {
|
||||
try {
|
||||
clearTimeout(timeout);
|
||||
} catch (e) {
|
||||
// pass
|
||||
}
|
||||
};
|
||||
return d;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Async.callLater */
|
||||
callLater: function (seconds, func) {
|
||||
var m = MochiKit.Base;
|
||||
var pfunc = m.partial.apply(m, m.extend(null, arguments, 1));
|
||||
return MochiKit.Async.wait(seconds).addCallback(
|
||||
function (res) { return pfunc(); }
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/** @id MochiKit.Async.DeferredLock */
|
||||
MochiKit.Async.DeferredLock = function () {
|
||||
this.waiting = [];
|
||||
this.locked = false;
|
||||
this.id = this._nextId();
|
||||
};
|
||||
|
||||
MochiKit.Async.DeferredLock.prototype = {
|
||||
__class__: MochiKit.Async.DeferredLock,
|
||||
/** @id MochiKit.Async.DeferredLock.prototype.acquire */
|
||||
acquire: function () {
|
||||
var d = new MochiKit.Async.Deferred();
|
||||
if (this.locked) {
|
||||
this.waiting.push(d);
|
||||
} else {
|
||||
this.locked = true;
|
||||
d.callback(this);
|
||||
}
|
||||
return d;
|
||||
},
|
||||
/** @id MochiKit.Async.DeferredLock.prototype.release */
|
||||
release: function () {
|
||||
if (!this.locked) {
|
||||
throw TypeError("Tried to release an unlocked DeferredLock");
|
||||
}
|
||||
this.locked = false;
|
||||
if (this.waiting.length > 0) {
|
||||
this.locked = true;
|
||||
this.waiting.shift().callback(this);
|
||||
}
|
||||
},
|
||||
_nextId: MochiKit.Base.counter(),
|
||||
repr: function () {
|
||||
var state;
|
||||
if (this.locked) {
|
||||
state = 'locked, ' + this.waiting.length + ' waiting';
|
||||
} else {
|
||||
state = 'unlocked';
|
||||
}
|
||||
return 'DeferredLock(' + this.id + ', ' + state + ')';
|
||||
},
|
||||
toString: MochiKit.Base.forwardCall("repr")
|
||||
|
||||
};
|
||||
|
||||
/** @id MochiKit.Async.DeferredList */
|
||||
MochiKit.Async.DeferredList = function (list, /* optional */fireOnOneCallback, fireOnOneErrback, consumeErrors, canceller) {
|
||||
|
||||
// call parent constructor
|
||||
MochiKit.Async.Deferred.apply(this, [canceller]);
|
||||
|
||||
this.list = list;
|
||||
var resultList = [];
|
||||
this.resultList = resultList;
|
||||
|
||||
this.finishedCount = 0;
|
||||
this.fireOnOneCallback = fireOnOneCallback;
|
||||
this.fireOnOneErrback = fireOnOneErrback;
|
||||
this.consumeErrors = consumeErrors;
|
||||
|
||||
var cb = MochiKit.Base.bind(this._cbDeferred, this);
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var d = list[i];
|
||||
resultList.push(undefined);
|
||||
d.addCallback(cb, i, true);
|
||||
d.addErrback(cb, i, false);
|
||||
}
|
||||
|
||||
if (list.length === 0 && !fireOnOneCallback) {
|
||||
this.callback(this.resultList);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
MochiKit.Async.DeferredList.prototype = new MochiKit.Async.Deferred();
|
||||
|
||||
MochiKit.Async.DeferredList.prototype._cbDeferred = function (index, succeeded, result) {
|
||||
this.resultList[index] = [succeeded, result];
|
||||
this.finishedCount += 1;
|
||||
if (this.fired == -1) {
|
||||
if (succeeded && this.fireOnOneCallback) {
|
||||
this.callback([index, result]);
|
||||
} else if (!succeeded && this.fireOnOneErrback) {
|
||||
this.errback(result);
|
||||
} else if (this.finishedCount == this.list.length) {
|
||||
this.callback(this.resultList);
|
||||
}
|
||||
}
|
||||
if (!succeeded && this.consumeErrors) {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/** @id MochiKit.Async.gatherResults */
|
||||
MochiKit.Async.gatherResults = function (deferredList) {
|
||||
var d = new MochiKit.Async.DeferredList(deferredList, false, true, false);
|
||||
d.addCallback(function (results) {
|
||||
var ret = [];
|
||||
for (var i = 0; i < results.length; i++) {
|
||||
ret.push(results[i][1]);
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
return d;
|
||||
};
|
||||
|
||||
/** @id MochiKit.Async.maybeDeferred */
|
||||
MochiKit.Async.maybeDeferred = function (func) {
|
||||
var self = MochiKit.Async;
|
||||
var result;
|
||||
try {
|
||||
var r = func.apply(null, MochiKit.Base.extend([], arguments, 1));
|
||||
if (r instanceof self.Deferred) {
|
||||
result = r;
|
||||
} else if (r instanceof Error) {
|
||||
result = self.fail(r);
|
||||
} else {
|
||||
result = self.succeed(r);
|
||||
}
|
||||
} catch (e) {
|
||||
result = self.fail(e);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
MochiKit.Async.EXPORT = [
|
||||
"AlreadyCalledError",
|
||||
"CancelledError",
|
||||
"BrowserComplianceError",
|
||||
"GenericError",
|
||||
"XMLHttpRequestError",
|
||||
"Deferred",
|
||||
"succeed",
|
||||
"fail",
|
||||
"getXMLHttpRequest",
|
||||
"doSimpleXMLHttpRequest",
|
||||
"loadJSONDoc",
|
||||
"wait",
|
||||
"callLater",
|
||||
"sendXMLHttpRequest",
|
||||
"DeferredLock",
|
||||
"DeferredList",
|
||||
"gatherResults",
|
||||
"maybeDeferred",
|
||||
"doXHR"
|
||||
];
|
||||
|
||||
MochiKit.Async.EXPORT_OK = [
|
||||
"evalJSONRequest"
|
||||
];
|
||||
|
||||
MochiKit.Async.__new__ = function () {
|
||||
var m = MochiKit.Base;
|
||||
var ne = m.partial(m._newNamedError, this);
|
||||
|
||||
ne("AlreadyCalledError",
|
||||
/** @id MochiKit.Async.AlreadyCalledError */
|
||||
function (deferred) {
|
||||
/***
|
||||
|
||||
Raised by the Deferred if callback or errback happens
|
||||
after it was already fired.
|
||||
|
||||
***/
|
||||
this.deferred = deferred;
|
||||
}
|
||||
);
|
||||
|
||||
ne("CancelledError",
|
||||
/** @id MochiKit.Async.CancelledError */
|
||||
function (deferred) {
|
||||
/***
|
||||
|
||||
Raised by the Deferred cancellation mechanism.
|
||||
|
||||
***/
|
||||
this.deferred = deferred;
|
||||
}
|
||||
);
|
||||
|
||||
ne("BrowserComplianceError",
|
||||
/** @id MochiKit.Async.BrowserComplianceError */
|
||||
function (msg) {
|
||||
/***
|
||||
|
||||
Raised when the JavaScript runtime is not capable of performing
|
||||
the given function. Technically, this should really never be
|
||||
raised because a non-conforming JavaScript runtime probably
|
||||
isn't going to support exceptions in the first place.
|
||||
|
||||
***/
|
||||
this.message = msg;
|
||||
}
|
||||
);
|
||||
|
||||
ne("GenericError",
|
||||
/** @id MochiKit.Async.GenericError */
|
||||
function (msg) {
|
||||
this.message = msg;
|
||||
}
|
||||
);
|
||||
|
||||
ne("XMLHttpRequestError",
|
||||
/** @id MochiKit.Async.XMLHttpRequestError */
|
||||
function (req, msg) {
|
||||
/***
|
||||
|
||||
Raised when an XMLHttpRequest does not complete for any reason.
|
||||
|
||||
***/
|
||||
this.req = req;
|
||||
this.message = msg;
|
||||
try {
|
||||
// Strange but true that this can raise in some cases.
|
||||
this.number = req.status;
|
||||
} catch (e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
this.EXPORT_TAGS = {
|
||||
":common": this.EXPORT,
|
||||
":all": m.concat(this.EXPORT, this.EXPORT_OK)
|
||||
};
|
||||
|
||||
m.nameFunctions(this);
|
||||
|
||||
};
|
||||
|
||||
MochiKit.Async.__new__();
|
||||
|
||||
MochiKit.Base._exportSymbols(this, MochiKit.Async);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,902 @@
|
|||
/***
|
||||
|
||||
MochiKit.Color 1.4
|
||||
|
||||
See <http://mochikit.com/> for documentation, downloads, license, etc.
|
||||
|
||||
(c) 2005 Bob Ippolito and others. All rights Reserved.
|
||||
|
||||
***/
|
||||
|
||||
if (typeof(dojo) != 'undefined') {
|
||||
dojo.provide('MochiKit.Color');
|
||||
dojo.require('MochiKit.Base');
|
||||
dojo.require('MochiKit.DOM');
|
||||
dojo.require('MochiKit.Style');
|
||||
}
|
||||
|
||||
if (typeof(JSAN) != 'undefined') {
|
||||
JSAN.use("MochiKit.Base", []);
|
||||
JSAN.use("MochiKit.DOM", []);
|
||||
JSAN.use("MochiKit.Style", []);
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof(MochiKit.Base) == 'undefined') {
|
||||
throw "";
|
||||
}
|
||||
} catch (e) {
|
||||
throw "MochiKit.Color depends on MochiKit.Base";
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof(MochiKit.Base) == 'undefined') {
|
||||
throw "";
|
||||
}
|
||||
} catch (e) {
|
||||
throw "MochiKit.Color depends on MochiKit.DOM";
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof(MochiKit.Base) == 'undefined') {
|
||||
throw "";
|
||||
}
|
||||
} catch (e) {
|
||||
throw "MochiKit.Color depends on MochiKit.Style";
|
||||
}
|
||||
|
||||
if (typeof(MochiKit.Color) == "undefined") {
|
||||
MochiKit.Color = {};
|
||||
}
|
||||
|
||||
MochiKit.Color.NAME = "MochiKit.Color";
|
||||
MochiKit.Color.VERSION = "1.4";
|
||||
|
||||
MochiKit.Color.__repr__ = function () {
|
||||
return "[" + this.NAME + " " + this.VERSION + "]";
|
||||
};
|
||||
|
||||
MochiKit.Color.toString = function () {
|
||||
return this.__repr__();
|
||||
};
|
||||
|
||||
|
||||
/** @id MochiKit.Color.Color */
|
||||
MochiKit.Color.Color = function (red, green, blue, alpha) {
|
||||
if (typeof(alpha) == 'undefined' || alpha === null) {
|
||||
alpha = 1.0;
|
||||
}
|
||||
this.rgb = {
|
||||
r: red,
|
||||
g: green,
|
||||
b: blue,
|
||||
a: alpha
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// Prototype methods
|
||||
|
||||
MochiKit.Color.Color.prototype = {
|
||||
|
||||
__class__: MochiKit.Color.Color,
|
||||
|
||||
/** @id MochiKit.Color.Color.prototype.colorWithAlpha */
|
||||
colorWithAlpha: function (alpha) {
|
||||
var rgb = this.rgb;
|
||||
var m = MochiKit.Color;
|
||||
return m.Color.fromRGB(rgb.r, rgb.g, rgb.b, alpha);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.prototype.colorWithHue */
|
||||
colorWithHue: function (hue) {
|
||||
// get an HSL model, and set the new hue...
|
||||
var hsl = this.asHSL();
|
||||
hsl.h = hue;
|
||||
var m = MochiKit.Color;
|
||||
// convert back to RGB...
|
||||
return m.Color.fromHSL(hsl);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.prototype.colorWithSaturation */
|
||||
colorWithSaturation: function (saturation) {
|
||||
// get an HSL model, and set the new hue...
|
||||
var hsl = this.asHSL();
|
||||
hsl.s = saturation;
|
||||
var m = MochiKit.Color;
|
||||
// convert back to RGB...
|
||||
return m.Color.fromHSL(hsl);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.prototype.colorWithLightness */
|
||||
colorWithLightness: function (lightness) {
|
||||
// get an HSL model, and set the new hue...
|
||||
var hsl = this.asHSL();
|
||||
hsl.l = lightness;
|
||||
var m = MochiKit.Color;
|
||||
// convert back to RGB...
|
||||
return m.Color.fromHSL(hsl);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.prototype.darkerColorWithLevel */
|
||||
darkerColorWithLevel: function (level) {
|
||||
var hsl = this.asHSL();
|
||||
hsl.l = Math.max(hsl.l - level, 0);
|
||||
var m = MochiKit.Color;
|
||||
return m.Color.fromHSL(hsl);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.prototype.lighterColorWithLevel */
|
||||
lighterColorWithLevel: function (level) {
|
||||
var hsl = this.asHSL();
|
||||
hsl.l = Math.min(hsl.l + level, 1);
|
||||
var m = MochiKit.Color;
|
||||
return m.Color.fromHSL(hsl);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.prototype.blendedColor */
|
||||
blendedColor: function (other, /* optional */ fraction) {
|
||||
if (typeof(fraction) == 'undefined' || fraction === null) {
|
||||
fraction = 0.5;
|
||||
}
|
||||
var sf = 1.0 - fraction;
|
||||
var s = this.rgb;
|
||||
var d = other.rgb;
|
||||
var df = fraction;
|
||||
return MochiKit.Color.Color.fromRGB(
|
||||
(s.r * sf) + (d.r * df),
|
||||
(s.g * sf) + (d.g * df),
|
||||
(s.b * sf) + (d.b * df),
|
||||
(s.a * sf) + (d.a * df)
|
||||
);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.prototype.compareRGB */
|
||||
compareRGB: function (other) {
|
||||
var a = this.asRGB();
|
||||
var b = other.asRGB();
|
||||
return MochiKit.Base.compare(
|
||||
[a.r, a.g, a.b, a.a],
|
||||
[b.r, b.g, b.b, b.a]
|
||||
);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.prototype.isLight */
|
||||
isLight: function () {
|
||||
return this.asHSL().b > 0.5;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.prototype.isDark */
|
||||
isDark: function () {
|
||||
return (!this.isLight());
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.prototype.toHSLString */
|
||||
toHSLString: function () {
|
||||
var c = this.asHSL();
|
||||
var ccc = MochiKit.Color.clampColorComponent;
|
||||
var rval = this._hslString;
|
||||
if (!rval) {
|
||||
var mid = (
|
||||
ccc(c.h, 360).toFixed(0)
|
||||
+ "," + ccc(c.s, 100).toPrecision(4) + "%"
|
||||
+ "," + ccc(c.l, 100).toPrecision(4) + "%"
|
||||
);
|
||||
var a = c.a;
|
||||
if (a >= 1) {
|
||||
a = 1;
|
||||
rval = "hsl(" + mid + ")";
|
||||
} else {
|
||||
if (a <= 0) {
|
||||
a = 0;
|
||||
}
|
||||
rval = "hsla(" + mid + "," + a + ")";
|
||||
}
|
||||
this._hslString = rval;
|
||||
}
|
||||
return rval;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.prototype.toRGBString */
|
||||
toRGBString: function () {
|
||||
var c = this.rgb;
|
||||
var ccc = MochiKit.Color.clampColorComponent;
|
||||
var rval = this._rgbString;
|
||||
if (!rval) {
|
||||
var mid = (
|
||||
ccc(c.r, 255).toFixed(0)
|
||||
+ "," + ccc(c.g, 255).toFixed(0)
|
||||
+ "," + ccc(c.b, 255).toFixed(0)
|
||||
);
|
||||
if (c.a != 1) {
|
||||
rval = "rgba(" + mid + "," + c.a + ")";
|
||||
} else {
|
||||
rval = "rgb(" + mid + ")";
|
||||
}
|
||||
this._rgbString = rval;
|
||||
}
|
||||
return rval;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.prototype.asRGB */
|
||||
asRGB: function () {
|
||||
return MochiKit.Base.clone(this.rgb);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.prototype.toHexString */
|
||||
toHexString: function () {
|
||||
var m = MochiKit.Color;
|
||||
var c = this.rgb;
|
||||
var ccc = MochiKit.Color.clampColorComponent;
|
||||
var rval = this._hexString;
|
||||
if (!rval) {
|
||||
rval = ("#" +
|
||||
m.toColorPart(ccc(c.r, 255)) +
|
||||
m.toColorPart(ccc(c.g, 255)) +
|
||||
m.toColorPart(ccc(c.b, 255))
|
||||
);
|
||||
this._hexString = rval;
|
||||
}
|
||||
return rval;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.prototype.asHSV */
|
||||
asHSV: function () {
|
||||
var hsv = this.hsv;
|
||||
var c = this.rgb;
|
||||
if (typeof(hsv) == 'undefined' || hsv === null) {
|
||||
hsv = MochiKit.Color.rgbToHSV(this.rgb);
|
||||
this.hsv = hsv;
|
||||
}
|
||||
return MochiKit.Base.clone(hsv);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.prototype.asHSL */
|
||||
asHSL: function () {
|
||||
var hsl = this.hsl;
|
||||
var c = this.rgb;
|
||||
if (typeof(hsl) == 'undefined' || hsl === null) {
|
||||
hsl = MochiKit.Color.rgbToHSL(this.rgb);
|
||||
this.hsl = hsl;
|
||||
}
|
||||
return MochiKit.Base.clone(hsl);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.prototype.toString */
|
||||
toString: function () {
|
||||
return this.toRGBString();
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.prototype.repr */
|
||||
repr: function () {
|
||||
var c = this.rgb;
|
||||
var col = [c.r, c.g, c.b, c.a];
|
||||
return this.__class__.NAME + "(" + col.join(", ") + ")";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Constructor methods
|
||||
|
||||
MochiKit.Base.update(MochiKit.Color.Color, {
|
||||
/** @id MochiKit.Color.Color.fromRGB */
|
||||
fromRGB: function (red, green, blue, alpha) {
|
||||
// designated initializer
|
||||
var Color = MochiKit.Color.Color;
|
||||
if (arguments.length == 1) {
|
||||
var rgb = red;
|
||||
red = rgb.r;
|
||||
green = rgb.g;
|
||||
blue = rgb.b;
|
||||
if (typeof(rgb.a) == 'undefined') {
|
||||
alpha = undefined;
|
||||
} else {
|
||||
alpha = rgb.a;
|
||||
}
|
||||
}
|
||||
return new Color(red, green, blue, alpha);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.fromHSL */
|
||||
fromHSL: function (hue, saturation, lightness, alpha) {
|
||||
var m = MochiKit.Color;
|
||||
return m.Color.fromRGB(m.hslToRGB.apply(m, arguments));
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.fromHSV */
|
||||
fromHSV: function (hue, saturation, value, alpha) {
|
||||
var m = MochiKit.Color;
|
||||
return m.Color.fromRGB(m.hsvToRGB.apply(m, arguments));
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.fromName */
|
||||
fromName: function (name) {
|
||||
var Color = MochiKit.Color.Color;
|
||||
// Opera 9 seems to "quote" named colors(?!)
|
||||
if (name.charAt(0) == '"') {
|
||||
name = name.substr(1, name.length - 2);
|
||||
}
|
||||
var htmlColor = Color._namedColors[name.toLowerCase()];
|
||||
if (typeof(htmlColor) == 'string') {
|
||||
return Color.fromHexString(htmlColor);
|
||||
} else if (name == "transparent") {
|
||||
return Color.transparentColor();
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.fromString */
|
||||
fromString: function (colorString) {
|
||||
var self = MochiKit.Color.Color;
|
||||
var three = colorString.substr(0, 3);
|
||||
if (three == "rgb") {
|
||||
return self.fromRGBString(colorString);
|
||||
} else if (three == "hsl") {
|
||||
return self.fromHSLString(colorString);
|
||||
} else if (colorString.charAt(0) == "#") {
|
||||
return self.fromHexString(colorString);
|
||||
}
|
||||
return self.fromName(colorString);
|
||||
},
|
||||
|
||||
|
||||
/** @id MochiKit.Color.Color.fromHexString */
|
||||
fromHexString: function (hexCode) {
|
||||
if (hexCode.charAt(0) == '#') {
|
||||
hexCode = hexCode.substring(1);
|
||||
}
|
||||
var components = [];
|
||||
var i, hex;
|
||||
if (hexCode.length == 3) {
|
||||
for (i = 0; i < 3; i++) {
|
||||
hex = hexCode.substr(i, 1);
|
||||
components.push(parseInt(hex + hex, 16) / 255.0);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < 6; i += 2) {
|
||||
hex = hexCode.substr(i, 2);
|
||||
components.push(parseInt(hex, 16) / 255.0);
|
||||
}
|
||||
}
|
||||
var Color = MochiKit.Color.Color;
|
||||
return Color.fromRGB.apply(Color, components);
|
||||
},
|
||||
|
||||
|
||||
_fromColorString: function (pre, method, scales, colorCode) {
|
||||
// parses either HSL or RGB
|
||||
if (colorCode.indexOf(pre) === 0) {
|
||||
colorCode = colorCode.substring(colorCode.indexOf("(", 3) + 1, colorCode.length - 1);
|
||||
}
|
||||
var colorChunks = colorCode.split(/\s*,\s*/);
|
||||
var colorFloats = [];
|
||||
for (var i = 0; i < colorChunks.length; i++) {
|
||||
var c = colorChunks[i];
|
||||
var val;
|
||||
var three = c.substring(c.length - 3);
|
||||
if (c.charAt(c.length - 1) == '%') {
|
||||
val = 0.01 * parseFloat(c.substring(0, c.length - 1));
|
||||
} else if (three == "deg") {
|
||||
val = parseFloat(c) / 360.0;
|
||||
} else if (three == "rad") {
|
||||
val = parseFloat(c) / (Math.PI * 2);
|
||||
} else {
|
||||
val = scales[i] * parseFloat(c);
|
||||
}
|
||||
colorFloats.push(val);
|
||||
}
|
||||
return this[method].apply(this, colorFloats);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.fromComputedStyle */
|
||||
fromComputedStyle: function (elem, style) {
|
||||
var d = MochiKit.DOM;
|
||||
var cls = MochiKit.Color.Color;
|
||||
for (elem = d.getElement(elem); elem; elem = elem.parentNode) {
|
||||
var actualColor = MochiKit.Style.computedStyle.apply(d, arguments);
|
||||
if (!actualColor) {
|
||||
continue;
|
||||
}
|
||||
var color = cls.fromString(actualColor);
|
||||
if (!color) {
|
||||
break;
|
||||
}
|
||||
if (color.asRGB().a > 0) {
|
||||
return color;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.fromBackground */
|
||||
fromBackground: function (elem) {
|
||||
var cls = MochiKit.Color.Color;
|
||||
return cls.fromComputedStyle(
|
||||
elem, "backgroundColor", "background-color") || cls.whiteColor();
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.fromText */
|
||||
fromText: function (elem) {
|
||||
var cls = MochiKit.Color.Color;
|
||||
return cls.fromComputedStyle(
|
||||
elem, "color", "color") || cls.blackColor();
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.Color.namedColors */
|
||||
namedColors: function () {
|
||||
return MochiKit.Base.clone(MochiKit.Color.Color._namedColors);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Module level functions
|
||||
|
||||
MochiKit.Base.update(MochiKit.Color, {
|
||||
/** @id MochiKit.Color.clampColorComponent */
|
||||
clampColorComponent: function (v, scale) {
|
||||
v *= scale;
|
||||
if (v < 0) {
|
||||
return 0;
|
||||
} else if (v > scale) {
|
||||
return scale;
|
||||
} else {
|
||||
return v;
|
||||
}
|
||||
},
|
||||
|
||||
_hslValue: function (n1, n2, hue) {
|
||||
if (hue > 6.0) {
|
||||
hue -= 6.0;
|
||||
} else if (hue < 0.0) {
|
||||
hue += 6.0;
|
||||
}
|
||||
var val;
|
||||
if (hue < 1.0) {
|
||||
val = n1 + (n2 - n1) * hue;
|
||||
} else if (hue < 3.0) {
|
||||
val = n2;
|
||||
} else if (hue < 4.0) {
|
||||
val = n1 + (n2 - n1) * (4.0 - hue);
|
||||
} else {
|
||||
val = n1;
|
||||
}
|
||||
return val;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.hsvToRGB */
|
||||
hsvToRGB: function (hue, saturation, value, alpha) {
|
||||
if (arguments.length == 1) {
|
||||
var hsv = hue;
|
||||
hue = hsv.h;
|
||||
saturation = hsv.s;
|
||||
value = hsv.v;
|
||||
alpha = hsv.a;
|
||||
}
|
||||
var red;
|
||||
var green;
|
||||
var blue;
|
||||
if (saturation === 0) {
|
||||
red = 0;
|
||||
green = 0;
|
||||
blue = 0;
|
||||
} else {
|
||||
var i = Math.floor(hue * 6);
|
||||
var f = (hue * 6) - i;
|
||||
var p = value * (1 - saturation);
|
||||
var q = value * (1 - (saturation * f));
|
||||
var t = value * (1 - (saturation * (1 - f)));
|
||||
switch (i) {
|
||||
case 1: red = q; green = value; blue = p; break;
|
||||
case 2: red = p; green = value; blue = t; break;
|
||||
case 3: red = p; green = q; blue = value; break;
|
||||
case 4: red = t; green = p; blue = value; break;
|
||||
case 5: red = value; green = p; blue = q; break;
|
||||
case 6: // fall through
|
||||
case 0: red = value; green = t; blue = p; break;
|
||||
}
|
||||
}
|
||||
return {
|
||||
r: red,
|
||||
g: green,
|
||||
b: blue,
|
||||
a: alpha
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.hslToRGB */
|
||||
hslToRGB: function (hue, saturation, lightness, alpha) {
|
||||
if (arguments.length == 1) {
|
||||
var hsl = hue;
|
||||
hue = hsl.h;
|
||||
saturation = hsl.s;
|
||||
lightness = hsl.l;
|
||||
alpha = hsl.a;
|
||||
}
|
||||
var red;
|
||||
var green;
|
||||
var blue;
|
||||
if (saturation === 0) {
|
||||
red = lightness;
|
||||
green = lightness;
|
||||
blue = lightness;
|
||||
} else {
|
||||
var m2;
|
||||
if (lightness <= 0.5) {
|
||||
m2 = lightness * (1.0 + saturation);
|
||||
} else {
|
||||
m2 = lightness + saturation - (lightness * saturation);
|
||||
}
|
||||
var m1 = (2.0 * lightness) - m2;
|
||||
var f = MochiKit.Color._hslValue;
|
||||
var h6 = hue * 6.0;
|
||||
red = f(m1, m2, h6 + 2);
|
||||
green = f(m1, m2, h6);
|
||||
blue = f(m1, m2, h6 - 2);
|
||||
}
|
||||
return {
|
||||
r: red,
|
||||
g: green,
|
||||
b: blue,
|
||||
a: alpha
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.rgbToHSV */
|
||||
rgbToHSV: function (red, green, blue, alpha) {
|
||||
if (arguments.length == 1) {
|
||||
var rgb = red;
|
||||
red = rgb.r;
|
||||
green = rgb.g;
|
||||
blue = rgb.b;
|
||||
alpha = rgb.a;
|
||||
}
|
||||
var max = Math.max(Math.max(red, green), blue);
|
||||
var min = Math.min(Math.min(red, green), blue);
|
||||
var hue;
|
||||
var saturation;
|
||||
var value = max;
|
||||
if (min == max) {
|
||||
hue = 0;
|
||||
saturation = 0;
|
||||
} else {
|
||||
var delta = (max - min);
|
||||
saturation = delta / max;
|
||||
|
||||
if (red == max) {
|
||||
hue = (green - blue) / delta;
|
||||
} else if (green == max) {
|
||||
hue = 2 + ((blue - red) / delta);
|
||||
} else {
|
||||
hue = 4 + ((red - green) / delta);
|
||||
}
|
||||
hue /= 6;
|
||||
if (hue < 0) {
|
||||
hue += 1;
|
||||
}
|
||||
if (hue > 1) {
|
||||
hue -= 1;
|
||||
}
|
||||
}
|
||||
return {
|
||||
h: hue,
|
||||
s: saturation,
|
||||
v: value,
|
||||
a: alpha
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.rgbToHSL */
|
||||
rgbToHSL: function (red, green, blue, alpha) {
|
||||
if (arguments.length == 1) {
|
||||
var rgb = red;
|
||||
red = rgb.r;
|
||||
green = rgb.g;
|
||||
blue = rgb.b;
|
||||
alpha = rgb.a;
|
||||
}
|
||||
var max = Math.max(red, Math.max(green, blue));
|
||||
var min = Math.min(red, Math.min(green, blue));
|
||||
var hue;
|
||||
var saturation;
|
||||
var lightness = (max + min) / 2.0;
|
||||
var delta = max - min;
|
||||
if (delta === 0) {
|
||||
hue = 0;
|
||||
saturation = 0;
|
||||
} else {
|
||||
if (lightness <= 0.5) {
|
||||
saturation = delta / (max + min);
|
||||
} else {
|
||||
saturation = delta / (2 - max - min);
|
||||
}
|
||||
if (red == max) {
|
||||
hue = (green - blue) / delta;
|
||||
} else if (green == max) {
|
||||
hue = 2 + ((blue - red) / delta);
|
||||
} else {
|
||||
hue = 4 + ((red - green) / delta);
|
||||
}
|
||||
hue /= 6;
|
||||
if (hue < 0) {
|
||||
hue += 1;
|
||||
}
|
||||
if (hue > 1) {
|
||||
hue -= 1;
|
||||
}
|
||||
|
||||
}
|
||||
return {
|
||||
h: hue,
|
||||
s: saturation,
|
||||
l: lightness,
|
||||
a: alpha
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Color.toColorPart */
|
||||
toColorPart: function (num) {
|
||||
num = Math.round(num);
|
||||
var digits = num.toString(16);
|
||||
if (num < 16) {
|
||||
return '0' + digits;
|
||||
}
|
||||
return digits;
|
||||
},
|
||||
|
||||
__new__: function () {
|
||||
var m = MochiKit.Base;
|
||||
/** @id MochiKit.Color.fromRGBString */
|
||||
this.Color.fromRGBString = m.bind(
|
||||
this.Color._fromColorString, this.Color, "rgb", "fromRGB",
|
||||
[1.0/255.0, 1.0/255.0, 1.0/255.0, 1]
|
||||
);
|
||||
/** @id MochiKit.Color.fromHSLString */
|
||||
this.Color.fromHSLString = m.bind(
|
||||
this.Color._fromColorString, this.Color, "hsl", "fromHSL",
|
||||
[1.0/360.0, 0.01, 0.01, 1]
|
||||
);
|
||||
|
||||
var third = 1.0 / 3.0;
|
||||
/** @id MochiKit.Color.colors */
|
||||
var colors = {
|
||||
// NSColor colors plus transparent
|
||||
/** @id MochiKit.Color.blackColor */
|
||||
black: [0, 0, 0],
|
||||
/** @id MochiKit.Color.blueColor */
|
||||
blue: [0, 0, 1],
|
||||
/** @id MochiKit.Color.brownColor */
|
||||
brown: [0.6, 0.4, 0.2],
|
||||
/** @id MochiKit.Color.cyanColor */
|
||||
cyan: [0, 1, 1],
|
||||
/** @id MochiKit.Color.darkGrayColor */
|
||||
darkGray: [third, third, third],
|
||||
/** @id MochiKit.Color.grayColor */
|
||||
gray: [0.5, 0.5, 0.5],
|
||||
/** @id MochiKit.Color.greenColor */
|
||||
green: [0, 1, 0],
|
||||
/** @id MochiKit.Color.lightGrayColor */
|
||||
lightGray: [2 * third, 2 * third, 2 * third],
|
||||
/** @id MochiKit.Color.magentaColor */
|
||||
magenta: [1, 0, 1],
|
||||
/** @id MochiKit.Color.orangeColor */
|
||||
orange: [1, 0.5, 0],
|
||||
/** @id MochiKit.Color.purpleColor */
|
||||
purple: [0.5, 0, 0.5],
|
||||
/** @id MochiKit.Color.redColor */
|
||||
red: [1, 0, 0],
|
||||
/** @id MochiKit.Color.transparentColor */
|
||||
transparent: [0, 0, 0, 0],
|
||||
/** @id MochiKit.Color.whiteColor */
|
||||
white: [1, 1, 1],
|
||||
/** @id MochiKit.Color.yellowColor */
|
||||
yellow: [1, 1, 0]
|
||||
};
|
||||
|
||||
var makeColor = function (name, r, g, b, a) {
|
||||
var rval = this.fromRGB(r, g, b, a);
|
||||
this[name] = function () { return rval; };
|
||||
return rval;
|
||||
};
|
||||
|
||||
for (var k in colors) {
|
||||
var name = k + "Color";
|
||||
var bindArgs = m.concat(
|
||||
[makeColor, this.Color, name],
|
||||
colors[k]
|
||||
);
|
||||
this.Color[name] = m.bind.apply(null, bindArgs);
|
||||
}
|
||||
|
||||
var isColor = function () {
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
if (!(arguments[i] instanceof Color)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
var compareColor = function (a, b) {
|
||||
return a.compareRGB(b);
|
||||
};
|
||||
|
||||
m.nameFunctions(this);
|
||||
|
||||
m.registerComparator(this.Color.NAME, isColor, compareColor);
|
||||
|
||||
this.EXPORT_TAGS = {
|
||||
":common": this.EXPORT,
|
||||
":all": m.concat(this.EXPORT, this.EXPORT_OK)
|
||||
};
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
MochiKit.Color.EXPORT = [
|
||||
"Color"
|
||||
];
|
||||
|
||||
MochiKit.Color.EXPORT_OK = [
|
||||
"clampColorComponent",
|
||||
"rgbToHSL",
|
||||
"hslToRGB",
|
||||
"rgbToHSV",
|
||||
"hsvToRGB",
|
||||
"toColorPart"
|
||||
];
|
||||
|
||||
MochiKit.Color.__new__();
|
||||
|
||||
MochiKit.Base._exportSymbols(this, MochiKit.Color);
|
||||
|
||||
// Full table of css3 X11 colors <http://www.w3.org/TR/css3-color/#X11COLORS>
|
||||
|
||||
MochiKit.Color.Color._namedColors = {
|
||||
aliceblue: "#f0f8ff",
|
||||
antiquewhite: "#faebd7",
|
||||
aqua: "#00ffff",
|
||||
aquamarine: "#7fffd4",
|
||||
azure: "#f0ffff",
|
||||
beige: "#f5f5dc",
|
||||
bisque: "#ffe4c4",
|
||||
black: "#000000",
|
||||
blanchedalmond: "#ffebcd",
|
||||
blue: "#0000ff",
|
||||
blueviolet: "#8a2be2",
|
||||
brown: "#a52a2a",
|
||||
burlywood: "#deb887",
|
||||
cadetblue: "#5f9ea0",
|
||||
chartreuse: "#7fff00",
|
||||
chocolate: "#d2691e",
|
||||
coral: "#ff7f50",
|
||||
cornflowerblue: "#6495ed",
|
||||
cornsilk: "#fff8dc",
|
||||
crimson: "#dc143c",
|
||||
cyan: "#00ffff",
|
||||
darkblue: "#00008b",
|
||||
darkcyan: "#008b8b",
|
||||
darkgoldenrod: "#b8860b",
|
||||
darkgray: "#a9a9a9",
|
||||
darkgreen: "#006400",
|
||||
darkgrey: "#a9a9a9",
|
||||
darkkhaki: "#bdb76b",
|
||||
darkmagenta: "#8b008b",
|
||||
darkolivegreen: "#556b2f",
|
||||
darkorange: "#ff8c00",
|
||||
darkorchid: "#9932cc",
|
||||
darkred: "#8b0000",
|
||||
darksalmon: "#e9967a",
|
||||
darkseagreen: "#8fbc8f",
|
||||
darkslateblue: "#483d8b",
|
||||
darkslategray: "#2f4f4f",
|
||||
darkslategrey: "#2f4f4f",
|
||||
darkturquoise: "#00ced1",
|
||||
darkviolet: "#9400d3",
|
||||
deeppink: "#ff1493",
|
||||
deepskyblue: "#00bfff",
|
||||
dimgray: "#696969",
|
||||
dimgrey: "#696969",
|
||||
dodgerblue: "#1e90ff",
|
||||
firebrick: "#b22222",
|
||||
floralwhite: "#fffaf0",
|
||||
forestgreen: "#228b22",
|
||||
fuchsia: "#ff00ff",
|
||||
gainsboro: "#dcdcdc",
|
||||
ghostwhite: "#f8f8ff",
|
||||
gold: "#ffd700",
|
||||
goldenrod: "#daa520",
|
||||
gray: "#808080",
|
||||
green: "#008000",
|
||||
greenyellow: "#adff2f",
|
||||
grey: "#808080",
|
||||
honeydew: "#f0fff0",
|
||||
hotpink: "#ff69b4",
|
||||
indianred: "#cd5c5c",
|
||||
indigo: "#4b0082",
|
||||
ivory: "#fffff0",
|
||||
khaki: "#f0e68c",
|
||||
lavender: "#e6e6fa",
|
||||
lavenderblush: "#fff0f5",
|
||||
lawngreen: "#7cfc00",
|
||||
lemonchiffon: "#fffacd",
|
||||
lightblue: "#add8e6",
|
||||
lightcoral: "#f08080",
|
||||
lightcyan: "#e0ffff",
|
||||
lightgoldenrodyellow: "#fafad2",
|
||||
lightgray: "#d3d3d3",
|
||||
lightgreen: "#90ee90",
|
||||
lightgrey: "#d3d3d3",
|
||||
lightpink: "#ffb6c1",
|
||||
lightsalmon: "#ffa07a",
|
||||
lightseagreen: "#20b2aa",
|
||||
lightskyblue: "#87cefa",
|
||||
lightslategray: "#778899",
|
||||
lightslategrey: "#778899",
|
||||
lightsteelblue: "#b0c4de",
|
||||
lightyellow: "#ffffe0",
|
||||
lime: "#00ff00",
|
||||
limegreen: "#32cd32",
|
||||
linen: "#faf0e6",
|
||||
magenta: "#ff00ff",
|
||||
maroon: "#800000",
|
||||
mediumaquamarine: "#66cdaa",
|
||||
mediumblue: "#0000cd",
|
||||
mediumorchid: "#ba55d3",
|
||||
mediumpurple: "#9370db",
|
||||
mediumseagreen: "#3cb371",
|
||||
mediumslateblue: "#7b68ee",
|
||||
mediumspringgreen: "#00fa9a",
|
||||
mediumturquoise: "#48d1cc",
|
||||
mediumvioletred: "#c71585",
|
||||
midnightblue: "#191970",
|
||||
mintcream: "#f5fffa",
|
||||
mistyrose: "#ffe4e1",
|
||||
moccasin: "#ffe4b5",
|
||||
navajowhite: "#ffdead",
|
||||
navy: "#000080",
|
||||
oldlace: "#fdf5e6",
|
||||
olive: "#808000",
|
||||
olivedrab: "#6b8e23",
|
||||
orange: "#ffa500",
|
||||
orangered: "#ff4500",
|
||||
orchid: "#da70d6",
|
||||
palegoldenrod: "#eee8aa",
|
||||
palegreen: "#98fb98",
|
||||
paleturquoise: "#afeeee",
|
||||
palevioletred: "#db7093",
|
||||
papayawhip: "#ffefd5",
|
||||
peachpuff: "#ffdab9",
|
||||
peru: "#cd853f",
|
||||
pink: "#ffc0cb",
|
||||
plum: "#dda0dd",
|
||||
powderblue: "#b0e0e6",
|
||||
purple: "#800080",
|
||||
red: "#ff0000",
|
||||
rosybrown: "#bc8f8f",
|
||||
royalblue: "#4169e1",
|
||||
saddlebrown: "#8b4513",
|
||||
salmon: "#fa8072",
|
||||
sandybrown: "#f4a460",
|
||||
seagreen: "#2e8b57",
|
||||
seashell: "#fff5ee",
|
||||
sienna: "#a0522d",
|
||||
silver: "#c0c0c0",
|
||||
skyblue: "#87ceeb",
|
||||
slateblue: "#6a5acd",
|
||||
slategray: "#708090",
|
||||
slategrey: "#708090",
|
||||
snow: "#fffafa",
|
||||
springgreen: "#00ff7f",
|
||||
steelblue: "#4682b4",
|
||||
tan: "#d2b48c",
|
||||
teal: "#008080",
|
||||
thistle: "#d8bfd8",
|
||||
tomato: "#ff6347",
|
||||
turquoise: "#40e0d0",
|
||||
violet: "#ee82ee",
|
||||
wheat: "#f5deb3",
|
||||
white: "#ffffff",
|
||||
whitesmoke: "#f5f5f5",
|
||||
yellow: "#ffff00",
|
||||
yellowgreen: "#9acd32"
|
||||
};
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,216 @@
|
|||
/***
|
||||
|
||||
MochiKit.DateTime 1.4
|
||||
|
||||
See <http://mochikit.com/> for documentation, downloads, license, etc.
|
||||
|
||||
(c) 2005 Bob Ippolito. All rights Reserved.
|
||||
|
||||
***/
|
||||
|
||||
if (typeof(dojo) != 'undefined') {
|
||||
dojo.provide('MochiKit.DateTime');
|
||||
}
|
||||
|
||||
if (typeof(MochiKit) == 'undefined') {
|
||||
MochiKit = {};
|
||||
}
|
||||
|
||||
if (typeof(MochiKit.DateTime) == 'undefined') {
|
||||
MochiKit.DateTime = {};
|
||||
}
|
||||
|
||||
MochiKit.DateTime.NAME = "MochiKit.DateTime";
|
||||
MochiKit.DateTime.VERSION = "1.4";
|
||||
MochiKit.DateTime.__repr__ = function () {
|
||||
return "[" + this.NAME + " " + this.VERSION + "]";
|
||||
};
|
||||
MochiKit.DateTime.toString = function () {
|
||||
return this.__repr__();
|
||||
};
|
||||
|
||||
/** @id MochiKit.DateTime.isoDate */
|
||||
MochiKit.DateTime.isoDate = function (str) {
|
||||
str = str + "";
|
||||
if (typeof(str) != "string" || str.length === 0) {
|
||||
return null;
|
||||
}
|
||||
var iso = str.split('-');
|
||||
if (iso.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return new Date(iso[0], iso[1] - 1, iso[2]);
|
||||
};
|
||||
|
||||
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}))?)?)?)?)?/;
|
||||
|
||||
/** @id MochiKit.DateTime.isoTimestamp */
|
||||
MochiKit.DateTime.isoTimestamp = function (str) {
|
||||
str = str + "";
|
||||
if (typeof(str) != "string" || str.length === 0) {
|
||||
return null;
|
||||
}
|
||||
var res = str.match(MochiKit.DateTime._isoRegexp);
|
||||
if (typeof(res) == "undefined" || res === null) {
|
||||
return null;
|
||||
}
|
||||
var year, month, day, hour, min, sec, msec;
|
||||
year = parseInt(res[1], 10);
|
||||
if (typeof(res[2]) == "undefined" || res[2] === '') {
|
||||
return new Date(year);
|
||||
}
|
||||
month = parseInt(res[2], 10) - 1;
|
||||
day = parseInt(res[3], 10);
|
||||
if (typeof(res[4]) == "undefined" || res[4] === '') {
|
||||
return new Date(year, month, day);
|
||||
}
|
||||
hour = parseInt(res[4], 10);
|
||||
min = parseInt(res[5], 10);
|
||||
sec = (typeof(res[6]) != "undefined" && res[6] !== '') ? parseInt(res[6], 10) : 0;
|
||||
if (typeof(res[7]) != "undefined" && res[7] !== '') {
|
||||
msec = Math.round(1000.0 * parseFloat("0." + res[7]));
|
||||
} else {
|
||||
msec = 0;
|
||||
}
|
||||
if ((typeof(res[8]) == "undefined" || res[8] === '') && (typeof(res[9]) == "undefined" || res[9] === '')) {
|
||||
return new Date(year, month, day, hour, min, sec, msec);
|
||||
}
|
||||
var ofs;
|
||||
if (typeof(res[9]) != "undefined" && res[9] !== '') {
|
||||
ofs = parseInt(res[10], 10) * 3600000;
|
||||
if (typeof(res[11]) != "undefined" && res[11] !== '') {
|
||||
ofs += parseInt(res[11], 10) * 60000;
|
||||
}
|
||||
if (res[9] == "-") {
|
||||
ofs = -ofs;
|
||||
}
|
||||
} else {
|
||||
ofs = 0;
|
||||
}
|
||||
return new Date(Date.UTC(year, month, day, hour, min, sec, msec) - ofs);
|
||||
};
|
||||
|
||||
/** @id MochiKit.DateTime.toISOTime */
|
||||
MochiKit.DateTime.toISOTime = function (date, realISO/* = false */) {
|
||||
if (typeof(date) == "undefined" || date === null) {
|
||||
return null;
|
||||
}
|
||||
var hh = date.getHours();
|
||||
var mm = date.getMinutes();
|
||||
var ss = date.getSeconds();
|
||||
var lst = [
|
||||
((realISO && (hh < 10)) ? "0" + hh : hh),
|
||||
((mm < 10) ? "0" + mm : mm),
|
||||
((ss < 10) ? "0" + ss : ss)
|
||||
];
|
||||
return lst.join(":");
|
||||
};
|
||||
|
||||
/** @id MochiKit.DateTime.toISOTimeStamp */
|
||||
MochiKit.DateTime.toISOTimestamp = function (date, realISO/* = false*/) {
|
||||
if (typeof(date) == "undefined" || date === null) {
|
||||
return null;
|
||||
}
|
||||
var sep = realISO ? "T" : " ";
|
||||
var foot = realISO ? "Z" : "";
|
||||
if (realISO) {
|
||||
date = new Date(date.getTime() + (date.getTimezoneOffset() * 60000));
|
||||
}
|
||||
return MochiKit.DateTime.toISODate(date) + sep + MochiKit.DateTime.toISOTime(date, realISO) + foot;
|
||||
};
|
||||
|
||||
/** @id MochiKit.DateTime.toISODate */
|
||||
MochiKit.DateTime.toISODate = function (date) {
|
||||
if (typeof(date) == "undefined" || date === null) {
|
||||
return null;
|
||||
}
|
||||
var _padTwo = MochiKit.DateTime._padTwo;
|
||||
return [
|
||||
date.getFullYear(),
|
||||
_padTwo(date.getMonth() + 1),
|
||||
_padTwo(date.getDate())
|
||||
].join("-");
|
||||
};
|
||||
|
||||
/** @id MochiKit.DateTime.americanDate */
|
||||
MochiKit.DateTime.americanDate = function (d) {
|
||||
d = d + "";
|
||||
if (typeof(d) != "string" || d.length === 0) {
|
||||
return null;
|
||||
}
|
||||
var a = d.split('/');
|
||||
return new Date(a[2], a[0] - 1, a[1]);
|
||||
};
|
||||
|
||||
MochiKit.DateTime._padTwo = function (n) {
|
||||
return (n > 9) ? n : "0" + n;
|
||||
};
|
||||
|
||||
/** @id MochiKit.DateTime.toPaddedAmericanDate */
|
||||
MochiKit.DateTime.toPaddedAmericanDate = function (d) {
|
||||
if (typeof(d) == "undefined" || d === null) {
|
||||
return null;
|
||||
}
|
||||
var _padTwo = MochiKit.DateTime._padTwo;
|
||||
return [
|
||||
_padTwo(d.getMonth() + 1),
|
||||
_padTwo(d.getDate()),
|
||||
d.getFullYear()
|
||||
].join('/');
|
||||
};
|
||||
|
||||
/** @id MochiKit.DateTime.toAmericanDate */
|
||||
MochiKit.DateTime.toAmericanDate = function (d) {
|
||||
if (typeof(d) == "undefined" || d === null) {
|
||||
return null;
|
||||
}
|
||||
return [d.getMonth() + 1, d.getDate(), d.getFullYear()].join('/');
|
||||
};
|
||||
|
||||
MochiKit.DateTime.EXPORT = [
|
||||
"isoDate",
|
||||
"isoTimestamp",
|
||||
"toISOTime",
|
||||
"toISOTimestamp",
|
||||
"toISODate",
|
||||
"americanDate",
|
||||
"toPaddedAmericanDate",
|
||||
"toAmericanDate"
|
||||
];
|
||||
|
||||
MochiKit.DateTime.EXPORT_OK = [];
|
||||
MochiKit.DateTime.EXPORT_TAGS = {
|
||||
":common": MochiKit.DateTime.EXPORT,
|
||||
":all": MochiKit.DateTime.EXPORT
|
||||
};
|
||||
|
||||
MochiKit.DateTime.__new__ = function () {
|
||||
// MochiKit.Base.nameFunctions(this);
|
||||
var base = this.NAME + ".";
|
||||
for (var k in this) {
|
||||
var o = this[k];
|
||||
if (typeof(o) == 'function' && typeof(o.NAME) == 'undefined') {
|
||||
try {
|
||||
o.NAME = base + k;
|
||||
} catch (e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MochiKit.DateTime.__new__();
|
||||
|
||||
if (typeof(MochiKit.Base) != "undefined") {
|
||||
MochiKit.Base._exportSymbols(this, MochiKit.DateTime);
|
||||
} else {
|
||||
(function (globals, module) {
|
||||
if ((typeof(JSAN) == 'undefined' && typeof(dojo) == 'undefined')
|
||||
|| (MochiKit.__export__ === false)) {
|
||||
var all = module.EXPORT_TAGS[":all"];
|
||||
for (var i = 0; i < all.length; i++) {
|
||||
globals[all[i]] = module[all[i]];
|
||||
}
|
||||
}
|
||||
})(this, MochiKit.DateTime);
|
||||
}
|
|
@ -0,0 +1,821 @@
|
|||
/***
|
||||
MochiKit.DragAndDrop 1.4
|
||||
|
||||
See <http://mochikit.com/> for documentation, downloads, license, etc.
|
||||
|
||||
Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||
Mochi-ized By Thomas Herve (_firstname_@nimail.org)
|
||||
|
||||
***/
|
||||
|
||||
if (typeof(dojo) != 'undefined') {
|
||||
dojo.provide('MochiKit.DragAndDrop');
|
||||
dojo.require('MochiKit.Base');
|
||||
dojo.require('MochiKit.DOM');
|
||||
dojo.require('MochiKit.Iter');
|
||||
dojo.require('MochiKit.Visual');
|
||||
dojo.require('MochiKit.Signal');
|
||||
}
|
||||
|
||||
if (typeof(JSAN) != 'undefined') {
|
||||
JSAN.use("MochiKit.Base", []);
|
||||
JSAN.use("MochiKit.DOM", []);
|
||||
JSAN.use("MochiKit.Visual", []);
|
||||
JSAN.use("MochiKit.Iter", []);
|
||||
JSAN.use("MochiKit.Signal", []);
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof(MochiKit.Base) == 'undefined' ||
|
||||
typeof(MochiKit.DOM) == 'undefined' ||
|
||||
typeof(MochiKit.Visual) == 'undefined' ||
|
||||
typeof(MochiKit.Signal) == 'undefined' ||
|
||||
typeof(MochiKit.Iter) == 'undefined') {
|
||||
throw "";
|
||||
}
|
||||
} catch (e) {
|
||||
throw "MochiKit.DragAndDrop depends on MochiKit.Base, MochiKit.DOM, MochiKit.Visual, MochiKit.Signal and MochiKit.Iter!";
|
||||
}
|
||||
|
||||
if (typeof(MochiKit.DragAndDrop) == 'undefined') {
|
||||
MochiKit.DragAndDrop = {};
|
||||
}
|
||||
|
||||
MochiKit.DragAndDrop.NAME = 'MochiKit.DragAndDrop';
|
||||
MochiKit.DragAndDrop.VERSION = '1.4';
|
||||
|
||||
MochiKit.DragAndDrop.__repr__ = function () {
|
||||
return '[' + this.NAME + ' ' + this.VERSION + ']';
|
||||
};
|
||||
|
||||
MochiKit.DragAndDrop.toString = function () {
|
||||
return this.__repr__();
|
||||
};
|
||||
|
||||
MochiKit.DragAndDrop.EXPORT = [
|
||||
"Droppable",
|
||||
"Draggable"
|
||||
];
|
||||
|
||||
MochiKit.DragAndDrop.EXPORT_OK = [
|
||||
"Droppables",
|
||||
"Draggables"
|
||||
];
|
||||
|
||||
MochiKit.DragAndDrop.Droppables = {
|
||||
/***
|
||||
|
||||
Manage all droppables. Shouldn't be used, use the Droppable object instead.
|
||||
|
||||
***/
|
||||
drops: [],
|
||||
|
||||
remove: function (element) {
|
||||
this.drops = MochiKit.Base.filter(function (d) {
|
||||
return d.element != MochiKit.DOM.getElement(element)
|
||||
}, this.drops);
|
||||
},
|
||||
|
||||
register: function (drop) {
|
||||
this.drops.push(drop);
|
||||
},
|
||||
|
||||
unregister: function (drop) {
|
||||
this.drops = MochiKit.Base.filter(function (d) {
|
||||
return d != drop;
|
||||
}, this.drops);
|
||||
},
|
||||
|
||||
prepare: function (element) {
|
||||
MochiKit.Base.map(function (drop) {
|
||||
if (drop.isAccepted(element)) {
|
||||
if (drop.options.activeclass) {
|
||||
MochiKit.DOM.addElementClass(drop.element,
|
||||
drop.options.activeclass);
|
||||
}
|
||||
drop.options.onactive(drop.element, element);
|
||||
}
|
||||
}, this.drops);
|
||||
},
|
||||
|
||||
findDeepestChild: function (drops) {
|
||||
deepest = drops[0];
|
||||
|
||||
for (i = 1; i < drops.length; ++i) {
|
||||
if (MochiKit.DOM.isParent(drops[i].element, deepest.element)) {
|
||||
deepest = drops[i];
|
||||
}
|
||||
}
|
||||
return deepest;
|
||||
},
|
||||
|
||||
show: function (point, element) {
|
||||
if (!this.drops.length) {
|
||||
return;
|
||||
}
|
||||
var affected = [];
|
||||
|
||||
if (this.last_active) {
|
||||
this.last_active.deactivate();
|
||||
}
|
||||
MochiKit.Iter.forEach(this.drops, function (drop) {
|
||||
if (drop.isAffected(point, element)) {
|
||||
affected.push(drop);
|
||||
}
|
||||
});
|
||||
if (affected.length > 0) {
|
||||
drop = this.findDeepestChild(affected);
|
||||
MochiKit.Position.within(drop.element, point.page.x, point.page.y);
|
||||
drop.options.onhover(element, drop.element,
|
||||
MochiKit.Position.overlap(drop.options.overlap, drop.element));
|
||||
drop.activate();
|
||||
}
|
||||
},
|
||||
|
||||
fire: function (event, element) {
|
||||
if (!this.last_active) {
|
||||
return;
|
||||
}
|
||||
MochiKit.Position.prepare();
|
||||
|
||||
if (this.last_active.isAffected(event.mouse(), element)) {
|
||||
this.last_active.options.ondrop(element,
|
||||
this.last_active.element, event);
|
||||
}
|
||||
},
|
||||
|
||||
reset: function (element) {
|
||||
MochiKit.Base.map(function (drop) {
|
||||
if (drop.options.activeclass) {
|
||||
MochiKit.DOM.removeElementClass(drop.element,
|
||||
drop.options.activeclass);
|
||||
}
|
||||
drop.options.ondesactive(drop.element, element);
|
||||
}, this.drops);
|
||||
if (this.last_active) {
|
||||
this.last_active.deactivate();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** @id MochiKit.DragAndDrop.Droppable */
|
||||
MochiKit.DragAndDrop.Droppable = function (element, options) {
|
||||
this.__init__(element, options);
|
||||
};
|
||||
|
||||
MochiKit.DragAndDrop.Droppable.prototype = {
|
||||
/***
|
||||
|
||||
A droppable object. Simple use is to create giving an element:
|
||||
|
||||
new MochiKit.DragAndDrop.Droppable('myelement');
|
||||
|
||||
Generally you'll want to define the 'ondrop' function and maybe the
|
||||
'accept' option to filter draggables.
|
||||
|
||||
***/
|
||||
__class__: MochiKit.DragAndDrop.Droppable,
|
||||
|
||||
__init__: function (element, /* optional */options) {
|
||||
var d = MochiKit.DOM;
|
||||
var b = MochiKit.Base;
|
||||
this.element = d.getElement(element);
|
||||
this.options = b.update({
|
||||
|
||||
/** @id MochiKit.DragAndDrop.greedy */
|
||||
greedy: true,
|
||||
|
||||
/** @id MochiKit.DragAndDrop.hoverclass */
|
||||
hoverclass: null,
|
||||
|
||||
/** @id MochiKit.DragAndDrop.activeclass */
|
||||
activeclass: null,
|
||||
|
||||
/** @id MochiKit.DragAndDrop.hoverfunc */
|
||||
hoverfunc: b.noop,
|
||||
|
||||
/** @id MochiKit.DragAndDrop.accept */
|
||||
accept: null,
|
||||
|
||||
/** @id MochiKit.DragAndDrop.onactive */
|
||||
onactive: b.noop,
|
||||
|
||||
/** @id MochiKit.DragAndDrop.ondesactive */
|
||||
ondesactive: b.noop,
|
||||
|
||||
/** @id MochiKit.DragAndDrop.onhover */
|
||||
onhover: b.noop,
|
||||
|
||||
/** @id MochiKit.DragAndDrop.ondrop */
|
||||
ondrop: b.noop,
|
||||
|
||||
/** @id MochiKit.DragAndDrop.containment */
|
||||
containment: [],
|
||||
tree: false
|
||||
}, options || {});
|
||||
|
||||
// cache containers
|
||||
this.options._containers = [];
|
||||
b.map(MochiKit.Base.bind(function (c) {
|
||||
this.options._containers.push(d.getElement(c));
|
||||
}, this), this.options.containment);
|
||||
|
||||
d.makePositioned(this.element); // fix IE
|
||||
|
||||
MochiKit.DragAndDrop.Droppables.register(this);
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.isContained */
|
||||
isContained: function (element) {
|
||||
if (this.options._containers.length) {
|
||||
var containmentNode;
|
||||
if (this.options.tree) {
|
||||
containmentNode = element.treeNode;
|
||||
} else {
|
||||
containmentNode = element.parentNode;
|
||||
}
|
||||
return MochiKit.Iter.some(this.options._containers, function (c) {
|
||||
return containmentNode == c;
|
||||
});
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.isAccepted */
|
||||
isAccepted: function (element) {
|
||||
return ((!this.options.accept) || MochiKit.Iter.some(
|
||||
this.options.accept, function (c) {
|
||||
return MochiKit.DOM.hasElementClass(element, c);
|
||||
}));
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.isAffected */
|
||||
isAffected: function (point, element) {
|
||||
return ((this.element != element) &&
|
||||
this.isContained(element) &&
|
||||
this.isAccepted(element) &&
|
||||
MochiKit.Position.within(this.element, point.page.x,
|
||||
point.page.y));
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.deactivate */
|
||||
deactivate: function () {
|
||||
/***
|
||||
|
||||
A droppable is deactivate when a draggable has been over it and left.
|
||||
|
||||
***/
|
||||
if (this.options.hoverclass) {
|
||||
MochiKit.DOM.removeElementClass(this.element,
|
||||
this.options.hoverclass);
|
||||
}
|
||||
this.options.hoverfunc(this.element, false);
|
||||
MochiKit.DragAndDrop.Droppables.last_active = null;
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.activate */
|
||||
activate: function () {
|
||||
/***
|
||||
|
||||
A droppable is active when a draggable is over it.
|
||||
|
||||
***/
|
||||
if (this.options.hoverclass) {
|
||||
MochiKit.DOM.addElementClass(this.element, this.options.hoverclass);
|
||||
}
|
||||
this.options.hoverfunc(this.element, true);
|
||||
MochiKit.DragAndDrop.Droppables.last_active = this;
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.destroy */
|
||||
destroy: function () {
|
||||
/***
|
||||
|
||||
Delete this droppable.
|
||||
|
||||
***/
|
||||
MochiKit.DragAndDrop.Droppables.unregister(this);
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.repr */
|
||||
repr: function () {
|
||||
return '[' + this.__class__.NAME + ", options:" + MochiKit.Base.repr(this.options) + "]";
|
||||
}
|
||||
};
|
||||
|
||||
MochiKit.DragAndDrop.Draggables = {
|
||||
/***
|
||||
|
||||
Manage draggables elements. Not intended to direct use.
|
||||
|
||||
***/
|
||||
drags: [],
|
||||
|
||||
register: function (draggable) {
|
||||
if (this.drags.length === 0) {
|
||||
var conn = MochiKit.Signal.connect;
|
||||
this.eventMouseUp = conn(document, 'onmouseup', this, this.endDrag);
|
||||
this.eventMouseMove = conn(document, 'onmousemove', this,
|
||||
this.updateDrag);
|
||||
this.eventKeypress = conn(document, 'onkeypress', this,
|
||||
this.keyPress);
|
||||
}
|
||||
this.drags.push(draggable);
|
||||
},
|
||||
|
||||
unregister: function (draggable) {
|
||||
this.drags = MochiKit.Base.filter(function (d) {
|
||||
return d != draggable;
|
||||
}, this.drags);
|
||||
if (this.drags.length === 0) {
|
||||
var disc = MochiKit.Signal.disconnect
|
||||
disc(this.eventMouseUp);
|
||||
disc(this.eventMouseMove);
|
||||
disc(this.eventKeypress);
|
||||
}
|
||||
},
|
||||
|
||||
activate: function (draggable) {
|
||||
// allows keypress events if window is not currently focused
|
||||
// fails for Safari
|
||||
window.focus();
|
||||
this.activeDraggable = draggable;
|
||||
},
|
||||
|
||||
deactivate: function () {
|
||||
this.activeDraggable = null;
|
||||
},
|
||||
|
||||
updateDrag: function (event) {
|
||||
if (!this.activeDraggable) {
|
||||
return;
|
||||
}
|
||||
var pointer = event.mouse();
|
||||
// Mozilla-based browsers fire successive mousemove events with
|
||||
// the same coordinates, prevent needless redrawing (moz bug?)
|
||||
if (this._lastPointer && (MochiKit.Base.repr(this._lastPointer.page) ==
|
||||
MochiKit.Base.repr(pointer.page))) {
|
||||
return;
|
||||
}
|
||||
this._lastPointer = pointer;
|
||||
this.activeDraggable.updateDrag(event, pointer);
|
||||
},
|
||||
|
||||
endDrag: function (event) {
|
||||
if (!this.activeDraggable) {
|
||||
return;
|
||||
}
|
||||
this._lastPointer = null;
|
||||
this.activeDraggable.endDrag(event);
|
||||
this.activeDraggable = null;
|
||||
},
|
||||
|
||||
keyPress: function (event) {
|
||||
if (this.activeDraggable) {
|
||||
this.activeDraggable.keyPress(event);
|
||||
}
|
||||
},
|
||||
|
||||
notify: function (eventName, draggable, event) {
|
||||
MochiKit.Signal.signal(this, eventName, draggable, event);
|
||||
}
|
||||
};
|
||||
|
||||
/** @id MochiKit.DragAndDrop.Draggable */
|
||||
MochiKit.DragAndDrop.Draggable = function (element, options) {
|
||||
this.__init__(element, options);
|
||||
};
|
||||
|
||||
MochiKit.DragAndDrop.Draggable.prototype = {
|
||||
/***
|
||||
|
||||
A draggable object. Simple instantiate :
|
||||
|
||||
new MochiKit.DragAndDrop.Draggable('myelement');
|
||||
|
||||
***/
|
||||
__class__ : MochiKit.DragAndDrop.Draggable,
|
||||
|
||||
__init__: function (element, /* optional */options) {
|
||||
var v = MochiKit.Visual;
|
||||
var b = MochiKit.Base;
|
||||
options = b.update({
|
||||
|
||||
/** @id MochiKit.DragAndDrop.handle */
|
||||
handle: false,
|
||||
|
||||
/** @id MochiKit.DragAndDrop.starteffect */
|
||||
starteffect: function (innerelement) {
|
||||
this._savedOpacity = MochiKit.Style.getOpacity(innerelement) || 1.0;
|
||||
new v.Opacity(innerelement, {duration:0.2, from:this._savedOpacity, to:0.7});
|
||||
},
|
||||
/** @id MochiKit.DragAndDrop.reverteffect */
|
||||
reverteffect: function (innerelement, top_offset, left_offset) {
|
||||
var dur = Math.sqrt(Math.abs(top_offset^2) +
|
||||
Math.abs(left_offset^2))*0.02;
|
||||
return new v.Move(innerelement,
|
||||
{x: -left_offset, y: -top_offset, duration: dur});
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.endeffect */
|
||||
endeffect: function (innerelement) {
|
||||
new v.Opacity(innerelement, {duration:0.2, from:0.7, to:this._savedOpacity});
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.onchange */
|
||||
onchange: b.noop,
|
||||
|
||||
/** @id MochiKit.DragAndDrop.zindex */
|
||||
zindex: 1000,
|
||||
|
||||
/** @id MochiKit.DragAndDrop.revert */
|
||||
revert: false,
|
||||
|
||||
/** @id MochiKit.DragAndDrop.scroll */
|
||||
scroll: false,
|
||||
|
||||
/** @id MochiKit.DragAndDrop.scrollSensitivity */
|
||||
scrollSensitivity: 20,
|
||||
|
||||
/** @id MochiKit.DragAndDrop.scrollSpeed */
|
||||
scrollSpeed: 15,
|
||||
// false, or xy or [x, y] or function (x, y){return [x, y];}
|
||||
|
||||
/** @id MochiKit.DragAndDrop.snap */
|
||||
snap: false
|
||||
}, options || {});
|
||||
|
||||
var d = MochiKit.DOM;
|
||||
this.element = d.getElement(element);
|
||||
|
||||
if (options.handle && (typeof(options.handle) == 'string')) {
|
||||
this.handle = d.getFirstElementByTagAndClassName(null,
|
||||
options.handle, this.element);
|
||||
}
|
||||
if (!this.handle) {
|
||||
this.handle = d.getElement(options.handle);
|
||||
}
|
||||
if (!this.handle) {
|
||||
this.handle = this.element;
|
||||
}
|
||||
|
||||
if (options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
|
||||
options.scroll = d.getElement(options.scroll);
|
||||
this._isScrollChild = MochiKit.DOM.isChildNode(this.element, options.scroll);
|
||||
}
|
||||
|
||||
d.makePositioned(this.element); // fix IE
|
||||
|
||||
this.delta = this.currentDelta();
|
||||
this.options = options;
|
||||
this.dragging = false;
|
||||
|
||||
this.eventMouseDown = MochiKit.Signal.connect(this.handle,
|
||||
'onmousedown', this, this.initDrag);
|
||||
MochiKit.DragAndDrop.Draggables.register(this);
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.destroy */
|
||||
destroy: function () {
|
||||
MochiKit.Signal.disconnect(this.eventMouseDown);
|
||||
MochiKit.DragAndDrop.Draggables.unregister(this);
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.currentDelta */
|
||||
currentDelta: function () {
|
||||
var s = MochiKit.Style.getStyle;
|
||||
return [
|
||||
parseInt(s(this.element, 'left') || '0'),
|
||||
parseInt(s(this.element, 'top') || '0')];
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.initDrag */
|
||||
initDrag: function (event) {
|
||||
if (!event.mouse().button.left) {
|
||||
return;
|
||||
}
|
||||
// abort on form elements, fixes a Firefox issue
|
||||
var src = event.target();
|
||||
var tagName = (src.tagName || '').toUpperCase();
|
||||
if (tagName === 'INPUT' || tagName === 'SELECT' ||
|
||||
tagName === 'OPTION' || tagName === 'BUTTON' ||
|
||||
tagName === 'TEXTAREA') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._revert) {
|
||||
this._revert.cancel();
|
||||
this._revert = null;
|
||||
}
|
||||
|
||||
var pointer = event.mouse();
|
||||
var pos = MochiKit.Position.cumulativeOffset(this.element);
|
||||
this.offset = [pointer.page.x - pos.x, pointer.page.y - pos.y]
|
||||
|
||||
MochiKit.DragAndDrop.Draggables.activate(this);
|
||||
event.stop();
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.startDrag */
|
||||
startDrag: function (event) {
|
||||
this.dragging = true;
|
||||
if (this.options.selectclass) {
|
||||
MochiKit.DOM.addElementClass(this.element,
|
||||
this.options.selectclass);
|
||||
}
|
||||
if (this.options.zindex) {
|
||||
this.originalZ = parseInt(MochiKit.Style.getStyle(this.element,
|
||||
'z-index') || '0');
|
||||
this.element.style.zIndex = this.options.zindex;
|
||||
}
|
||||
|
||||
if (this.options.ghosting) {
|
||||
this._clone = this.element.cloneNode(true);
|
||||
this.ghostPosition = MochiKit.Position.absolutize(this.element);
|
||||
this.element.parentNode.insertBefore(this._clone, this.element);
|
||||
}
|
||||
|
||||
if (this.options.scroll) {
|
||||
if (this.options.scroll == window) {
|
||||
var where = this._getWindowScroll(this.options.scroll);
|
||||
this.originalScrollLeft = where.left;
|
||||
this.originalScrollTop = where.top;
|
||||
} else {
|
||||
this.originalScrollLeft = this.options.scroll.scrollLeft;
|
||||
this.originalScrollTop = this.options.scroll.scrollTop;
|
||||
}
|
||||
}
|
||||
|
||||
MochiKit.DragAndDrop.Droppables.prepare(this.element);
|
||||
MochiKit.DragAndDrop.Draggables.notify('start', this, event);
|
||||
if (this.options.starteffect) {
|
||||
this.options.starteffect(this.element);
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.updateDrag */
|
||||
updateDrag: function (event, pointer) {
|
||||
if (!this.dragging) {
|
||||
this.startDrag(event);
|
||||
}
|
||||
MochiKit.Position.prepare();
|
||||
MochiKit.DragAndDrop.Droppables.show(pointer, this.element);
|
||||
MochiKit.DragAndDrop.Draggables.notify('drag', this, event);
|
||||
this.draw(pointer);
|
||||
this.options.onchange(this);
|
||||
|
||||
if (this.options.scroll) {
|
||||
this.stopScrolling();
|
||||
var p, q;
|
||||
if (this.options.scroll == window) {
|
||||
var s = this._getWindowScroll(this.options.scroll);
|
||||
p = new MochiKit.Style.Coordinates(s.left, s.top);
|
||||
q = new MochiKit.Style.Coordinates(s.left + s.width,
|
||||
s.top + s.height);
|
||||
} else {
|
||||
p = MochiKit.Position.page(this.options.scroll);
|
||||
p.x += this.options.scroll.scrollLeft;
|
||||
p.y += this.options.scroll.scrollTop;
|
||||
p.x += (window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0);
|
||||
p.y += (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0);
|
||||
q = new MochiKit.Style.Coordinates(p.x + this.options.scroll.offsetWidth,
|
||||
p.y + this.options.scroll.offsetHeight);
|
||||
}
|
||||
var speed = [0, 0];
|
||||
if (pointer.page.x > (q.x - this.options.scrollSensitivity)) {
|
||||
speed[0] = pointer.page.x - (q.x - this.options.scrollSensitivity);
|
||||
} else if (pointer.page.x < (p.x + this.options.scrollSensitivity)) {
|
||||
speed[0] = pointer.page.x - (p.x + this.options.scrollSensitivity);
|
||||
}
|
||||
if (pointer.page.y > (q.y - this.options.scrollSensitivity)) {
|
||||
speed[1] = pointer.page.y - (q.y - this.options.scrollSensitivity);
|
||||
} else if (pointer.page.y < (p.y + this.options.scrollSensitivity)) {
|
||||
speed[1] = pointer.page.y - (p.y + this.options.scrollSensitivity);
|
||||
}
|
||||
this.startScrolling(speed);
|
||||
}
|
||||
|
||||
// fix AppleWebKit rendering
|
||||
if (/AppleWebKit'/.test(navigator.appVersion)) {
|
||||
window.scrollBy(0, 0);
|
||||
}
|
||||
event.stop();
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.finishDrag */
|
||||
finishDrag: function (event, success) {
|
||||
var dr = MochiKit.DragAndDrop;
|
||||
this.dragging = false;
|
||||
if (this.options.selectclass) {
|
||||
MochiKit.DOM.removeElementClass(this.element,
|
||||
this.options.selectclass);
|
||||
}
|
||||
|
||||
if (this.options.ghosting) {
|
||||
// XXX: from a user point of view, it would be better to remove
|
||||
// the node only *after* the MochiKit.Visual.Move end when used
|
||||
// with revert.
|
||||
MochiKit.Position.relativize(this.element, this.ghostPosition);
|
||||
MochiKit.DOM.removeElement(this._clone);
|
||||
this._clone = null;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
dr.Droppables.fire(event, this.element);
|
||||
}
|
||||
dr.Draggables.notify('end', this, event);
|
||||
|
||||
var revert = this.options.revert;
|
||||
if (revert && typeof(revert) == 'function') {
|
||||
revert = revert(this.element);
|
||||
}
|
||||
|
||||
var d = this.currentDelta();
|
||||
if (revert && this.options.reverteffect) {
|
||||
this._revert = this.options.reverteffect(this.element,
|
||||
d[1] - this.delta[1], d[0] - this.delta[0]);
|
||||
} else {
|
||||
this.delta = d;
|
||||
}
|
||||
|
||||
if (this.options.zindex) {
|
||||
this.element.style.zIndex = this.originalZ;
|
||||
}
|
||||
|
||||
if (this.options.endeffect) {
|
||||
this.options.endeffect(this.element);
|
||||
}
|
||||
|
||||
dr.Draggables.deactivate();
|
||||
dr.Droppables.reset(this.element);
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.keyPress */
|
||||
keyPress: function (event) {
|
||||
if (event.key().string != "KEY_ESCAPE") {
|
||||
return;
|
||||
}
|
||||
this.finishDrag(event, false);
|
||||
event.stop();
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.endDrag */
|
||||
endDrag: function (event) {
|
||||
if (!this.dragging) {
|
||||
return;
|
||||
}
|
||||
this.stopScrolling();
|
||||
this.finishDrag(event, true);
|
||||
event.stop();
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.draw */
|
||||
draw: function (point) {
|
||||
var pos = MochiKit.Position.cumulativeOffset(this.element);
|
||||
if (this.options.ghosting) {
|
||||
var r = MochiKit.Position.realOffset(this.element);
|
||||
pos.x += r.x - MochiKit.Position.windowOffset.x;
|
||||
pos.y += r.y - MochiKit.Position.windowOffset.y;
|
||||
}
|
||||
var d = this.currentDelta();
|
||||
pos.x -= d[0];
|
||||
pos.y -= d[1];
|
||||
|
||||
if (this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
|
||||
pos.x -= this.options.scroll.scrollLeft - this.originalScrollLeft;
|
||||
pos.y -= this.options.scroll.scrollTop - this.originalScrollTop;
|
||||
}
|
||||
|
||||
var p = [point.page.x - pos.x - this.offset[0],
|
||||
point.page.y - pos.y - this.offset[1]]
|
||||
|
||||
if (this.options.snap) {
|
||||
if (typeof(this.options.snap) == 'function') {
|
||||
p = this.options.snap(p[0], p[1]);
|
||||
} else {
|
||||
if (this.options.snap instanceof Array) {
|
||||
var i = -1;
|
||||
p = MochiKit.Base.map(MochiKit.Base.bind(function (v) {
|
||||
i += 1;
|
||||
return Math.round(v/this.options.snap[i]) *
|
||||
this.options.snap[i]
|
||||
}, this), p)
|
||||
} else {
|
||||
p = MochiKit.Base.map(MochiKit.Base.bind(function (v) {
|
||||
return Math.round(v/this.options.snap) *
|
||||
this.options.snap
|
||||
}, this), p)
|
||||
}
|
||||
}
|
||||
}
|
||||
var style = this.element.style;
|
||||
if ((!this.options.constraint) ||
|
||||
(this.options.constraint == 'horizontal')) {
|
||||
style.left = p[0] + 'px';
|
||||
}
|
||||
if ((!this.options.constraint) ||
|
||||
(this.options.constraint == 'vertical')) {
|
||||
style.top = p[1] + 'px';
|
||||
}
|
||||
if (style.visibility == 'hidden') {
|
||||
style.visibility = ''; // fix gecko rendering
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.stopScrolling */
|
||||
stopScrolling: function () {
|
||||
if (this.scrollInterval) {
|
||||
clearInterval(this.scrollInterval);
|
||||
this.scrollInterval = null;
|
||||
MochiKit.DragAndDrop.Draggables._lastScrollPointer = null;
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.startScrolling */
|
||||
startScrolling: function (speed) {
|
||||
if (!speed[0] && !speed[1]) {
|
||||
return;
|
||||
}
|
||||
this.scrollSpeed = [speed[0] * this.options.scrollSpeed,
|
||||
speed[1] * this.options.scrollSpeed];
|
||||
this.lastScrolled = new Date();
|
||||
this.scrollInterval = setInterval(MochiKit.Base.bind(this.scroll, this), 10);
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.scroll */
|
||||
scroll: function () {
|
||||
var current = new Date();
|
||||
var delta = current - this.lastScrolled;
|
||||
this.lastScrolled = current;
|
||||
|
||||
if (this.options.scroll == window) {
|
||||
var s = this._getWindowScroll(this.options.scroll);
|
||||
if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
|
||||
var d = delta / 1000;
|
||||
this.options.scroll.scrollTo(s.left + d * this.scrollSpeed[0],
|
||||
s.top + d * this.scrollSpeed[1]);
|
||||
}
|
||||
} else {
|
||||
this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
|
||||
this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
|
||||
}
|
||||
|
||||
var d = MochiKit.DragAndDrop;
|
||||
|
||||
MochiKit.Position.prepare();
|
||||
d.Droppables.show(d.Draggables._lastPointer, this.element);
|
||||
d.Draggables.notify('drag', this);
|
||||
if (this._isScrollChild) {
|
||||
d.Draggables._lastScrollPointer = d.Draggables._lastScrollPointer || d.Draggables._lastPointer;
|
||||
d.Draggables._lastScrollPointer.x += this.scrollSpeed[0] * delta / 1000;
|
||||
d.Draggables._lastScrollPointer.y += this.scrollSpeed[1] * delta / 1000;
|
||||
if (d.Draggables._lastScrollPointer.x < 0) {
|
||||
d.Draggables._lastScrollPointer.x = 0;
|
||||
}
|
||||
if (d.Draggables._lastScrollPointer.y < 0) {
|
||||
d.Draggables._lastScrollPointer.y = 0;
|
||||
}
|
||||
this.draw(d.Draggables._lastScrollPointer);
|
||||
}
|
||||
|
||||
this.options.onchange(this);
|
||||
},
|
||||
|
||||
_getWindowScroll: function (w) {
|
||||
var vp, w, h;
|
||||
MochiKit.DOM.withWindow(w, function () {
|
||||
vp = MochiKit.Style.getViewportPosition(w.document);
|
||||
});
|
||||
if (w.innerWidth) {
|
||||
w = w.innerWidth;
|
||||
h = w.innerHeight;
|
||||
} else if (w.document.documentElement && w.document.documentElement.clientWidth) {
|
||||
w = w.document.documentElement.clientWidth;
|
||||
h = w.document.documentElement.clientHeight;
|
||||
} else {
|
||||
w = w.document.body.offsetWidth;
|
||||
h = w.document.body.offsetHeight
|
||||
}
|
||||
return {top: vp.x, left: vp.y, width: w, height: h};
|
||||
},
|
||||
|
||||
/** @id MochiKit.DragAndDrop.repr */
|
||||
repr: function () {
|
||||
return '[' + this.__class__.NAME + ", options:" + MochiKit.Base.repr(this.options) + "]";
|
||||
}
|
||||
};
|
||||
|
||||
MochiKit.DragAndDrop.__new__ = function () {
|
||||
MochiKit.Base.nameFunctions(this);
|
||||
|
||||
this.EXPORT_TAGS = {
|
||||
":common": this.EXPORT,
|
||||
":all": MochiKit.Base.concat(this.EXPORT, this.EXPORT_OK)
|
||||
};
|
||||
};
|
||||
|
||||
MochiKit.DragAndDrop.__new__();
|
||||
|
||||
MochiKit.Base._exportSymbols(this, MochiKit.DragAndDrop);
|
||||
|
|
@ -0,0 +1,304 @@
|
|||
/***
|
||||
|
||||
MochiKit.Format 1.4
|
||||
|
||||
See <http://mochikit.com/> for documentation, downloads, license, etc.
|
||||
|
||||
(c) 2005 Bob Ippolito. All rights Reserved.
|
||||
|
||||
***/
|
||||
|
||||
if (typeof(dojo) != 'undefined') {
|
||||
dojo.provide('MochiKit.Format');
|
||||
}
|
||||
|
||||
if (typeof(MochiKit) == 'undefined') {
|
||||
MochiKit = {};
|
||||
}
|
||||
|
||||
if (typeof(MochiKit.Format) == 'undefined') {
|
||||
MochiKit.Format = {};
|
||||
}
|
||||
|
||||
MochiKit.Format.NAME = "MochiKit.Format";
|
||||
MochiKit.Format.VERSION = "1.4";
|
||||
MochiKit.Format.__repr__ = function () {
|
||||
return "[" + this.NAME + " " + this.VERSION + "]";
|
||||
};
|
||||
MochiKit.Format.toString = function () {
|
||||
return this.__repr__();
|
||||
};
|
||||
|
||||
MochiKit.Format._numberFormatter = function (placeholder, header, footer, locale, isPercent, precision, leadingZeros, separatorAt, trailingZeros) {
|
||||
return function (num) {
|
||||
num = parseFloat(num);
|
||||
if (typeof(num) == "undefined" || num === null || isNaN(num)) {
|
||||
return placeholder;
|
||||
}
|
||||
var curheader = header;
|
||||
var curfooter = footer;
|
||||
if (num < 0) {
|
||||
num = -num;
|
||||
} else {
|
||||
curheader = curheader.replace(/-/, "");
|
||||
}
|
||||
var me = arguments.callee;
|
||||
var fmt = MochiKit.Format.formatLocale(locale);
|
||||
if (isPercent) {
|
||||
num = num * 100.0;
|
||||
curfooter = fmt.percent + curfooter;
|
||||
}
|
||||
num = MochiKit.Format.roundToFixed(num, precision);
|
||||
var parts = num.split(/\./);
|
||||
var whole = parts[0];
|
||||
var frac = (parts.length == 1) ? "" : parts[1];
|
||||
var res = "";
|
||||
while (whole.length < leadingZeros) {
|
||||
whole = "0" + whole;
|
||||
}
|
||||
if (separatorAt) {
|
||||
while (whole.length > separatorAt) {
|
||||
var i = whole.length - separatorAt;
|
||||
//res = res + fmt.separator + whole.substring(i, whole.length);
|
||||
res = fmt.separator + whole.substring(i, whole.length) + res;
|
||||
whole = whole.substring(0, i);
|
||||
}
|
||||
}
|
||||
res = whole + res;
|
||||
if (precision > 0) {
|
||||
while (frac.length < trailingZeros) {
|
||||
frac = frac + "0";
|
||||
}
|
||||
res = res + fmt.decimal + frac;
|
||||
}
|
||||
return curheader + res + curfooter;
|
||||
};
|
||||
};
|
||||
|
||||
/** @id MochiKit.Format.numberFormatter */
|
||||
MochiKit.Format.numberFormatter = function (pattern, placeholder/* = "" */, locale/* = "default" */) {
|
||||
// http://java.sun.com/docs/books/tutorial/i18n/format/numberpattern.html
|
||||
// | 0 | leading or trailing zeros
|
||||
// | # | just the number
|
||||
// | , | separator
|
||||
// | . | decimal separator
|
||||
// | % | Multiply by 100 and format as percent
|
||||
if (typeof(placeholder) == "undefined") {
|
||||
placeholder = "";
|
||||
}
|
||||
var match = pattern.match(/((?:[0#]+,)?[0#]+)(?:\.([0#]+))?(%)?/);
|
||||
if (!match) {
|
||||
throw TypeError("Invalid pattern");
|
||||
}
|
||||
var header = pattern.substr(0, match.index);
|
||||
var footer = pattern.substr(match.index + match[0].length);
|
||||
if (header.search(/-/) == -1) {
|
||||
header = header + "-";
|
||||
}
|
||||
var whole = match[1];
|
||||
var frac = (typeof(match[2]) == "string" && match[2] != "") ? match[2] : "";
|
||||
var isPercent = (typeof(match[3]) == "string" && match[3] != "");
|
||||
var tmp = whole.split(/,/);
|
||||
var separatorAt;
|
||||
if (typeof(locale) == "undefined") {
|
||||
locale = "default";
|
||||
}
|
||||
if (tmp.length == 1) {
|
||||
separatorAt = null;
|
||||
} else {
|
||||
separatorAt = tmp[1].length;
|
||||
}
|
||||
var leadingZeros = whole.length - whole.replace(/0/g, "").length;
|
||||
var trailingZeros = frac.length - frac.replace(/0/g, "").length;
|
||||
var precision = frac.length;
|
||||
var rval = MochiKit.Format._numberFormatter(
|
||||
placeholder, header, footer, locale, isPercent, precision,
|
||||
leadingZeros, separatorAt, trailingZeros
|
||||
);
|
||||
var m = MochiKit.Base;
|
||||
if (m) {
|
||||
var fn = arguments.callee;
|
||||
var args = m.concat(arguments);
|
||||
rval.repr = function () {
|
||||
return [
|
||||
self.NAME,
|
||||
"(",
|
||||
map(m.repr, args).join(", "),
|
||||
")"
|
||||
].join("");
|
||||
};
|
||||
}
|
||||
return rval;
|
||||
};
|
||||
|
||||
/** @id MochiKit.Format.formatLocale */
|
||||
MochiKit.Format.formatLocale = function (locale) {
|
||||
if (typeof(locale) == "undefined" || locale === null) {
|
||||
locale = "default";
|
||||
}
|
||||
if (typeof(locale) == "string") {
|
||||
var rval = MochiKit.Format.LOCALE[locale];
|
||||
if (typeof(rval) == "string") {
|
||||
rval = arguments.callee(rval);
|
||||
MochiKit.Format.LOCALE[locale] = rval;
|
||||
}
|
||||
return rval;
|
||||
} else {
|
||||
return locale;
|
||||
}
|
||||
};
|
||||
|
||||
/** @id MochiKit.Format.twoDigitAverage */
|
||||
MochiKit.Format.twoDigitAverage = function (numerator, denominator) {
|
||||
if (denominator) {
|
||||
var res = numerator / denominator;
|
||||
if (!isNaN(res)) {
|
||||
return MochiKit.Format.twoDigitFloat(numerator / denominator);
|
||||
}
|
||||
}
|
||||
return "0";
|
||||
};
|
||||
|
||||
/** @id MochiKit.Format.twoDigitFloat */
|
||||
MochiKit.Format.twoDigitFloat = function (someFloat) {
|
||||
var sign = (someFloat < 0 ? '-' : '');
|
||||
var s = Math.floor(Math.abs(someFloat) * 100).toString();
|
||||
if (s == '0') {
|
||||
return s;
|
||||
}
|
||||
if (s.length < 3) {
|
||||
while (s.charAt(s.length - 1) == '0') {
|
||||
s = s.substring(0, s.length - 1);
|
||||
}
|
||||
return sign + '0.' + s;
|
||||
}
|
||||
var head = sign + s.substring(0, s.length - 2);
|
||||
var tail = s.substring(s.length - 2, s.length);
|
||||
if (tail == '00') {
|
||||
return head;
|
||||
} else if (tail.charAt(1) == '0') {
|
||||
return head + '.' + tail.charAt(0);
|
||||
} else {
|
||||
return head + '.' + tail;
|
||||
}
|
||||
};
|
||||
|
||||
/** @id MochiKit.Format.lstrip */
|
||||
MochiKit.Format.lstrip = function (str, /* optional */chars) {
|
||||
str = str + "";
|
||||
if (typeof(str) != "string") {
|
||||
return null;
|
||||
}
|
||||
if (!chars) {
|
||||
return str.replace(/^\s+/, "");
|
||||
} else {
|
||||
return str.replace(new RegExp("^[" + chars + "]+"), "");
|
||||
}
|
||||
};
|
||||
|
||||
/** @id MochiKit.Format.rstrip */
|
||||
MochiKit.Format.rstrip = function (str, /* optional */chars) {
|
||||
str = str + "";
|
||||
if (typeof(str) != "string") {
|
||||
return null;
|
||||
}
|
||||
if (!chars) {
|
||||
return str.replace(/\s+$/, "");
|
||||
} else {
|
||||
return str.replace(new RegExp("[" + chars + "]+$"), "");
|
||||
}
|
||||
};
|
||||
|
||||
/** @id MochiKit.Format.strip */
|
||||
MochiKit.Format.strip = function (str, /* optional */chars) {
|
||||
var self = MochiKit.Format;
|
||||
return self.rstrip(self.lstrip(str, chars), chars);
|
||||
};
|
||||
|
||||
/** @id MochiKit.Format.truncToFixed */
|
||||
MochiKit.Format.truncToFixed = function (aNumber, precision) {
|
||||
aNumber = Math.floor(aNumber * Math.pow(10, precision));
|
||||
var res = (aNumber * Math.pow(10, -precision)).toFixed(precision);
|
||||
if (res.charAt(0) == ".") {
|
||||
res = "0" + res;
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
/** @id MochiKit.Format.roundToFixed */
|
||||
MochiKit.Format.roundToFixed = function (aNumber, precision) {
|
||||
return MochiKit.Format.truncToFixed(
|
||||
aNumber + 0.5 * Math.pow(10, -precision),
|
||||
precision
|
||||
);
|
||||
};
|
||||
|
||||
/** @id MochiKit.Format.percentFormat */
|
||||
MochiKit.Format.percentFormat = function (someFloat) {
|
||||
return MochiKit.Format.twoDigitFloat(100 * someFloat) + '%';
|
||||
};
|
||||
|
||||
MochiKit.Format.EXPORT = [
|
||||
"truncToFixed",
|
||||
"roundToFixed",
|
||||
"numberFormatter",
|
||||
"formatLocale",
|
||||
"twoDigitAverage",
|
||||
"twoDigitFloat",
|
||||
"percentFormat",
|
||||
"lstrip",
|
||||
"rstrip",
|
||||
"strip"
|
||||
];
|
||||
|
||||
MochiKit.Format.LOCALE = {
|
||||
en_US: {separator: ",", decimal: ".", percent: "%"},
|
||||
de_DE: {separator: ".", decimal: ",", percent: "%"},
|
||||
fr_FR: {separator: " ", decimal: ",", percent: "%"},
|
||||
"default": "en_US"
|
||||
};
|
||||
|
||||
MochiKit.Format.EXPORT_OK = [];
|
||||
MochiKit.Format.EXPORT_TAGS = {
|
||||
':all': MochiKit.Format.EXPORT,
|
||||
':common': MochiKit.Format.EXPORT
|
||||
};
|
||||
|
||||
MochiKit.Format.__new__ = function () {
|
||||
// MochiKit.Base.nameFunctions(this);
|
||||
var base = this.NAME + ".";
|
||||
var k, v, o;
|
||||
for (k in this.LOCALE) {
|
||||
o = this.LOCALE[k];
|
||||
if (typeof(o) == "object") {
|
||||
o.repr = function () { return this.NAME; };
|
||||
o.NAME = base + "LOCALE." + k;
|
||||
}
|
||||
}
|
||||
for (k in this) {
|
||||
o = this[k];
|
||||
if (typeof(o) == 'function' && typeof(o.NAME) == 'undefined') {
|
||||
try {
|
||||
o.NAME = base + k;
|
||||
} catch (e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MochiKit.Format.__new__();
|
||||
|
||||
if (typeof(MochiKit.Base) != "undefined") {
|
||||
MochiKit.Base._exportSymbols(this, MochiKit.Format);
|
||||
} else {
|
||||
(function (globals, module) {
|
||||
if ((typeof(JSAN) == 'undefined' && typeof(dojo) == 'undefined')
|
||||
|| (MochiKit.__export__ === false)) {
|
||||
var all = module.EXPORT_TAGS[":all"];
|
||||
for (var i = 0; i < all.length; i++) {
|
||||
globals[all[i]] = module[all[i]];
|
||||
}
|
||||
}
|
||||
})(this, MochiKit.Format);
|
||||
}
|
|
@ -0,0 +1,843 @@
|
|||
/***
|
||||
|
||||
MochiKit.Iter 1.4
|
||||
|
||||
See <http://mochikit.com/> for documentation, downloads, license, etc.
|
||||
|
||||
(c) 2005 Bob Ippolito. All rights Reserved.
|
||||
|
||||
***/
|
||||
|
||||
if (typeof(dojo) != 'undefined') {
|
||||
dojo.provide('MochiKit.Iter');
|
||||
dojo.require('MochiKit.Base');
|
||||
}
|
||||
|
||||
if (typeof(JSAN) != 'undefined') {
|
||||
JSAN.use("MochiKit.Base", []);
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof(MochiKit.Base) == 'undefined') {
|
||||
throw "";
|
||||
}
|
||||
} catch (e) {
|
||||
throw "MochiKit.Iter depends on MochiKit.Base!";
|
||||
}
|
||||
|
||||
if (typeof(MochiKit.Iter) == 'undefined') {
|
||||
MochiKit.Iter = {};
|
||||
}
|
||||
|
||||
MochiKit.Iter.NAME = "MochiKit.Iter";
|
||||
MochiKit.Iter.VERSION = "1.4";
|
||||
MochiKit.Base.update(MochiKit.Iter, {
|
||||
__repr__: function () {
|
||||
return "[" + this.NAME + " " + this.VERSION + "]";
|
||||
},
|
||||
toString: function () {
|
||||
return this.__repr__();
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.registerIteratorFactory */
|
||||
registerIteratorFactory: function (name, check, iterfactory, /* optional */ override) {
|
||||
MochiKit.Iter.iteratorRegistry.register(name, check, iterfactory, override);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.iter */
|
||||
iter: function (iterable, /* optional */ sentinel) {
|
||||
var self = MochiKit.Iter;
|
||||
if (arguments.length == 2) {
|
||||
return self.takewhile(
|
||||
function (a) { return a != sentinel; },
|
||||
iterable
|
||||
);
|
||||
}
|
||||
if (typeof(iterable.next) == 'function') {
|
||||
return iterable;
|
||||
} else if (typeof(iterable.iter) == 'function') {
|
||||
return iterable.iter();
|
||||
/*
|
||||
} else if (typeof(iterable.__iterator__) == 'function') {
|
||||
//
|
||||
// XXX: We can't support JavaScript 1.7 __iterator__ directly
|
||||
// because of Object.prototype.__iterator__
|
||||
//
|
||||
return iterable.__iterator__();
|
||||
*/
|
||||
}
|
||||
|
||||
try {
|
||||
return self.iteratorRegistry.match(iterable);
|
||||
} catch (e) {
|
||||
var m = MochiKit.Base;
|
||||
if (e == m.NotFound) {
|
||||
e = new TypeError(typeof(iterable) + ": " + m.repr(iterable) + " is not iterable");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.count */
|
||||
count: function (n) {
|
||||
if (!n) {
|
||||
n = 0;
|
||||
}
|
||||
var m = MochiKit.Base;
|
||||
return {
|
||||
repr: function () { return "count(" + n + ")"; },
|
||||
toString: m.forwardCall("repr"),
|
||||
next: m.counter(n)
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.cycle */
|
||||
cycle: function (p) {
|
||||
var self = MochiKit.Iter;
|
||||
var m = MochiKit.Base;
|
||||
var lst = [];
|
||||
var iterator = self.iter(p);
|
||||
return {
|
||||
repr: function () { return "cycle(...)"; },
|
||||
toString: m.forwardCall("repr"),
|
||||
next: function () {
|
||||
try {
|
||||
var rval = iterator.next();
|
||||
lst.push(rval);
|
||||
return rval;
|
||||
} catch (e) {
|
||||
if (e != self.StopIteration) {
|
||||
throw e;
|
||||
}
|
||||
if (lst.length === 0) {
|
||||
this.next = function () {
|
||||
throw self.StopIteration;
|
||||
};
|
||||
} else {
|
||||
var i = -1;
|
||||
this.next = function () {
|
||||
i = (i + 1) % lst.length;
|
||||
return lst[i];
|
||||
};
|
||||
}
|
||||
return this.next();
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.repeat */
|
||||
repeat: function (elem, /* optional */n) {
|
||||
var m = MochiKit.Base;
|
||||
if (typeof(n) == 'undefined') {
|
||||
return {
|
||||
repr: function () {
|
||||
return "repeat(" + m.repr(elem) + ")";
|
||||
},
|
||||
toString: m.forwardCall("repr"),
|
||||
next: function () {
|
||||
return elem;
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
repr: function () {
|
||||
return "repeat(" + m.repr(elem) + ", " + n + ")";
|
||||
},
|
||||
toString: m.forwardCall("repr"),
|
||||
next: function () {
|
||||
if (n <= 0) {
|
||||
throw MochiKit.Iter.StopIteration;
|
||||
}
|
||||
n -= 1;
|
||||
return elem;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.next */
|
||||
next: function (iterator) {
|
||||
return iterator.next();
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.izip */
|
||||
izip: function (p, q/*, ...*/) {
|
||||
var m = MochiKit.Base;
|
||||
var self = MochiKit.Iter;
|
||||
var next = self.next;
|
||||
var iterables = m.map(self.iter, arguments);
|
||||
return {
|
||||
repr: function () { return "izip(...)"; },
|
||||
toString: m.forwardCall("repr"),
|
||||
next: function () { return m.map(next, iterables); }
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.ifilter */
|
||||
ifilter: function (pred, seq) {
|
||||
var m = MochiKit.Base;
|
||||
seq = MochiKit.Iter.iter(seq);
|
||||
if (pred === null) {
|
||||
pred = m.operator.truth;
|
||||
}
|
||||
return {
|
||||
repr: function () { return "ifilter(...)"; },
|
||||
toString: m.forwardCall("repr"),
|
||||
next: function () {
|
||||
while (true) {
|
||||
var rval = seq.next();
|
||||
if (pred(rval)) {
|
||||
return rval;
|
||||
}
|
||||
}
|
||||
// mozilla warnings aren't too bright
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.ifilterfalse */
|
||||
ifilterfalse: function (pred, seq) {
|
||||
var m = MochiKit.Base;
|
||||
seq = MochiKit.Iter.iter(seq);
|
||||
if (pred === null) {
|
||||
pred = m.operator.truth;
|
||||
}
|
||||
return {
|
||||
repr: function () { return "ifilterfalse(...)"; },
|
||||
toString: m.forwardCall("repr"),
|
||||
next: function () {
|
||||
while (true) {
|
||||
var rval = seq.next();
|
||||
if (!pred(rval)) {
|
||||
return rval;
|
||||
}
|
||||
}
|
||||
// mozilla warnings aren't too bright
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.islice */
|
||||
islice: function (seq/*, [start,] stop[, step] */) {
|
||||
var self = MochiKit.Iter;
|
||||
var m = MochiKit.Base;
|
||||
seq = self.iter(seq);
|
||||
var start = 0;
|
||||
var stop = 0;
|
||||
var step = 1;
|
||||
var i = -1;
|
||||
if (arguments.length == 2) {
|
||||
stop = arguments[1];
|
||||
} else if (arguments.length == 3) {
|
||||
start = arguments[1];
|
||||
stop = arguments[2];
|
||||
} else {
|
||||
start = arguments[1];
|
||||
stop = arguments[2];
|
||||
step = arguments[3];
|
||||
}
|
||||
return {
|
||||
repr: function () {
|
||||
return "islice(" + ["...", start, stop, step].join(", ") + ")";
|
||||
},
|
||||
toString: m.forwardCall("repr"),
|
||||
next: function () {
|
||||
var rval;
|
||||
while (i < start) {
|
||||
rval = seq.next();
|
||||
i++;
|
||||
}
|
||||
if (start >= stop) {
|
||||
throw self.StopIteration;
|
||||
}
|
||||
start += step;
|
||||
return rval;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.imap */
|
||||
imap: function (fun, p, q/*, ...*/) {
|
||||
var m = MochiKit.Base;
|
||||
var self = MochiKit.Iter;
|
||||
var iterables = m.map(self.iter, m.extend(null, arguments, 1));
|
||||
var map = m.map;
|
||||
var next = self.next;
|
||||
return {
|
||||
repr: function () { return "imap(...)"; },
|
||||
toString: m.forwardCall("repr"),
|
||||
next: function () {
|
||||
return fun.apply(this, map(next, iterables));
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.applymap */
|
||||
applymap: function (fun, seq, self) {
|
||||
seq = MochiKit.Iter.iter(seq);
|
||||
var m = MochiKit.Base;
|
||||
return {
|
||||
repr: function () { return "applymap(...)"; },
|
||||
toString: m.forwardCall("repr"),
|
||||
next: function () {
|
||||
return fun.apply(self, seq.next());
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.chain */
|
||||
chain: function (p, q/*, ...*/) {
|
||||
// dumb fast path
|
||||
var self = MochiKit.Iter;
|
||||
var m = MochiKit.Base;
|
||||
if (arguments.length == 1) {
|
||||
return self.iter(arguments[0]);
|
||||
}
|
||||
var argiter = m.map(self.iter, arguments);
|
||||
return {
|
||||
repr: function () { return "chain(...)"; },
|
||||
toString: m.forwardCall("repr"),
|
||||
next: function () {
|
||||
while (argiter.length > 1) {
|
||||
try {
|
||||
return argiter[0].next();
|
||||
} catch (e) {
|
||||
if (e != self.StopIteration) {
|
||||
throw e;
|
||||
}
|
||||
argiter.shift();
|
||||
}
|
||||
}
|
||||
if (argiter.length == 1) {
|
||||
// optimize last element
|
||||
var arg = argiter.shift();
|
||||
this.next = m.bind("next", arg);
|
||||
return this.next();
|
||||
}
|
||||
throw self.StopIteration;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.takewhile */
|
||||
takewhile: function (pred, seq) {
|
||||
var self = MochiKit.Iter;
|
||||
seq = self.iter(seq);
|
||||
return {
|
||||
repr: function () { return "takewhile(...)"; },
|
||||
toString: MochiKit.Base.forwardCall("repr"),
|
||||
next: function () {
|
||||
var rval = seq.next();
|
||||
if (!pred(rval)) {
|
||||
this.next = function () {
|
||||
throw self.StopIteration;
|
||||
};
|
||||
this.next();
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.dropwhile */
|
||||
dropwhile: function (pred, seq) {
|
||||
seq = MochiKit.Iter.iter(seq);
|
||||
var m = MochiKit.Base;
|
||||
var bind = m.bind;
|
||||
return {
|
||||
"repr": function () { return "dropwhile(...)"; },
|
||||
"toString": m.forwardCall("repr"),
|
||||
"next": function () {
|
||||
while (true) {
|
||||
var rval = seq.next();
|
||||
if (!pred(rval)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.next = bind("next", seq);
|
||||
return rval;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
_tee: function (ident, sync, iterable) {
|
||||
sync.pos[ident] = -1;
|
||||
var m = MochiKit.Base;
|
||||
var listMin = m.listMin;
|
||||
return {
|
||||
repr: function () { return "tee(" + ident + ", ...)"; },
|
||||
toString: m.forwardCall("repr"),
|
||||
next: function () {
|
||||
var rval;
|
||||
var i = sync.pos[ident];
|
||||
|
||||
if (i == sync.max) {
|
||||
rval = iterable.next();
|
||||
sync.deque.push(rval);
|
||||
sync.max += 1;
|
||||
sync.pos[ident] += 1;
|
||||
} else {
|
||||
rval = sync.deque[i - sync.min];
|
||||
sync.pos[ident] += 1;
|
||||
if (i == sync.min && listMin(sync.pos) != sync.min) {
|
||||
sync.min += 1;
|
||||
sync.deque.shift();
|
||||
}
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.tee */
|
||||
tee: function (iterable, n/* = 2 */) {
|
||||
var rval = [];
|
||||
var sync = {
|
||||
"pos": [],
|
||||
"deque": [],
|
||||
"max": -1,
|
||||
"min": -1
|
||||
};
|
||||
if (arguments.length == 1 || typeof(n) == "undefined" || n === null) {
|
||||
n = 2;
|
||||
}
|
||||
var self = MochiKit.Iter;
|
||||
iterable = self.iter(iterable);
|
||||
var _tee = self._tee;
|
||||
for (var i = 0; i < n; i++) {
|
||||
rval.push(_tee(i, sync, iterable));
|
||||
}
|
||||
return rval;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.list */
|
||||
list: function (iterable) {
|
||||
// Fast-path for Array and Array-like
|
||||
var m = MochiKit.Base;
|
||||
if (typeof(iterable.slice) == 'function') {
|
||||
return iterable.slice();
|
||||
} else if (m.isArrayLike(iterable)) {
|
||||
return m.concat(iterable);
|
||||
}
|
||||
|
||||
var self = MochiKit.Iter;
|
||||
iterable = self.iter(iterable);
|
||||
var rval = [];
|
||||
try {
|
||||
while (true) {
|
||||
rval.push(iterable.next());
|
||||
}
|
||||
} catch (e) {
|
||||
if (e != self.StopIteration) {
|
||||
throw e;
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
// mozilla warnings aren't too bright
|
||||
return undefined;
|
||||
},
|
||||
|
||||
|
||||
/** @id MochiKit.Iter.reduce */
|
||||
reduce: function (fn, iterable, /* optional */initial) {
|
||||
var i = 0;
|
||||
var x = initial;
|
||||
var self = MochiKit.Iter;
|
||||
iterable = self.iter(iterable);
|
||||
if (arguments.length < 3) {
|
||||
try {
|
||||
x = iterable.next();
|
||||
} catch (e) {
|
||||
if (e == self.StopIteration) {
|
||||
e = new TypeError("reduce() of empty sequence with no initial value");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
try {
|
||||
while (true) {
|
||||
x = fn(x, iterable.next());
|
||||
}
|
||||
} catch (e) {
|
||||
if (e != self.StopIteration) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return x;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.range */
|
||||
range: function (/* [start,] stop[, step] */) {
|
||||
var start = 0;
|
||||
var stop = 0;
|
||||
var step = 1;
|
||||
if (arguments.length == 1) {
|
||||
stop = arguments[0];
|
||||
} else if (arguments.length == 2) {
|
||||
start = arguments[0];
|
||||
stop = arguments[1];
|
||||
} else if (arguments.length == 3) {
|
||||
start = arguments[0];
|
||||
stop = arguments[1];
|
||||
step = arguments[2];
|
||||
} else {
|
||||
throw new TypeError("range() takes 1, 2, or 3 arguments!");
|
||||
}
|
||||
if (step === 0) {
|
||||
throw new TypeError("range() step must not be 0");
|
||||
}
|
||||
return {
|
||||
next: function () {
|
||||
if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
|
||||
throw MochiKit.Iter.StopIteration;
|
||||
}
|
||||
var rval = start;
|
||||
start += step;
|
||||
return rval;
|
||||
},
|
||||
repr: function () {
|
||||
return "range(" + [start, stop, step].join(", ") + ")";
|
||||
},
|
||||
toString: MochiKit.Base.forwardCall("repr")
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.sum */
|
||||
sum: function (iterable, start/* = 0 */) {
|
||||
if (typeof(start) == "undefined" || start === null) {
|
||||
start = 0;
|
||||
}
|
||||
var x = start;
|
||||
var self = MochiKit.Iter;
|
||||
iterable = self.iter(iterable);
|
||||
try {
|
||||
while (true) {
|
||||
x += iterable.next();
|
||||
}
|
||||
} catch (e) {
|
||||
if (e != self.StopIteration) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return x;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.exhaust */
|
||||
exhaust: function (iterable) {
|
||||
var self = MochiKit.Iter;
|
||||
iterable = self.iter(iterable);
|
||||
try {
|
||||
while (true) {
|
||||
iterable.next();
|
||||
}
|
||||
} catch (e) {
|
||||
if (e != self.StopIteration) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.forEach */
|
||||
forEach: function (iterable, func, /* optional */self) {
|
||||
var m = MochiKit.Base;
|
||||
if (arguments.length > 2) {
|
||||
func = m.bind(func, self);
|
||||
}
|
||||
// fast path for array
|
||||
if (m.isArrayLike(iterable)) {
|
||||
try {
|
||||
for (var i = 0; i < iterable.length; i++) {
|
||||
func(iterable[i]);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e != MochiKit.Iter.StopIteration) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self = MochiKit.Iter;
|
||||
self.exhaust(self.imap(func, iterable));
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.every */
|
||||
every: function (iterable, func) {
|
||||
var self = MochiKit.Iter;
|
||||
try {
|
||||
self.ifilterfalse(func, iterable).next();
|
||||
return false;
|
||||
} catch (e) {
|
||||
if (e != self.StopIteration) {
|
||||
throw e;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.sorted */
|
||||
sorted: function (iterable, /* optional */cmp) {
|
||||
var rval = MochiKit.Iter.list(iterable);
|
||||
if (arguments.length == 1) {
|
||||
cmp = MochiKit.Base.compare;
|
||||
}
|
||||
rval.sort(cmp);
|
||||
return rval;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.reversed */
|
||||
reversed: function (iterable) {
|
||||
var rval = MochiKit.Iter.list(iterable);
|
||||
rval.reverse();
|
||||
return rval;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.some */
|
||||
some: function (iterable, func) {
|
||||
var self = MochiKit.Iter;
|
||||
try {
|
||||
self.ifilter(func, iterable).next();
|
||||
return true;
|
||||
} catch (e) {
|
||||
if (e != self.StopIteration) {
|
||||
throw e;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.iextend */
|
||||
iextend: function (lst, iterable) {
|
||||
if (MochiKit.Base.isArrayLike(iterable)) {
|
||||
// fast-path for array-like
|
||||
for (var i = 0; i < iterable.length; i++) {
|
||||
lst.push(iterable[i]);
|
||||
}
|
||||
} else {
|
||||
var self = MochiKit.Iter;
|
||||
iterable = self.iter(iterable);
|
||||
try {
|
||||
while (true) {
|
||||
lst.push(iterable.next());
|
||||
}
|
||||
} catch (e) {
|
||||
if (e != self.StopIteration) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
return lst;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.groupby */
|
||||
groupby: function(iterable, /* optional */ keyfunc) {
|
||||
var m = MochiKit.Base;
|
||||
var self = MochiKit.Iter;
|
||||
if (arguments.length < 2) {
|
||||
keyfunc = m.operator.identity;
|
||||
}
|
||||
iterable = self.iter(iterable);
|
||||
|
||||
// shared
|
||||
var pk = undefined;
|
||||
var k = undefined;
|
||||
var v;
|
||||
|
||||
function fetch() {
|
||||
v = iterable.next();
|
||||
k = keyfunc(v);
|
||||
};
|
||||
|
||||
function eat() {
|
||||
var ret = v;
|
||||
v = undefined;
|
||||
return ret;
|
||||
};
|
||||
|
||||
var first = true;
|
||||
var compare = m.compare;
|
||||
return {
|
||||
repr: function () { return "groupby(...)"; },
|
||||
next: function() {
|
||||
// iterator-next
|
||||
|
||||
// iterate until meet next group
|
||||
while (compare(k, pk) === 0) {
|
||||
fetch();
|
||||
if (first) {
|
||||
first = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pk = k;
|
||||
return [k, {
|
||||
next: function() {
|
||||
// subiterator-next
|
||||
if (v == undefined) { // Is there something to eat?
|
||||
fetch();
|
||||
}
|
||||
if (compare(k, pk) !== 0) {
|
||||
throw self.StopIteration;
|
||||
}
|
||||
return eat();
|
||||
}
|
||||
}];
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.groupby_as_array */
|
||||
groupby_as_array: function (iterable, /* optional */ keyfunc) {
|
||||
var m = MochiKit.Base;
|
||||
var self = MochiKit.Iter;
|
||||
if (arguments.length < 2) {
|
||||
keyfunc = m.operator.identity;
|
||||
}
|
||||
|
||||
iterable = self.iter(iterable);
|
||||
var result = [];
|
||||
var first = true;
|
||||
var prev_key;
|
||||
var compare = m.compare;
|
||||
while (true) {
|
||||
try {
|
||||
var value = iterable.next();
|
||||
var key = keyfunc(value);
|
||||
} catch (e) {
|
||||
if (e == self.StopIteration) {
|
||||
break;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
if (first || compare(key, prev_key) !== 0) {
|
||||
var values = [];
|
||||
result.push([key, values]);
|
||||
}
|
||||
values.push(value);
|
||||
first = false;
|
||||
prev_key = key;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.arrayLikeIter */
|
||||
arrayLikeIter: function (iterable) {
|
||||
var i = 0;
|
||||
return {
|
||||
repr: function () { return "arrayLikeIter(...)"; },
|
||||
toString: MochiKit.Base.forwardCall("repr"),
|
||||
next: function () {
|
||||
if (i >= iterable.length) {
|
||||
throw MochiKit.Iter.StopIteration;
|
||||
}
|
||||
return iterable[i++];
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.hasIterateNext */
|
||||
hasIterateNext: function (iterable) {
|
||||
return (iterable && typeof(iterable.iterateNext) == "function");
|
||||
},
|
||||
|
||||
/** @id MochiKit.Iter.iterateNextIter */
|
||||
iterateNextIter: function (iterable) {
|
||||
return {
|
||||
repr: function () { return "iterateNextIter(...)"; },
|
||||
toString: MochiKit.Base.forwardCall("repr"),
|
||||
next: function () {
|
||||
var rval = iterable.iterateNext();
|
||||
if (rval === null || rval === undefined) {
|
||||
throw MochiKit.Iter.StopIteration;
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
MochiKit.Iter.EXPORT_OK = [
|
||||
"iteratorRegistry",
|
||||
"arrayLikeIter",
|
||||
"hasIterateNext",
|
||||
"iterateNextIter",
|
||||
];
|
||||
|
||||
MochiKit.Iter.EXPORT = [
|
||||
"StopIteration",
|
||||
"registerIteratorFactory",
|
||||
"iter",
|
||||
"count",
|
||||
"cycle",
|
||||
"repeat",
|
||||
"next",
|
||||
"izip",
|
||||
"ifilter",
|
||||
"ifilterfalse",
|
||||
"islice",
|
||||
"imap",
|
||||
"applymap",
|
||||
"chain",
|
||||
"takewhile",
|
||||
"dropwhile",
|
||||
"tee",
|
||||
"list",
|
||||
"reduce",
|
||||
"range",
|
||||
"sum",
|
||||
"exhaust",
|
||||
"forEach",
|
||||
"every",
|
||||
"sorted",
|
||||
"reversed",
|
||||
"some",
|
||||
"iextend",
|
||||
"groupby",
|
||||
"groupby_as_array"
|
||||
];
|
||||
|
||||
MochiKit.Iter.__new__ = function () {
|
||||
var m = MochiKit.Base;
|
||||
// Re-use StopIteration if exists (e.g. SpiderMonkey)
|
||||
if (typeof(StopIteration) != "undefined") {
|
||||
this.StopIteration = StopIteration;
|
||||
} else {
|
||||
/** @id MochiKit.Iter.StopIteration */
|
||||
this.StopIteration = new m.NamedError("StopIteration");
|
||||
}
|
||||
this.iteratorRegistry = new m.AdapterRegistry();
|
||||
// Register the iterator factory for arrays
|
||||
this.registerIteratorFactory(
|
||||
"arrayLike",
|
||||
m.isArrayLike,
|
||||
this.arrayLikeIter
|
||||
);
|
||||
|
||||
this.registerIteratorFactory(
|
||||
"iterateNext",
|
||||
this.hasIterateNext,
|
||||
this.iterateNextIter
|
||||
);
|
||||
|
||||
this.EXPORT_TAGS = {
|
||||
":common": this.EXPORT,
|
||||
":all": m.concat(this.EXPORT, this.EXPORT_OK)
|
||||
};
|
||||
|
||||
m.nameFunctions(this);
|
||||
|
||||
};
|
||||
|
||||
MochiKit.Iter.__new__();
|
||||
|
||||
//
|
||||
// XXX: Internet Explorer blows
|
||||
//
|
||||
if (MochiKit.__export__) {
|
||||
reduce = MochiKit.Iter.reduce;
|
||||
}
|
||||
|
||||
MochiKit.Base._exportSymbols(this, MochiKit.Iter);
|
|
@ -0,0 +1,69 @@
|
|||
MochiKit is dual-licensed software. It is available under the terms of the
|
||||
MIT License, or the Academic Free License version 2.1. The full text of
|
||||
each license is included below.
|
||||
|
||||
MIT License
|
||||
===========
|
||||
|
||||
Copyright (c) 2005 Bob Ippolito. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
Academic Free License v. 2.1
|
||||
============================
|
||||
|
||||
Copyright (c) 2005 Bob Ippolito. All rights reserved.
|
||||
|
||||
This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following notice immediately following the copyright notice for the Original Work:
|
||||
|
||||
Licensed under the Academic Free License version 2.1
|
||||
|
||||
1) Grant of Copyright License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license to do the following:
|
||||
|
||||
a) to reproduce the Original Work in copies;
|
||||
|
||||
b) to prepare derivative works ("Derivative Works") based upon the Original Work;
|
||||
|
||||
c) to distribute copies of the Original Work and Derivative Works to the public;
|
||||
|
||||
d) to perform the Original Work publicly; and
|
||||
|
||||
e) to display the Original Work publicly.
|
||||
|
||||
2) Grant of Patent License. Licensor hereby grants You a world-wide, royalty-free, non-exclusive, perpetual, sublicenseable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, to make, use, sell and offer for sale the Original Work and Derivative Works.
|
||||
|
||||
3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor hereby agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work, and by publishing the address of that information repository in a notice immediately following the copyright notice that applies to the Original Work.
|
||||
|
||||
4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior written permission of the Licensor. Nothing in this License shall be deemed to grant any rights to trademarks, copyrights, patents, trade secrets or any other intellectual property of Licensor except as expressly stated herein. No patent license is granted to make, use, sell or offer to sell embodiments of any patent claims other than the licensed claims defined in Section 2. No right is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under different terms from this License any Original Work that Licensor otherwise would have a right to license.
|
||||
|
||||
5) This section intentionally omitted.
|
||||
|
||||
6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
|
||||
|
||||
7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately proceeding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to Original Work is granted hereunder except under this disclaimer.
|
||||
|
||||
8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to any person for any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to liability for death or personal injury resulting from Licensor's negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You.
|
||||
|
||||
9) Acceptance and Termination. If You distribute copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. Nothing else but this License (or another written agreement between Licensor and You) grants You permission to create Derivative Works based upon the Original Work or to exercise any of the rights granted in Section 1 herein, and any attempt to do so except under the terms of this License (or another written agreement between Licensor and You) is expressly prohibited by U.S. copyright law, the equivalent laws of other countries, and by international treaty. Therefore, by exercising any of the rights granted to You in Section 1 herein, You indicate Your acceptance of this License and all of its terms and conditions.
|
||||
|
||||
10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
|
||||
|
||||
11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et seq., the equivalent laws of other countries, and international treaty. This section shall survive the termination of this License.
|
||||
|
||||
12) Attorneys Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
|
||||
|
||||
13) Miscellaneous. This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
|
||||
|
||||
14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
|
||||
|
||||
This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved. Permission is hereby granted to copy and distribute this license without modification. This license may not be modified without the express written permission of its copyright owner.
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,321 @@
|
|||
/***
|
||||
|
||||
MochiKit.Logging 1.4
|
||||
|
||||
See <http://mochikit.com/> for documentation, downloads, license, etc.
|
||||
|
||||
(c) 2005 Bob Ippolito. All rights Reserved.
|
||||
|
||||
***/
|
||||
|
||||
if (typeof(dojo) != 'undefined') {
|
||||
dojo.provide('MochiKit.Logging');
|
||||
dojo.require('MochiKit.Base');
|
||||
}
|
||||
|
||||
if (typeof(JSAN) != 'undefined') {
|
||||
JSAN.use("MochiKit.Base", []);
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof(MochiKit.Base) == 'undefined') {
|
||||
throw "";
|
||||
}
|
||||
} catch (e) {
|
||||
throw "MochiKit.Logging depends on MochiKit.Base!";
|
||||
}
|
||||
|
||||
if (typeof(MochiKit.Logging) == 'undefined') {
|
||||
MochiKit.Logging = {};
|
||||
}
|
||||
|
||||
MochiKit.Logging.NAME = "MochiKit.Logging";
|
||||
MochiKit.Logging.VERSION = "1.4";
|
||||
MochiKit.Logging.__repr__ = function () {
|
||||
return "[" + this.NAME + " " + this.VERSION + "]";
|
||||
};
|
||||
|
||||
MochiKit.Logging.toString = function () {
|
||||
return this.__repr__();
|
||||
};
|
||||
|
||||
|
||||
MochiKit.Logging.EXPORT = [
|
||||
"LogLevel",
|
||||
"LogMessage",
|
||||
"Logger",
|
||||
"alertListener",
|
||||
"logger",
|
||||
"log",
|
||||
"logError",
|
||||
"logDebug",
|
||||
"logFatal",
|
||||
"logWarning"
|
||||
];
|
||||
|
||||
|
||||
MochiKit.Logging.EXPORT_OK = [
|
||||
"logLevelAtLeast",
|
||||
"isLogMessage",
|
||||
"compareLogMessage"
|
||||
];
|
||||
|
||||
|
||||
/** @id MochiKit.Logging.LogMessage */
|
||||
MochiKit.Logging.LogMessage = function (num, level, info) {
|
||||
this.num = num;
|
||||
this.level = level;
|
||||
this.info = info;
|
||||
this.timestamp = new Date();
|
||||
};
|
||||
|
||||
MochiKit.Logging.LogMessage.prototype = {
|
||||
/** @id MochiKit.Logging.LogMessage.prototype.repr */
|
||||
repr: function () {
|
||||
var m = MochiKit.Base;
|
||||
return 'LogMessage(' +
|
||||
m.map(
|
||||
m.repr,
|
||||
[this.num, this.level, this.info]
|
||||
).join(', ') + ')';
|
||||
},
|
||||
/** @id MochiKit.Logging.LogMessage.prototype.toString */
|
||||
toString: MochiKit.Base.forwardCall("repr")
|
||||
};
|
||||
|
||||
MochiKit.Base.update(MochiKit.Logging, {
|
||||
/** @id MochiKit.Logging.logLevelAtLeast */
|
||||
logLevelAtLeast: function (minLevel) {
|
||||
var self = MochiKit.Logging;
|
||||
if (typeof(minLevel) == 'string') {
|
||||
minLevel = self.LogLevel[minLevel];
|
||||
}
|
||||
return function (msg) {
|
||||
var msgLevel = msg.level;
|
||||
if (typeof(msgLevel) == 'string') {
|
||||
msgLevel = self.LogLevel[msgLevel];
|
||||
}
|
||||
return msgLevel >= minLevel;
|
||||
};
|
||||
},
|
||||
|
||||
/** @id MochiKit.Logging.isLogMessage */
|
||||
isLogMessage: function (/* ... */) {
|
||||
var LogMessage = MochiKit.Logging.LogMessage;
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
if (!(arguments[i] instanceof LogMessage)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Logging.compareLogMessage */
|
||||
compareLogMessage: function (a, b) {
|
||||
return MochiKit.Base.compare([a.level, a.info], [b.level, b.info]);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Logging.alertListener */
|
||||
alertListener: function (msg) {
|
||||
alert(
|
||||
"num: " + msg.num +
|
||||
"\nlevel: " + msg.level +
|
||||
"\ninfo: " + msg.info.join(" ")
|
||||
);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/** @id MochiKit.Logging.Logger */
|
||||
MochiKit.Logging.Logger = function (/* optional */maxSize) {
|
||||
this.counter = 0;
|
||||
if (typeof(maxSize) == 'undefined' || maxSize === null) {
|
||||
maxSize = -1;
|
||||
}
|
||||
this.maxSize = maxSize;
|
||||
this._messages = [];
|
||||
this.listeners = {};
|
||||
this.useNativeConsole = false;
|
||||
};
|
||||
|
||||
MochiKit.Logging.Logger.prototype = {
|
||||
/** @id MochiKit.Logging.Logger.prototype.clear */
|
||||
clear: function () {
|
||||
this._messages.splice(0, this._messages.length);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Logging.Logger.prototype.logToConsole */
|
||||
logToConsole: function (msg) {
|
||||
if (typeof(window) != "undefined" && window.console
|
||||
&& window.console.log) {
|
||||
// Safari and FireBug 0.4
|
||||
// Percent replacement is a workaround for cute Safari crashing bug
|
||||
window.console.log(msg.replace(/%/g, '\uFF05'));
|
||||
} else if (typeof(opera) != "undefined" && opera.postError) {
|
||||
// Opera
|
||||
opera.postError(msg);
|
||||
} else if (typeof(printfire) == "function") {
|
||||
// FireBug 0.3 and earlier
|
||||
printfire(msg);
|
||||
} else if (typeof(Debug) != "undefined" && Debug.writeln) {
|
||||
// IE Web Development Helper (?)
|
||||
// http://www.nikhilk.net/Entry.aspx?id=93
|
||||
Debug.writeln(msg);
|
||||
} else if (typeof(debug) != "undefined" && debug.trace) {
|
||||
// Atlas framework (?)
|
||||
// http://www.nikhilk.net/Entry.aspx?id=93
|
||||
debug.trace(msg);
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Logging.Logger.prototype.dispatchListeners */
|
||||
dispatchListeners: function (msg) {
|
||||
for (var k in this.listeners) {
|
||||
var pair = this.listeners[k];
|
||||
if (pair.ident != k || (pair[0] && !pair[0](msg))) {
|
||||
continue;
|
||||
}
|
||||
pair[1](msg);
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Logging.Logger.prototype.addListener */
|
||||
addListener: function (ident, filter, listener) {
|
||||
if (typeof(filter) == 'string') {
|
||||
filter = MochiKit.Logging.logLevelAtLeast(filter);
|
||||
}
|
||||
var entry = [filter, listener];
|
||||
entry.ident = ident;
|
||||
this.listeners[ident] = entry;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Logging.Logger.prototype.removeListener */
|
||||
removeListener: function (ident) {
|
||||
delete this.listeners[ident];
|
||||
},
|
||||
|
||||
/** @id MochiKit.Logging.Logger.prototype.baseLog */
|
||||
baseLog: function (level, message/*, ...*/) {
|
||||
var msg = new MochiKit.Logging.LogMessage(
|
||||
this.counter,
|
||||
level,
|
||||
MochiKit.Base.extend(null, arguments, 1)
|
||||
);
|
||||
this._messages.push(msg);
|
||||
this.dispatchListeners(msg);
|
||||
if (this.useNativeConsole) {
|
||||
this.logToConsole(msg.level + ": " + msg.info.join(" "));
|
||||
}
|
||||
this.counter += 1;
|
||||
while (this.maxSize >= 0 && this._messages.length > this.maxSize) {
|
||||
this._messages.shift();
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Logging.Logger.prototype.getMessages */
|
||||
getMessages: function (howMany) {
|
||||
var firstMsg = 0;
|
||||
if (!(typeof(howMany) == 'undefined' || howMany === null)) {
|
||||
firstMsg = Math.max(0, this._messages.length - howMany);
|
||||
}
|
||||
return this._messages.slice(firstMsg);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Logging.Logger.prototype.getMessageText */
|
||||
getMessageText: function (howMany) {
|
||||
if (typeof(howMany) == 'undefined' || howMany === null) {
|
||||
howMany = 30;
|
||||
}
|
||||
var messages = this.getMessages(howMany);
|
||||
if (messages.length) {
|
||||
var lst = map(function (m) {
|
||||
return '\n [' + m.num + '] ' + m.level + ': ' + m.info.join(' ');
|
||||
}, messages);
|
||||
lst.unshift('LAST ' + messages.length + ' MESSAGES:');
|
||||
return lst.join('');
|
||||
}
|
||||
return '';
|
||||
},
|
||||
|
||||
/** @id MochiKit.Logging.Logger.prototype.debuggingBookmarklet */
|
||||
debuggingBookmarklet: function (inline) {
|
||||
if (typeof(MochiKit.LoggingPane) == "undefined") {
|
||||
alert(this.getMessageText());
|
||||
} else {
|
||||
MochiKit.LoggingPane.createLoggingPane(inline || false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MochiKit.Logging.__new__ = function () {
|
||||
this.LogLevel = {
|
||||
ERROR: 40,
|
||||
FATAL: 50,
|
||||
WARNING: 30,
|
||||
INFO: 20,
|
||||
DEBUG: 10
|
||||
};
|
||||
|
||||
var m = MochiKit.Base;
|
||||
m.registerComparator("LogMessage",
|
||||
this.isLogMessage,
|
||||
this.compareLogMessage
|
||||
);
|
||||
|
||||
var partial = m.partial;
|
||||
|
||||
var Logger = this.Logger;
|
||||
var baseLog = Logger.prototype.baseLog;
|
||||
m.update(this.Logger.prototype, {
|
||||
debug: partial(baseLog, 'DEBUG'),
|
||||
log: partial(baseLog, 'INFO'),
|
||||
error: partial(baseLog, 'ERROR'),
|
||||
fatal: partial(baseLog, 'FATAL'),
|
||||
warning: partial(baseLog, 'WARNING')
|
||||
});
|
||||
|
||||
// indirectly find logger so it can be replaced
|
||||
var self = this;
|
||||
var connectLog = function (name) {
|
||||
return function () {
|
||||
self.logger[name].apply(self.logger, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
/** @id MochiKit.Logging.log */
|
||||
this.log = connectLog('log');
|
||||
/** @id MochiKit.Logging.logError */
|
||||
this.logError = connectLog('error');
|
||||
/** @id MochiKit.Logging.logDebug */
|
||||
this.logDebug = connectLog('debug');
|
||||
/** @id MochiKit.Logging.logFatal */
|
||||
this.logFatal = connectLog('fatal');
|
||||
/** @id MochiKit.Logging.logWarning */
|
||||
this.logWarning = connectLog('warning');
|
||||
this.logger = new Logger();
|
||||
this.logger.useNativeConsole = true;
|
||||
|
||||
this.EXPORT_TAGS = {
|
||||
":common": this.EXPORT,
|
||||
":all": m.concat(this.EXPORT, this.EXPORT_OK)
|
||||
};
|
||||
|
||||
m.nameFunctions(this);
|
||||
|
||||
};
|
||||
|
||||
if (typeof(printfire) == "undefined" &&
|
||||
typeof(document) != "undefined" && document.createEvent &&
|
||||
typeof(dispatchEvent) != "undefined") {
|
||||
// FireBug really should be less lame about this global function
|
||||
printfire = function () {
|
||||
printfire.args = arguments;
|
||||
var ev = document.createEvent("Events");
|
||||
ev.initEvent("printfire", false, true);
|
||||
dispatchEvent(ev);
|
||||
};
|
||||
}
|
||||
|
||||
MochiKit.Logging.__new__();
|
||||
|
||||
MochiKit.Base._exportSymbols(this, MochiKit.Logging);
|
|
@ -0,0 +1,371 @@
|
|||
/***
|
||||
|
||||
MochiKit.LoggingPane 1.4
|
||||
|
||||
See <http://mochikit.com/> for documentation, downloads, license, etc.
|
||||
|
||||
(c) 2005 Bob Ippolito. All rights Reserved.
|
||||
|
||||
***/
|
||||
|
||||
if (typeof(dojo) != 'undefined') {
|
||||
dojo.provide('MochiKit.LoggingPane');
|
||||
dojo.require('MochiKit.Logging');
|
||||
dojo.require('MochiKit.Base');
|
||||
}
|
||||
|
||||
if (typeof(JSAN) != 'undefined') {
|
||||
JSAN.use("MochiKit.Logging", []);
|
||||
JSAN.use("MochiKit.Base", []);
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof(MochiKit.Base) == 'undefined' || typeof(MochiKit.Logging) == 'undefined') {
|
||||
throw "";
|
||||
}
|
||||
} catch (e) {
|
||||
throw "MochiKit.LoggingPane depends on MochiKit.Base and MochiKit.Logging!";
|
||||
}
|
||||
|
||||
if (typeof(MochiKit.LoggingPane) == 'undefined') {
|
||||
MochiKit.LoggingPane = {};
|
||||
}
|
||||
|
||||
MochiKit.LoggingPane.NAME = "MochiKit.LoggingPane";
|
||||
MochiKit.LoggingPane.VERSION = "1.4";
|
||||
MochiKit.LoggingPane.__repr__ = function () {
|
||||
return "[" + this.NAME + " " + this.VERSION + "]";
|
||||
};
|
||||
|
||||
MochiKit.LoggingPane.toString = function () {
|
||||
return this.__repr__();
|
||||
};
|
||||
|
||||
/** @id MochiKit.LoggingPane.createLoggingPane */
|
||||
MochiKit.LoggingPane.createLoggingPane = function (inline/* = false */) {
|
||||
var m = MochiKit.LoggingPane;
|
||||
inline = !(!inline);
|
||||
if (m._loggingPane && m._loggingPane.inline != inline) {
|
||||
m._loggingPane.closePane();
|
||||
m._loggingPane = null;
|
||||
}
|
||||
if (!m._loggingPane || m._loggingPane.closed) {
|
||||
m._loggingPane = new m.LoggingPane(inline, MochiKit.Logging.logger);
|
||||
}
|
||||
return m._loggingPane;
|
||||
};
|
||||
|
||||
/** @id MochiKit.LoggingPane.LoggingPane */
|
||||
MochiKit.LoggingPane.LoggingPane = function (inline/* = false */, logger/* = MochiKit.Logging.logger */) {
|
||||
|
||||
/* Use a div if inline, pop up a window if not */
|
||||
/* Create the elements */
|
||||
if (typeof(logger) == "undefined" || logger === null) {
|
||||
logger = MochiKit.Logging.logger;
|
||||
}
|
||||
this.logger = logger;
|
||||
var update = MochiKit.Base.update;
|
||||
var updatetree = MochiKit.Base.updatetree;
|
||||
var bind = MochiKit.Base.bind;
|
||||
var clone = MochiKit.Base.clone;
|
||||
var win = window;
|
||||
var uid = "_MochiKit_LoggingPane";
|
||||
if (typeof(MochiKit.DOM) != "undefined") {
|
||||
win = MochiKit.DOM.currentWindow();
|
||||
}
|
||||
if (!inline) {
|
||||
// name the popup with the base URL for uniqueness
|
||||
var url = win.location.href.split("?")[0].replace(/[#:\/.><&-]/g, "_");
|
||||
var name = uid + "_" + url;
|
||||
var nwin = win.open("", name, "dependent,resizable,height=200");
|
||||
if (!nwin) {
|
||||
alert("Not able to open debugging window due to pop-up blocking.");
|
||||
return undefined;
|
||||
}
|
||||
nwin.document.write(
|
||||
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" '
|
||||
+ '"http://www.w3.org/TR/html4/loose.dtd">'
|
||||
+ '<html><head><title>[MochiKit.LoggingPane]</title></head>'
|
||||
+ '<body></body></html>'
|
||||
);
|
||||
nwin.document.close();
|
||||
nwin.document.title += ' ' + win.document.title;
|
||||
win = nwin;
|
||||
}
|
||||
var doc = win.document;
|
||||
this.doc = doc;
|
||||
|
||||
// Connect to the debug pane if it already exists (i.e. in a window orphaned by the page being refreshed)
|
||||
var debugPane = doc.getElementById(uid);
|
||||
var existing_pane = !!debugPane;
|
||||
if (debugPane && typeof(debugPane.loggingPane) != "undefined") {
|
||||
debugPane.loggingPane.logger = this.logger;
|
||||
debugPane.loggingPane.buildAndApplyFilter();
|
||||
return debugPane.loggingPane;
|
||||
}
|
||||
|
||||
if (existing_pane) {
|
||||
// clear any existing contents
|
||||
var child;
|
||||
while ((child = debugPane.firstChild)) {
|
||||
debugPane.removeChild(child);
|
||||
}
|
||||
} else {
|
||||
debugPane = doc.createElement("div");
|
||||
debugPane.id = uid;
|
||||
}
|
||||
debugPane.loggingPane = this;
|
||||
var levelFilterField = doc.createElement("input");
|
||||
var infoFilterField = doc.createElement("input");
|
||||
var filterButton = doc.createElement("button");
|
||||
var loadButton = doc.createElement("button");
|
||||
var clearButton = doc.createElement("button");
|
||||
var closeButton = doc.createElement("button");
|
||||
var logPaneArea = doc.createElement("div");
|
||||
var logPane = doc.createElement("div");
|
||||
|
||||
/* Set up the functions */
|
||||
var listenerId = uid + "_Listener";
|
||||
this.colorTable = clone(this.colorTable);
|
||||
var messages = [];
|
||||
var messageFilter = null;
|
||||
|
||||
/** @id MochiKit.LoggingPane.messageLevel */
|
||||
var messageLevel = function (msg) {
|
||||
var level = msg.level;
|
||||
if (typeof(level) == "number") {
|
||||
level = MochiKit.Logging.LogLevel[level];
|
||||
}
|
||||
return level;
|
||||
};
|
||||
|
||||
/** @id MochiKit.LoggingPane.messageText */
|
||||
var messageText = function (msg) {
|
||||
return msg.info.join(" ");
|
||||
};
|
||||
|
||||
/** @id MochiKit.LoggingPane.addMessageText */
|
||||
var addMessageText = bind(function (msg) {
|
||||
var level = messageLevel(msg);
|
||||
var text = messageText(msg);
|
||||
var c = this.colorTable[level];
|
||||
var p = doc.createElement("span");
|
||||
p.className = "MochiKit-LogMessage MochiKit-LogLevel-" + level;
|
||||
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;
|
||||
p.appendChild(doc.createTextNode(level + ": " + text));
|
||||
logPane.appendChild(p);
|
||||
logPane.appendChild(doc.createElement("br"));
|
||||
if (logPaneArea.offsetHeight > logPaneArea.scrollHeight) {
|
||||
logPaneArea.scrollTop = 0;
|
||||
} else {
|
||||
logPaneArea.scrollTop = logPaneArea.scrollHeight;
|
||||
}
|
||||
}, this);
|
||||
|
||||
/** @id MochiKit.LoggingPane.addMessage */
|
||||
var addMessage = function (msg) {
|
||||
messages[messages.length] = msg;
|
||||
addMessageText(msg);
|
||||
};
|
||||
|
||||
/** @id MochiKit.LoggingPane.buildMessageFilter */
|
||||
var buildMessageFilter = function () {
|
||||
var levelre, infore;
|
||||
try {
|
||||
/* Catch any exceptions that might arise due to invalid regexes */
|
||||
levelre = new RegExp(levelFilterField.value);
|
||||
infore = new RegExp(infoFilterField.value);
|
||||
} catch(e) {
|
||||
/* If there was an error with the regexes, do no filtering */
|
||||
logDebug("Error in filter regex: " + e.message);
|
||||
return null;
|
||||
}
|
||||
|
||||
return function (msg) {
|
||||
return (
|
||||
levelre.test(messageLevel(msg)) &&
|
||||
infore.test(messageText(msg))
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
/** @id MochiKit.LoggingPane.clearMessagePane */
|
||||
var clearMessagePane = function () {
|
||||
while (logPane.firstChild) {
|
||||
logPane.removeChild(logPane.firstChild);
|
||||
}
|
||||
};
|
||||
|
||||
/** @id MochiKit.LoggingPane.clearMessages */
|
||||
var clearMessages = function () {
|
||||
messages = [];
|
||||
clearMessagePane();
|
||||
};
|
||||
|
||||
/** @id MochiKit.LoggingPane.closePane */
|
||||
var closePane = bind(function () {
|
||||
if (this.closed) {
|
||||
return;
|
||||
}
|
||||
this.closed = true;
|
||||
if (MochiKit.LoggingPane._loggingPane == this) {
|
||||
MochiKit.LoggingPane._loggingPane = null;
|
||||
}
|
||||
this.logger.removeListener(listenerId);
|
||||
|
||||
debugPane.loggingPane = null;
|
||||
|
||||
if (inline) {
|
||||
debugPane.parentNode.removeChild(debugPane);
|
||||
} else {
|
||||
this.win.close();
|
||||
}
|
||||
}, this);
|
||||
|
||||
/** @id MochiKit.LoggingPane.filterMessages */
|
||||
var filterMessages = function () {
|
||||
clearMessagePane();
|
||||
|
||||
for (var i = 0; i < messages.length; i++) {
|
||||
var msg = messages[i];
|
||||
if (messageFilter === null || messageFilter(msg)) {
|
||||
addMessageText(msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.buildAndApplyFilter = function () {
|
||||
messageFilter = buildMessageFilter();
|
||||
|
||||
filterMessages();
|
||||
|
||||
this.logger.removeListener(listenerId);
|
||||
this.logger.addListener(listenerId, messageFilter, addMessage);
|
||||
};
|
||||
|
||||
|
||||
/** @id MochiKit.LoggingPane.loadMessages */
|
||||
var loadMessages = bind(function () {
|
||||
messages = this.logger.getMessages();
|
||||
filterMessages();
|
||||
}, this);
|
||||
|
||||
/** @id MochiKit.LoggingPane.filterOnEnter */
|
||||
var filterOnEnter = bind(function (event) {
|
||||
event = event || window.event;
|
||||
key = event.which || event.keyCode;
|
||||
if (key == 13) {
|
||||
this.buildAndApplyFilter();
|
||||
}
|
||||
}, this);
|
||||
|
||||
/* Create the debug pane */
|
||||
var style = "display: block; z-index: 1000; left: 0px; bottom: 0px; position: fixed; width: 100%; background-color: white; font: " + this.logFont;
|
||||
if (inline) {
|
||||
style += "; height: 10em; border-top: 2px solid black";
|
||||
} else {
|
||||
style += "; height: 100%;";
|
||||
}
|
||||
debugPane.style.cssText = style;
|
||||
|
||||
if (!existing_pane) {
|
||||
doc.body.appendChild(debugPane);
|
||||
}
|
||||
|
||||
/* Create the filter fields */
|
||||
style = {"cssText": "width: 33%; display: inline; font: " + this.logFont};
|
||||
|
||||
updatetree(levelFilterField, {
|
||||
"value": "FATAL|ERROR|WARNING|INFO|DEBUG",
|
||||
"onkeypress": filterOnEnter,
|
||||
"style": style
|
||||
});
|
||||
debugPane.appendChild(levelFilterField);
|
||||
|
||||
updatetree(infoFilterField, {
|
||||
"value": ".*",
|
||||
"onkeypress": filterOnEnter,
|
||||
"style": style
|
||||
});
|
||||
debugPane.appendChild(infoFilterField);
|
||||
|
||||
/* Create the buttons */
|
||||
style = "width: 8%; display:inline; font: " + this.logFont;
|
||||
|
||||
filterButton.appendChild(doc.createTextNode("Filter"));
|
||||
filterButton.onclick = bind("buildAndApplyFilter", this);
|
||||
filterButton.style.cssText = style;
|
||||
debugPane.appendChild(filterButton);
|
||||
|
||||
loadButton.appendChild(doc.createTextNode("Load"));
|
||||
loadButton.onclick = loadMessages;
|
||||
loadButton.style.cssText = style;
|
||||
debugPane.appendChild(loadButton);
|
||||
|
||||
clearButton.appendChild(doc.createTextNode("Clear"));
|
||||
clearButton.onclick = clearMessages;
|
||||
clearButton.style.cssText = style;
|
||||
debugPane.appendChild(clearButton);
|
||||
|
||||
closeButton.appendChild(doc.createTextNode("Close"));
|
||||
closeButton.onclick = closePane;
|
||||
closeButton.style.cssText = style;
|
||||
debugPane.appendChild(closeButton);
|
||||
|
||||
/* Create the logging pane */
|
||||
logPaneArea.style.cssText = "overflow: auto; width: 100%";
|
||||
logPane.style.cssText = "width: 100%; height: " + (inline ? "8em" : "100%");
|
||||
|
||||
logPaneArea.appendChild(logPane);
|
||||
debugPane.appendChild(logPaneArea);
|
||||
|
||||
this.buildAndApplyFilter();
|
||||
loadMessages();
|
||||
|
||||
if (inline) {
|
||||
this.win = undefined;
|
||||
} else {
|
||||
this.win = win;
|
||||
}
|
||||
this.inline = inline;
|
||||
this.closePane = closePane;
|
||||
this.closed = false;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
MochiKit.LoggingPane.LoggingPane.prototype = {
|
||||
"logFont": "8pt Verdana,sans-serif",
|
||||
"colorTable": {
|
||||
"ERROR": "red",
|
||||
"FATAL": "darkred",
|
||||
"WARNING": "blue",
|
||||
"INFO": "black",
|
||||
"DEBUG": "green"
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
MochiKit.LoggingPane.EXPORT_OK = [
|
||||
"LoggingPane"
|
||||
];
|
||||
|
||||
MochiKit.LoggingPane.EXPORT = [
|
||||
"createLoggingPane"
|
||||
];
|
||||
|
||||
MochiKit.LoggingPane.__new__ = function () {
|
||||
this.EXPORT_TAGS = {
|
||||
":common": this.EXPORT,
|
||||
":all": MochiKit.Base.concat(this.EXPORT, this.EXPORT_OK)
|
||||
};
|
||||
|
||||
MochiKit.Base.nameFunctions(this);
|
||||
|
||||
MochiKit.LoggingPane._loggingPane = null;
|
||||
|
||||
};
|
||||
|
||||
MochiKit.LoggingPane.__new__();
|
||||
|
||||
MochiKit.Base._exportSymbols(this, MochiKit.LoggingPane);
|
|
@ -0,0 +1,71 @@
|
|||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Netscape Communications Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 1998
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = testing/mochitest/MochiKit
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_JS_FILES = Async.js \
|
||||
Base.js \
|
||||
Color.js \
|
||||
Controls.js \
|
||||
DateTime.js \
|
||||
DOM.js \
|
||||
DragAndDrop.js \
|
||||
Format.js \
|
||||
Iter.js \
|
||||
Logging.js \
|
||||
LoggingPane.js \
|
||||
MochiKit.js \
|
||||
MockDOM.js \
|
||||
New.js \
|
||||
__package__.js \
|
||||
packed.js \
|
||||
Signal.js \
|
||||
Sortable.js \
|
||||
Style.js \
|
||||
Test.js \
|
||||
Visual.js \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_JS_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/$(relativesrcdir)
|
152
com.ibm.wala.cast.js.test/examples-src/pages/mochitest/MochiKit/MochiKit.js
vendored
Normal file
152
com.ibm.wala.cast.js.test/examples-src/pages/mochitest/MochiKit/MochiKit.js
vendored
Normal file
|
@ -0,0 +1,152 @@
|
|||
/***
|
||||
|
||||
MochiKit.MochiKit 1.4
|
||||
|
||||
See <http://mochikit.com/> for documentation, downloads, license, etc.
|
||||
|
||||
(c) 2005 Bob Ippolito. All rights Reserved.
|
||||
|
||||
***/
|
||||
|
||||
if (typeof(MochiKit) == 'undefined') {
|
||||
MochiKit = {};
|
||||
}
|
||||
|
||||
if (typeof(MochiKit.MochiKit) == 'undefined') {
|
||||
/** @id MochiKit.MochiKit */
|
||||
MochiKit.MochiKit = {};
|
||||
}
|
||||
|
||||
MochiKit.MochiKit.NAME = "MochiKit.MochiKit";
|
||||
MochiKit.MochiKit.VERSION = "1.4";
|
||||
MochiKit.MochiKit.__repr__ = function () {
|
||||
return "[" + this.NAME + " " + this.VERSION + "]";
|
||||
};
|
||||
|
||||
/** @id MochiKit.MochiKit.toString */
|
||||
MochiKit.MochiKit.toString = function () {
|
||||
return this.__repr__();
|
||||
};
|
||||
|
||||
/** @id MochiKit.MochiKit.SUBMODULES */
|
||||
MochiKit.MochiKit.SUBMODULES = [
|
||||
"Base",
|
||||
"Iter",
|
||||
"Logging",
|
||||
"DateTime",
|
||||
"Format",
|
||||
"Async",
|
||||
"DOM",
|
||||
"Style",
|
||||
"LoggingPane",
|
||||
"Color",
|
||||
"Signal",
|
||||
"Visual"
|
||||
];
|
||||
|
||||
if (typeof(JSAN) != 'undefined' || typeof(dojo) != 'undefined') {
|
||||
if (typeof(dojo) != 'undefined') {
|
||||
dojo.provide('MochiKit.MochiKit');
|
||||
dojo.require("MochiKit.*");
|
||||
}
|
||||
if (typeof(JSAN) != 'undefined') {
|
||||
(function (lst) {
|
||||
for (var i = 0; i < lst.length; i++) {
|
||||
JSAN.use("MochiKit." + lst[i], []);
|
||||
}
|
||||
})(MochiKit.MochiKit.SUBMODULES);
|
||||
}
|
||||
(function () {
|
||||
var extend = MochiKit.Base.extend;
|
||||
var self = MochiKit.MochiKit;
|
||||
var modules = self.SUBMODULES;
|
||||
var EXPORT = [];
|
||||
var EXPORT_OK = [];
|
||||
var EXPORT_TAGS = {};
|
||||
var i, k, m, all;
|
||||
for (i = 0; i < modules.length; i++) {
|
||||
m = MochiKit[modules[i]];
|
||||
extend(EXPORT, m.EXPORT);
|
||||
extend(EXPORT_OK, m.EXPORT_OK);
|
||||
for (k in m.EXPORT_TAGS) {
|
||||
EXPORT_TAGS[k] = extend(EXPORT_TAGS[k], m.EXPORT_TAGS[k]);
|
||||
}
|
||||
all = m.EXPORT_TAGS[":all"];
|
||||
if (!all) {
|
||||
all = extend(null, m.EXPORT, m.EXPORT_OK);
|
||||
}
|
||||
var j;
|
||||
for (j = 0; j < all.length; j++) {
|
||||
k = all[j];
|
||||
self[k] = m[k];
|
||||
}
|
||||
}
|
||||
self.EXPORT = EXPORT;
|
||||
self.EXPORT_OK = EXPORT_OK;
|
||||
self.EXPORT_TAGS = EXPORT_TAGS;
|
||||
}());
|
||||
|
||||
} else {
|
||||
if (typeof(MochiKit.__compat__) == 'undefined') {
|
||||
MochiKit.__compat__ = true;
|
||||
}
|
||||
(function () {
|
||||
if (typeof(document) == "undefined") {
|
||||
return;
|
||||
}
|
||||
var scripts = document.getElementsByTagName("script");
|
||||
var kXULNSURI = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
var base = null;
|
||||
var baseElem = null;
|
||||
var allScripts = {};
|
||||
var i;
|
||||
for (i = 0; i < scripts.length; i++) {
|
||||
var src = scripts[i].getAttribute("src");
|
||||
if (!src) {
|
||||
continue;
|
||||
}
|
||||
allScripts[src] = true;
|
||||
if (src.match(/MochiKit.js$/)) {
|
||||
base = src.substring(0, src.lastIndexOf('MochiKit.js'));
|
||||
baseElem = scripts[i];
|
||||
}
|
||||
}
|
||||
if (base === null) {
|
||||
return;
|
||||
}
|
||||
var modules = MochiKit.MochiKit.SUBMODULES;
|
||||
for (var i = 0; i < modules.length; i++) {
|
||||
if (MochiKit[modules[i]]) {
|
||||
continue;
|
||||
}
|
||||
var uri = base + modules[i] + '.js';
|
||||
if (uri in allScripts) {
|
||||
continue;
|
||||
}
|
||||
if (document.documentElement &&
|
||||
document.documentElement.namespaceURI == kXULNSURI) {
|
||||
// XUL
|
||||
var s = document.createElementNS(kXULNSURI, 'script');
|
||||
s.setAttribute("id", "MochiKit_" + base + modules[i]);
|
||||
s.setAttribute("src", uri);
|
||||
s.setAttribute("type", "application/x-javascript");
|
||||
baseElem.parentNode.appendChild(s);
|
||||
} else {
|
||||
// HTML
|
||||
/*
|
||||
DOM can not be used here because Safari does
|
||||
deferred loading of scripts unless they are
|
||||
in the document or inserted with document.write
|
||||
|
||||
This is not XHTML compliant. If you want XHTML
|
||||
compliance then you must use the packed version of MochiKit
|
||||
or include each script individually (basically unroll
|
||||
these document.write calls into your XHTML source)
|
||||
|
||||
*/
|
||||
document.write('<script src="' + uri +
|
||||
'" type="text/javascript"></script>');
|
||||
}
|
||||
};
|
||||
})();
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/***
|
||||
|
||||
MochiKit.MockDOM 1.4
|
||||
|
||||
See <http://mochikit.com/> for documentation, downloads, license, etc.
|
||||
|
||||
(c) 2005 Bob Ippolito. All rights Reserved.
|
||||
|
||||
***/
|
||||
|
||||
if (typeof(MochiKit) == "undefined") {
|
||||
MochiKit = {};
|
||||
}
|
||||
|
||||
if (typeof(MochiKit.MockDOM) == "undefined") {
|
||||
MochiKit.MockDOM = {};
|
||||
}
|
||||
|
||||
MochiKit.MockDOM.NAME = "MochiKit.MockDOM";
|
||||
MochiKit.MockDOM.VERSION = "1.4";
|
||||
|
||||
MochiKit.MockDOM.__repr__ = function () {
|
||||
return "[" + this.NAME + " " + this.VERSION + "]";
|
||||
};
|
||||
|
||||
/** @id MochiKit.MockDOM.toString */
|
||||
MochiKit.MockDOM.toString = function () {
|
||||
return this.__repr__();
|
||||
};
|
||||
|
||||
/** @id MochiKit.MockDOM.createDocument */
|
||||
MochiKit.MockDOM.createDocument = function () {
|
||||
var doc = new MochiKit.MockDOM.MockElement("DOCUMENT");
|
||||
doc.body = doc.createElement("BODY");
|
||||
doc.appendChild(doc.body);
|
||||
return doc;
|
||||
};
|
||||
|
||||
/** @id MochiKit.MockDOM.MockElement */
|
||||
MochiKit.MockDOM.MockElement = function (name, data) {
|
||||
this.tagName = this.nodeName = name.toUpperCase();
|
||||
if (typeof(data) == "string") {
|
||||
this.nodeValue = data;
|
||||
this.nodeType = 3;
|
||||
} else {
|
||||
this.nodeType = 1;
|
||||
this.childNodes = [];
|
||||
}
|
||||
if (name.substring(0, 1) == "<") {
|
||||
var nameattr = name.substring(
|
||||
name.indexOf('"') + 1, name.lastIndexOf('"'));
|
||||
name = name.substring(1, name.indexOf(" "));
|
||||
this.tagName = this.nodeName = name.toUpperCase();
|
||||
this.setAttribute("name", nameattr);
|
||||
}
|
||||
};
|
||||
|
||||
MochiKit.MockDOM.MockElement.prototype = {
|
||||
/** @id MochiKit.MockDOM.MockElement.prototype.createElement */
|
||||
createElement: function (tagName) {
|
||||
return new MochiKit.MockDOM.MockElement(tagName);
|
||||
},
|
||||
/** @id MochiKit.MockDOM.MockElement.prototype.createTextNode */
|
||||
createTextNode: function (text) {
|
||||
return new MochiKit.MockDOM.MockElement("text", text);
|
||||
},
|
||||
/** @id MochiKit.MockDOM.MockElement.prototype.setAttribute */
|
||||
setAttribute: function (name, value) {
|
||||
this[name] = value;
|
||||
},
|
||||
/** @id MochiKit.MockDOM.MockElement.prototype.getAttribute */
|
||||
getAttribute: function (name) {
|
||||
return this[name];
|
||||
},
|
||||
/** @id MochiKit.MockDOM.MockElement.prototype.appendChild */
|
||||
appendChild: function (child) {
|
||||
this.childNodes.push(child);
|
||||
},
|
||||
/** @id MochiKit.MockDOM.MockElement.prototype.toString */
|
||||
toString: function () {
|
||||
return "MockElement(" + this.tagName + ")";
|
||||
}
|
||||
};
|
||||
|
||||
/** @id MochiKit.MockDOM.EXPORT_OK */
|
||||
MochiKit.MockDOM.EXPORT_OK = [
|
||||
"mockElement",
|
||||
"createDocument"
|
||||
];
|
||||
|
||||
/** @id MochiKit.MockDOM.EXPORT */
|
||||
MochiKit.MockDOM.EXPORT = [
|
||||
"document"
|
||||
];
|
||||
|
||||
MochiKit.MockDOM.__new__ = function () {
|
||||
this.document = this.createDocument();
|
||||
};
|
||||
|
||||
MochiKit.MockDOM.__new__();
|
|
@ -0,0 +1,283 @@
|
|||
|
||||
MochiKit.Base.update(MochiKit.DOM, {
|
||||
/** @id MochiKit.DOM.makeClipping */
|
||||
makeClipping: function (element) {
|
||||
element = MochiKit.DOM.getElement(element);
|
||||
var oldOverflow = element.style.overflow;
|
||||
if ((MochiKit.Style.getStyle(element, 'overflow') || 'visible') != 'hidden') {
|
||||
element.style.overflow = 'hidden';
|
||||
}
|
||||
return oldOverflow;
|
||||
},
|
||||
|
||||
/** @id MochiKit.DOM.undoClipping */
|
||||
undoClipping: function (element, overflow) {
|
||||
element = MochiKit.DOM.getElement(element);
|
||||
if (!overflow) {
|
||||
return;
|
||||
}
|
||||
element.style.overflow = overflow;
|
||||
},
|
||||
|
||||
/** @id MochiKit.DOM.makePositioned */
|
||||
makePositioned: function (element) {
|
||||
element = MochiKit.DOM.getElement(element);
|
||||
var pos = MochiKit.Style.getStyle(element, 'position');
|
||||
if (pos == 'static' || !pos) {
|
||||
element.style.position = 'relative';
|
||||
// Opera returns the offset relative to the positioning context,
|
||||
// when an element is position relative but top and left have
|
||||
// not been defined
|
||||
if (/Opera/.test(navigator.userAgent)) {
|
||||
element.style.top = 0;
|
||||
element.style.left = 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.DOM.undoPositioned */
|
||||
undoPositioned: function (element) {
|
||||
element = MochiKit.DOM.getElement(element);
|
||||
if (element.style.position == 'relative') {
|
||||
element.style.position = element.style.top = element.style.left = element.style.bottom = element.style.right = '';
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.DOM.getFirstElementByTagAndClassName */
|
||||
getFirstElementByTagAndClassName: function (tagName, className,
|
||||
/* optional */parent) {
|
||||
var self = MochiKit.DOM;
|
||||
if (typeof(tagName) == 'undefined' || tagName === null) {
|
||||
tagName = '*';
|
||||
}
|
||||
if (typeof(parent) == 'undefined' || parent === null) {
|
||||
parent = self._document;
|
||||
}
|
||||
parent = self.getElement(parent);
|
||||
var children = (parent.getElementsByTagName(tagName)
|
||||
|| self._document.all);
|
||||
if (typeof(className) == 'undefined' || className === null) {
|
||||
return children[0];
|
||||
}
|
||||
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var child = children[i];
|
||||
var classNames = child.className.split(' ');
|
||||
for (var j = 0; j < classNames.length; j++) {
|
||||
if (classNames[j] == className) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.DOM.isParent */
|
||||
isParent: function (child, element) {
|
||||
if (!child.parentNode || child == element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (child.parentNode == element) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return MochiKit.DOM.isParent(child.parentNode, element);
|
||||
}
|
||||
});
|
||||
|
||||
MochiKit.Position = {
|
||||
// set to true if needed, warning: firefox performance problems
|
||||
// NOT neeeded for page scrolling, only if draggable contained in
|
||||
// scrollable elements
|
||||
includeScrollOffsets: false,
|
||||
|
||||
/** @id MochiKit.Position.prepare */
|
||||
prepare: function () {
|
||||
var deltaX = window.pageXOffset
|
||||
|| document.documentElement.scrollLeft
|
||||
|| document.body.scrollLeft
|
||||
|| 0;
|
||||
var deltaY = window.pageYOffset
|
||||
|| document.documentElement.scrollTop
|
||||
|| document.body.scrollTop
|
||||
|| 0;
|
||||
this.windowOffset = new MochiKit.Style.Coordinates(deltaX, deltaY);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Position.cumulativeOffset */
|
||||
cumulativeOffset: function (element) {
|
||||
var valueT = 0;
|
||||
var valueL = 0;
|
||||
do {
|
||||
valueT += element.offsetTop || 0;
|
||||
valueL += element.offsetLeft || 0;
|
||||
element = element.offsetParent;
|
||||
} while (element);
|
||||
return new MochiKit.Style.Coordinates(valueL, valueT);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Position.realOffset */
|
||||
realOffset: function (element) {
|
||||
var valueT = 0;
|
||||
var valueL = 0;
|
||||
do {
|
||||
valueT += element.scrollTop || 0;
|
||||
valueL += element.scrollLeft || 0;
|
||||
element = element.parentNode;
|
||||
} while (element);
|
||||
return new MochiKit.Style.Coordinates(valueL, valueT);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Position.within */
|
||||
within: function (element, x, y) {
|
||||
if (this.includeScrollOffsets) {
|
||||
return this.withinIncludingScrolloffsets(element, x, y);
|
||||
}
|
||||
this.xcomp = x;
|
||||
this.ycomp = y;
|
||||
this.offset = this.cumulativeOffset(element);
|
||||
if (element.style.position == "fixed") {
|
||||
this.offset.x += this.windowOffset.x;
|
||||
this.offset.y += this.windowOffset.y;
|
||||
}
|
||||
|
||||
return (y >= this.offset.y &&
|
||||
y < this.offset.y + element.offsetHeight &&
|
||||
x >= this.offset.x &&
|
||||
x < this.offset.x + element.offsetWidth);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Position.withinIncludingScrolloffsets */
|
||||
withinIncludingScrolloffsets: function (element, x, y) {
|
||||
var offsetcache = this.realOffset(element);
|
||||
|
||||
this.xcomp = x + offsetcache.x - this.windowOffset.x;
|
||||
this.ycomp = y + offsetcache.y - this.windowOffset.y;
|
||||
this.offset = this.cumulativeOffset(element);
|
||||
|
||||
return (this.ycomp >= this.offset.y &&
|
||||
this.ycomp < this.offset.y + element.offsetHeight &&
|
||||
this.xcomp >= this.offset.x &&
|
||||
this.xcomp < this.offset.x + element.offsetWidth);
|
||||
},
|
||||
|
||||
// within must be called directly before
|
||||
/** @id MochiKit.Position.overlap */
|
||||
overlap: function (mode, element) {
|
||||
if (!mode) {
|
||||
return 0;
|
||||
}
|
||||
if (mode == 'vertical') {
|
||||
return ((this.offset.y + element.offsetHeight) - this.ycomp) /
|
||||
element.offsetHeight;
|
||||
}
|
||||
if (mode == 'horizontal') {
|
||||
return ((this.offset.x + element.offsetWidth) - this.xcomp) /
|
||||
element.offsetWidth;
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Position.absolutize */
|
||||
absolutize: function (element) {
|
||||
element = MochiKit.DOM.getElement(element);
|
||||
if (element.style.position == 'absolute') {
|
||||
return;
|
||||
}
|
||||
MochiKit.Position.prepare();
|
||||
|
||||
var offsets = MochiKit.Position.positionedOffset(element);
|
||||
var width = element.clientWidth;
|
||||
var height = element.clientHeight;
|
||||
|
||||
var oldStyle = {
|
||||
'position': element.style.position,
|
||||
'left': offsets.x - parseFloat(element.style.left || 0),
|
||||
'top': offsets.y - parseFloat(element.style.top || 0),
|
||||
'width': element.style.width,
|
||||
'height': element.style.height
|
||||
};
|
||||
|
||||
element.style.position = 'absolute';
|
||||
element.style.top = offsets.y + 'px';
|
||||
element.style.left = offsets.x + 'px';
|
||||
element.style.width = width + 'px';
|
||||
element.style.height = height + 'px';
|
||||
|
||||
return oldStyle;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Position.positionedOffset */
|
||||
positionedOffset: function (element) {
|
||||
var valueT = 0, valueL = 0;
|
||||
do {
|
||||
valueT += element.offsetTop || 0;
|
||||
valueL += element.offsetLeft || 0;
|
||||
element = element.offsetParent;
|
||||
if (element) {
|
||||
p = MochiKit.Style.getStyle(element, 'position');
|
||||
if (p == 'relative' || p == 'absolute') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (element);
|
||||
return new MochiKit.Style.Coordinates(valueL, valueT);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Position.relativize */
|
||||
relativize: function (element, oldPos) {
|
||||
element = MochiKit.DOM.getElement(element);
|
||||
if (element.style.position == 'relative') {
|
||||
return;
|
||||
}
|
||||
MochiKit.Position.prepare();
|
||||
|
||||
var top = parseFloat(element.style.top || 0) -
|
||||
(oldPos['top'] || 0);
|
||||
var left = parseFloat(element.style.left || 0) -
|
||||
(oldPos['left'] || 0);
|
||||
|
||||
element.style.position = oldPos['position'];
|
||||
element.style.top = top + 'px';
|
||||
element.style.left = left + 'px';
|
||||
element.style.width = oldPos['width'];
|
||||
element.style.height = oldPos['height'];
|
||||
},
|
||||
|
||||
/** @id MochiKit.Position.clone */
|
||||
clone: function (source, target) {
|
||||
source = MochiKit.DOM.getElement(source);
|
||||
target = MochiKit.DOM.getElement(target);
|
||||
target.style.position = 'absolute';
|
||||
var offsets = this.cumulativeOffset(source);
|
||||
target.style.top = offsets.y + 'px';
|
||||
target.style.left = offsets.x + 'px';
|
||||
target.style.width = source.offsetWidth + 'px';
|
||||
target.style.height = source.offsetHeight + 'px';
|
||||
},
|
||||
|
||||
/** @id MochiKit.Position.page */
|
||||
page: function (forElement) {
|
||||
var valueT = 0;
|
||||
var valueL = 0;
|
||||
|
||||
var element = forElement;
|
||||
do {
|
||||
valueT += element.offsetTop || 0;
|
||||
valueL += element.offsetLeft || 0;
|
||||
|
||||
// Safari fix
|
||||
if (element.offsetParent == document.body && MochiKit.Style.getStyle(element, 'position') == 'absolute') {
|
||||
break;
|
||||
}
|
||||
} while (element = element.offsetParent);
|
||||
|
||||
element = forElement;
|
||||
do {
|
||||
valueT -= element.scrollTop || 0;
|
||||
valueL -= element.scrollLeft || 0;
|
||||
} while (element = element.parentNode);
|
||||
|
||||
return new MochiKit.Style.Coordinates(valueL, valueT);
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,857 @@
|
|||
/***
|
||||
|
||||
MochiKit.Signal 1.4
|
||||
|
||||
See <http://mochikit.com/> for documentation, downloads, license, etc.
|
||||
|
||||
(c) 2006 Jonathan Gardner, Beau Hartshorne, Bob Ippolito. All rights Reserved.
|
||||
|
||||
***/
|
||||
|
||||
if (typeof(dojo) != 'undefined') {
|
||||
dojo.provide('MochiKit.Signal');
|
||||
dojo.require('MochiKit.Base');
|
||||
dojo.require('MochiKit.DOM');
|
||||
dojo.require('MochiKit.Style');
|
||||
}
|
||||
if (typeof(JSAN) != 'undefined') {
|
||||
JSAN.use('MochiKit.Base', []);
|
||||
JSAN.use('MochiKit.DOM', []);
|
||||
JSAN.use('MochiKit.Style', []);
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof(MochiKit.Base) == 'undefined') {
|
||||
throw '';
|
||||
}
|
||||
} catch (e) {
|
||||
throw 'MochiKit.Signal depends on MochiKit.Base!';
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof(MochiKit.DOM) == 'undefined') {
|
||||
throw '';
|
||||
}
|
||||
} catch (e) {
|
||||
throw 'MochiKit.Signal depends on MochiKit.DOM!';
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof(MochiKit.Style) == 'undefined') {
|
||||
throw '';
|
||||
}
|
||||
} catch (e) {
|
||||
throw 'MochiKit.Signal depends on MochiKit.Style!';
|
||||
}
|
||||
|
||||
if (typeof(MochiKit.Signal) == 'undefined') {
|
||||
MochiKit.Signal = {};
|
||||
}
|
||||
|
||||
MochiKit.Signal.NAME = 'MochiKit.Signal';
|
||||
MochiKit.Signal.VERSION = '1.4';
|
||||
|
||||
MochiKit.Signal._observers = [];
|
||||
|
||||
/** @id MochiKit.Signal.Event */
|
||||
MochiKit.Signal.Event = function (src, e) {
|
||||
this._event = e || window.event;
|
||||
this._src = src;
|
||||
};
|
||||
|
||||
MochiKit.Base.update(MochiKit.Signal.Event.prototype, {
|
||||
|
||||
__repr__: function () {
|
||||
var repr = MochiKit.Base.repr;
|
||||
var str = '{event(): ' + repr(this.event()) +
|
||||
', src(): ' + repr(this.src()) +
|
||||
', type(): ' + repr(this.type()) +
|
||||
', target(): ' + repr(this.target()) +
|
||||
', modifier(): ' + '{alt: ' + repr(this.modifier().alt) +
|
||||
', ctrl: ' + repr(this.modifier().ctrl) +
|
||||
', meta: ' + repr(this.modifier().meta) +
|
||||
', shift: ' + repr(this.modifier().shift) +
|
||||
', any: ' + repr(this.modifier().any) + '}';
|
||||
|
||||
if (this.type() && this.type().indexOf('key') === 0) {
|
||||
str += ', key(): {code: ' + repr(this.key().code) +
|
||||
', string: ' + repr(this.key().string) + '}';
|
||||
}
|
||||
|
||||
if (this.type() && (
|
||||
this.type().indexOf('mouse') === 0 ||
|
||||
this.type().indexOf('click') != -1 ||
|
||||
this.type() == 'contextmenu')) {
|
||||
|
||||
str += ', mouse(): {page: ' + repr(this.mouse().page) +
|
||||
', client: ' + repr(this.mouse().client);
|
||||
|
||||
if (this.type() != 'mousemove') {
|
||||
str += ', button: {left: ' + repr(this.mouse().button.left) +
|
||||
', middle: ' + repr(this.mouse().button.middle) +
|
||||
', right: ' + repr(this.mouse().button.right) + '}}';
|
||||
} else {
|
||||
str += '}';
|
||||
}
|
||||
}
|
||||
if (this.type() == 'mouseover' || this.type() == 'mouseout') {
|
||||
str += ', relatedTarget(): ' + repr(this.relatedTarget());
|
||||
}
|
||||
str += '}';
|
||||
return str;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Signal.Event.prototype.toString */
|
||||
toString: function () {
|
||||
return this.__repr__();
|
||||
},
|
||||
|
||||
/** @id MochiKit.Signal.Event.prototype.src */
|
||||
src: function () {
|
||||
return this._src;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Signal.Event.prototype.event */
|
||||
event: function () {
|
||||
return this._event;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Signal.Event.prototype.type */
|
||||
type: function () {
|
||||
return this._event.type || undefined;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Signal.Event.prototype.target */
|
||||
target: function () {
|
||||
return this._event.target || this._event.srcElement;
|
||||
},
|
||||
|
||||
_relatedTarget: null,
|
||||
/** @id MochiKit.Signal.Event.prototype.relatedTarget */
|
||||
relatedTarget: function () {
|
||||
if (this._relatedTarget !== null) {
|
||||
return this._relatedTarget;
|
||||
}
|
||||
|
||||
var elem = null;
|
||||
if (this.type() == 'mouseover') {
|
||||
elem = (this._event.relatedTarget ||
|
||||
this._event.fromElement);
|
||||
} else if (this.type() == 'mouseout') {
|
||||
elem = (this._event.relatedTarget ||
|
||||
this._event.toElement);
|
||||
}
|
||||
if (elem !== null) {
|
||||
this._relatedTarget = elem;
|
||||
return elem;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
|
||||
_modifier: null,
|
||||
/** @id MochiKit.Signal.Event.prototype.modifier */
|
||||
modifier: function () {
|
||||
if (this._modifier !== null) {
|
||||
return this._modifier;
|
||||
}
|
||||
var m = {};
|
||||
m.alt = this._event.altKey;
|
||||
m.ctrl = this._event.ctrlKey;
|
||||
m.meta = this._event.metaKey || false; // IE and Opera punt here
|
||||
m.shift = this._event.shiftKey;
|
||||
m.any = m.alt || m.ctrl || m.shift || m.meta;
|
||||
this._modifier = m;
|
||||
return m;
|
||||
},
|
||||
|
||||
_key: null,
|
||||
/** @id MochiKit.Signal.Event.prototype.key */
|
||||
key: function () {
|
||||
if (this._key !== null) {
|
||||
return this._key;
|
||||
}
|
||||
var k = {};
|
||||
if (this.type() && this.type().indexOf('key') === 0) {
|
||||
|
||||
/*
|
||||
|
||||
If you're looking for a special key, look for it in keydown or
|
||||
keyup, but never keypress. If you're looking for a Unicode
|
||||
chracter, look for it with keypress, but never keyup or
|
||||
keydown.
|
||||
|
||||
Notes:
|
||||
|
||||
FF key event behavior:
|
||||
key event charCode keyCode
|
||||
DOWN ku,kd 0 40
|
||||
DOWN kp 0 40
|
||||
ESC ku,kd 0 27
|
||||
ESC kp 0 27
|
||||
a ku,kd 0 65
|
||||
a kp 97 0
|
||||
shift+a ku,kd 0 65
|
||||
shift+a kp 65 0
|
||||
1 ku,kd 0 49
|
||||
1 kp 49 0
|
||||
shift+1 ku,kd 0 0
|
||||
shift+1 kp 33 0
|
||||
|
||||
IE key event behavior:
|
||||
(IE doesn't fire keypress events for special keys.)
|
||||
key event keyCode
|
||||
DOWN ku,kd 40
|
||||
DOWN kp undefined
|
||||
ESC ku,kd 27
|
||||
ESC kp 27
|
||||
a ku,kd 65
|
||||
a kp 97
|
||||
shift+a ku,kd 65
|
||||
shift+a kp 65
|
||||
1 ku,kd 49
|
||||
1 kp 49
|
||||
shift+1 ku,kd 49
|
||||
shift+1 kp 33
|
||||
|
||||
Safari key event behavior:
|
||||
(Safari sets charCode and keyCode to something crazy for
|
||||
special keys.)
|
||||
key event charCode keyCode
|
||||
DOWN ku,kd 63233 40
|
||||
DOWN kp 63233 63233
|
||||
ESC ku,kd 27 27
|
||||
ESC kp 27 27
|
||||
a ku,kd 97 65
|
||||
a kp 97 97
|
||||
shift+a ku,kd 65 65
|
||||
shift+a kp 65 65
|
||||
1 ku,kd 49 49
|
||||
1 kp 49 49
|
||||
shift+1 ku,kd 33 49
|
||||
shift+1 kp 33 33
|
||||
|
||||
*/
|
||||
|
||||
/* look for special keys here */
|
||||
if (this.type() == 'keydown' || this.type() == 'keyup') {
|
||||
k.code = this._event.keyCode;
|
||||
k.string = (MochiKit.Signal._specialKeys[k.code] ||
|
||||
'KEY_UNKNOWN');
|
||||
this._key = k;
|
||||
return k;
|
||||
|
||||
/* look for characters here */
|
||||
} else if (this.type() == 'keypress') {
|
||||
|
||||
/*
|
||||
|
||||
Special key behavior:
|
||||
|
||||
IE: does not fire keypress events for special keys
|
||||
FF: sets charCode to 0, and sets the correct keyCode
|
||||
Safari: sets keyCode and charCode to something stupid
|
||||
|
||||
*/
|
||||
|
||||
k.code = 0;
|
||||
k.string = '';
|
||||
|
||||
if (typeof(this._event.charCode) != 'undefined' &&
|
||||
this._event.charCode !== 0 &&
|
||||
!MochiKit.Signal._specialMacKeys[this._event.charCode]) {
|
||||
k.code = this._event.charCode;
|
||||
k.string = String.fromCharCode(k.code);
|
||||
} else if (this._event.keyCode &&
|
||||
typeof(this._event.charCode) == 'undefined') { // IE
|
||||
k.code = this._event.keyCode;
|
||||
k.string = String.fromCharCode(k.code);
|
||||
}
|
||||
|
||||
this._key = k;
|
||||
return k;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
_mouse: null,
|
||||
/** @id MochiKit.Signal.Event.prototype.mouse */
|
||||
mouse: function () {
|
||||
if (this._mouse !== null) {
|
||||
return this._mouse;
|
||||
}
|
||||
|
||||
var m = {};
|
||||
var e = this._event;
|
||||
|
||||
if (this.type() && (
|
||||
this.type().indexOf('mouse') === 0 ||
|
||||
this.type().indexOf('click') != -1 ||
|
||||
this.type() == 'contextmenu')) {
|
||||
|
||||
m.client = new MochiKit.Style.Coordinates(0, 0);
|
||||
if (e.clientX || e.clientY) {
|
||||
m.client.x = (!e.clientX || e.clientX < 0) ? 0 : e.clientX;
|
||||
m.client.y = (!e.clientY || e.clientY < 0) ? 0 : e.clientY;
|
||||
}
|
||||
|
||||
m.page = new MochiKit.Style.Coordinates(0, 0);
|
||||
if (e.pageX || e.pageY) {
|
||||
m.page.x = (!e.pageX || e.pageX < 0) ? 0 : e.pageX;
|
||||
m.page.y = (!e.pageY || e.pageY < 0) ? 0 : e.pageY;
|
||||
} else {
|
||||
/*
|
||||
|
||||
The IE shortcut can be off by two. We fix it. See:
|
||||
http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/getboundingclientrect.asp
|
||||
|
||||
This is similar to the method used in
|
||||
MochiKit.Style.getElementPosition().
|
||||
|
||||
*/
|
||||
var de = MochiKit.DOM._document.documentElement;
|
||||
var b = MochiKit.DOM._document.body;
|
||||
|
||||
m.page.x = e.clientX +
|
||||
(de.scrollLeft || b.scrollLeft) -
|
||||
(de.clientLeft || 0);
|
||||
|
||||
m.page.y = e.clientY +
|
||||
(de.scrollTop || b.scrollTop) -
|
||||
(de.clientTop || 0);
|
||||
|
||||
}
|
||||
if (this.type() != 'mousemove') {
|
||||
m.button = {};
|
||||
m.button.left = false;
|
||||
m.button.right = false;
|
||||
m.button.middle = false;
|
||||
|
||||
/* we could check e.button, but which is more consistent */
|
||||
if (e.which) {
|
||||
m.button.left = (e.which == 1);
|
||||
m.button.middle = (e.which == 2);
|
||||
m.button.right = (e.which == 3);
|
||||
|
||||
/*
|
||||
|
||||
Mac browsers and right click:
|
||||
|
||||
- Safari doesn't fire any click events on a right
|
||||
click:
|
||||
http://bugzilla.opendarwin.org/show_bug.cgi?id=6595
|
||||
|
||||
- Firefox fires the event, and sets ctrlKey = true
|
||||
|
||||
- Opera fires the event, and sets metaKey = true
|
||||
|
||||
oncontextmenu is fired on right clicks between
|
||||
browsers and across platforms.
|
||||
|
||||
*/
|
||||
|
||||
} else {
|
||||
m.button.left = !!(e.button & 1);
|
||||
m.button.right = !!(e.button & 2);
|
||||
m.button.middle = !!(e.button & 4);
|
||||
}
|
||||
}
|
||||
this._mouse = m;
|
||||
return m;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Signal.Event.prototype.stop */
|
||||
stop: function () {
|
||||
this.stopPropagation();
|
||||
this.preventDefault();
|
||||
},
|
||||
|
||||
/** @id MochiKit.Signal.Event.prototype.stopPropagation */
|
||||
stopPropagation: function () {
|
||||
if (this._event.stopPropagation) {
|
||||
this._event.stopPropagation();
|
||||
} else {
|
||||
this._event.cancelBubble = true;
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Signal.Event.prototype.preventDefault */
|
||||
preventDefault: function () {
|
||||
if (this._event.preventDefault) {
|
||||
this._event.preventDefault();
|
||||
} else if (this._confirmUnload === null) {
|
||||
this._event.returnValue = false;
|
||||
}
|
||||
},
|
||||
|
||||
_confirmUnload: null,
|
||||
|
||||
/** @id MochiKit.Signal.Event.prototype.confirmUnload */
|
||||
confirmUnload: function (msg) {
|
||||
if (this.type() == 'beforeunload') {
|
||||
this._confirmUnload = msg;
|
||||
this._event.returnValue = msg;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* Safari sets keyCode to these special values onkeypress. */
|
||||
MochiKit.Signal._specialMacKeys = {
|
||||
3: 'KEY_ENTER',
|
||||
63289: 'KEY_NUM_PAD_CLEAR',
|
||||
63276: 'KEY_PAGE_UP',
|
||||
63277: 'KEY_PAGE_DOWN',
|
||||
63275: 'KEY_END',
|
||||
63273: 'KEY_HOME',
|
||||
63234: 'KEY_ARROW_LEFT',
|
||||
63232: 'KEY_ARROW_UP',
|
||||
63235: 'KEY_ARROW_RIGHT',
|
||||
63233: 'KEY_ARROW_DOWN',
|
||||
63302: 'KEY_INSERT',
|
||||
63272: 'KEY_DELETE'
|
||||
};
|
||||
|
||||
/* for KEY_F1 - KEY_F12 */
|
||||
(function () {
|
||||
var _specialMacKeys = MochiKit.Signal._specialMacKeys;
|
||||
for (i = 63236; i <= 63242; i++) {
|
||||
// no F0
|
||||
_specialMacKeys[i] = 'KEY_F' + (i - 63236 + 1);
|
||||
}
|
||||
})();
|
||||
|
||||
/* Standard keyboard key codes. */
|
||||
MochiKit.Signal._specialKeys = {
|
||||
8: 'KEY_BACKSPACE',
|
||||
9: 'KEY_TAB',
|
||||
12: 'KEY_NUM_PAD_CLEAR', // weird, for Safari and Mac FF only
|
||||
13: 'KEY_ENTER',
|
||||
16: 'KEY_SHIFT',
|
||||
17: 'KEY_CTRL',
|
||||
18: 'KEY_ALT',
|
||||
19: 'KEY_PAUSE',
|
||||
20: 'KEY_CAPS_LOCK',
|
||||
27: 'KEY_ESCAPE',
|
||||
32: 'KEY_SPACEBAR',
|
||||
33: 'KEY_PAGE_UP',
|
||||
34: 'KEY_PAGE_DOWN',
|
||||
35: 'KEY_END',
|
||||
36: 'KEY_HOME',
|
||||
37: 'KEY_ARROW_LEFT',
|
||||
38: 'KEY_ARROW_UP',
|
||||
39: 'KEY_ARROW_RIGHT',
|
||||
40: 'KEY_ARROW_DOWN',
|
||||
44: 'KEY_PRINT_SCREEN',
|
||||
45: 'KEY_INSERT',
|
||||
46: 'KEY_DELETE',
|
||||
59: 'KEY_SEMICOLON', // weird, for Safari and IE only
|
||||
91: 'KEY_WINDOWS_LEFT',
|
||||
92: 'KEY_WINDOWS_RIGHT',
|
||||
93: 'KEY_SELECT',
|
||||
106: 'KEY_NUM_PAD_ASTERISK',
|
||||
107: 'KEY_NUM_PAD_PLUS_SIGN',
|
||||
109: 'KEY_NUM_PAD_HYPHEN-MINUS',
|
||||
110: 'KEY_NUM_PAD_FULL_STOP',
|
||||
111: 'KEY_NUM_PAD_SOLIDUS',
|
||||
144: 'KEY_NUM_LOCK',
|
||||
145: 'KEY_SCROLL_LOCK',
|
||||
186: 'KEY_SEMICOLON',
|
||||
187: 'KEY_EQUALS_SIGN',
|
||||
188: 'KEY_COMMA',
|
||||
189: 'KEY_HYPHEN-MINUS',
|
||||
190: 'KEY_FULL_STOP',
|
||||
191: 'KEY_SOLIDUS',
|
||||
192: 'KEY_GRAVE_ACCENT',
|
||||
219: 'KEY_LEFT_SQUARE_BRACKET',
|
||||
220: 'KEY_REVERSE_SOLIDUS',
|
||||
221: 'KEY_RIGHT_SQUARE_BRACKET',
|
||||
222: 'KEY_APOSTROPHE'
|
||||
// undefined: 'KEY_UNKNOWN'
|
||||
};
|
||||
|
||||
(function () {
|
||||
/* for KEY_0 - KEY_9 */
|
||||
var _specialKeys = MochiKit.Signal._specialKeys;
|
||||
for (var i = 48; i <= 57; i++) {
|
||||
_specialKeys[i] = 'KEY_' + (i - 48);
|
||||
}
|
||||
|
||||
/* for KEY_A - KEY_Z */
|
||||
for (i = 65; i <= 90; i++) {
|
||||
_specialKeys[i] = 'KEY_' + String.fromCharCode(i);
|
||||
}
|
||||
|
||||
/* for KEY_NUM_PAD_0 - KEY_NUM_PAD_9 */
|
||||
for (i = 96; i <= 105; i++) {
|
||||
_specialKeys[i] = 'KEY_NUM_PAD_' + (i - 96);
|
||||
}
|
||||
|
||||
/* for KEY_F1 - KEY_F12 */
|
||||
for (i = 112; i <= 123; i++) {
|
||||
// no F0
|
||||
_specialKeys[i] = 'KEY_F' + (i - 112 + 1);
|
||||
}
|
||||
})();
|
||||
|
||||
MochiKit.Base.update(MochiKit.Signal, {
|
||||
|
||||
__repr__: function () {
|
||||
return '[' + this.NAME + ' ' + this.VERSION + ']';
|
||||
},
|
||||
|
||||
toString: function () {
|
||||
return this.__repr__();
|
||||
},
|
||||
|
||||
_unloadCache: function () {
|
||||
var self = MochiKit.Signal;
|
||||
var observers = self._observers;
|
||||
|
||||
for (var i = 0; i < observers.length; i++) {
|
||||
self._disconnect(observers[i]);
|
||||
}
|
||||
|
||||
delete self._observers;
|
||||
|
||||
try {
|
||||
window.onload = undefined;
|
||||
} catch(e) {
|
||||
// pass
|
||||
}
|
||||
|
||||
try {
|
||||
window.onunload = undefined;
|
||||
} catch(e) {
|
||||
// pass
|
||||
}
|
||||
},
|
||||
|
||||
_listener: function (src, func, obj, isDOM) {
|
||||
var self = MochiKit.Signal;
|
||||
var E = self.Event;
|
||||
if (!isDOM) {
|
||||
return MochiKit.Base.bind(func, obj);
|
||||
}
|
||||
obj = obj || src;
|
||||
if (typeof(func) == "string") {
|
||||
return function (nativeEvent) {
|
||||
obj[func].apply(obj, [new E(src, nativeEvent)]);
|
||||
};
|
||||
} else {
|
||||
return function (nativeEvent) {
|
||||
func.apply(obj, [new E(src, nativeEvent)]);
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
_browserAlreadyHasMouseEnterAndLeave: function () {
|
||||
return /MSIE/.test(navigator.userAgent);
|
||||
},
|
||||
|
||||
_mouseEnterListener: function (src, sig, func, obj) {
|
||||
var E = MochiKit.Signal.Event;
|
||||
return function (nativeEvent) {
|
||||
var e = new E(src, nativeEvent);
|
||||
try {
|
||||
e.relatedTarget().nodeName;
|
||||
} catch (err) {
|
||||
/* probably hit a permission denied error; possibly one of
|
||||
* firefox's screwy anonymous DIVs inside an input element.
|
||||
* Allow this event to propogate up.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
e.stop();
|
||||
if (MochiKit.DOM.isChildNode(e.relatedTarget(), src)) {
|
||||
/* We've moved between our node and a child. Ignore. */
|
||||
return;
|
||||
}
|
||||
e.type = function () { return sig; };
|
||||
if (typeof(func) == "string") {
|
||||
return obj[func].apply(obj, [e]);
|
||||
} else {
|
||||
return func.apply(obj, [e]);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
_getDestPair: function (objOrFunc, funcOrStr) {
|
||||
var obj = null;
|
||||
var func = null;
|
||||
if (typeof(funcOrStr) != 'undefined') {
|
||||
obj = objOrFunc;
|
||||
func = funcOrStr;
|
||||
if (typeof(funcOrStr) == 'string') {
|
||||
if (typeof(objOrFunc[funcOrStr]) != "function") {
|
||||
throw new Error("'funcOrStr' must be a function on 'objOrFunc'");
|
||||
}
|
||||
} else if (typeof(funcOrStr) != 'function') {
|
||||
throw new Error("'funcOrStr' must be a function or string");
|
||||
}
|
||||
} else if (typeof(objOrFunc) != "function") {
|
||||
throw new Error("'objOrFunc' must be a function if 'funcOrStr' is not given");
|
||||
} else {
|
||||
func = objOrFunc;
|
||||
}
|
||||
return [obj, func];
|
||||
|
||||
},
|
||||
|
||||
/** @id MochiKit.Signal.connect */
|
||||
connect: function (src, sig, objOrFunc/* optional */, funcOrStr) {
|
||||
src = MochiKit.DOM.getElement(src);
|
||||
var self = MochiKit.Signal;
|
||||
|
||||
if (typeof(sig) != 'string') {
|
||||
throw new Error("'sig' must be a string");
|
||||
}
|
||||
|
||||
var destPair = self._getDestPair(objOrFunc, funcOrStr);
|
||||
var obj = destPair[0];
|
||||
var func = destPair[1];
|
||||
if (typeof(obj) == 'undefined' || obj === null) {
|
||||
obj = src;
|
||||
}
|
||||
|
||||
var isDOM = !!(src.addEventListener || src.attachEvent);
|
||||
if (isDOM && (sig === "onmouseenter" || sig === "onmouseleave")
|
||||
&& !self._browserAlreadyHasMouseEnterAndLeave()) {
|
||||
var listener = self._mouseEnterListener(src, sig.substr(2), func, obj);
|
||||
if (sig === "onmouseenter") {
|
||||
sig = "onmouseover";
|
||||
} else {
|
||||
sig = "onmouseout";
|
||||
}
|
||||
} else {
|
||||
var listener = self._listener(src, func, obj, isDOM);
|
||||
}
|
||||
|
||||
if (src.addEventListener) {
|
||||
src.addEventListener(sig.substr(2), listener, false);
|
||||
} else if (src.attachEvent) {
|
||||
src.attachEvent(sig, listener); // useCapture unsupported
|
||||
}
|
||||
|
||||
var ident = [src, sig, listener, isDOM, objOrFunc, funcOrStr, true];
|
||||
self._observers.push(ident);
|
||||
|
||||
|
||||
if (!isDOM && typeof(src.__connect__) == 'function') {
|
||||
var args = MochiKit.Base.extend([ident], arguments, 1);
|
||||
src.__connect__.apply(src, args);
|
||||
}
|
||||
|
||||
|
||||
return ident;
|
||||
},
|
||||
|
||||
_disconnect: function (ident) {
|
||||
// already disconnected
|
||||
if (!ident[6]) { return; }
|
||||
ident[6] = false;
|
||||
// check isDOM
|
||||
if (!ident[3]) { return; }
|
||||
var src = ident[0];
|
||||
var sig = ident[1];
|
||||
var listener = ident[2];
|
||||
if (src.removeEventListener) {
|
||||
src.removeEventListener(sig.substr(2), listener, false);
|
||||
} else if (src.detachEvent) {
|
||||
src.detachEvent(sig, listener); // useCapture unsupported
|
||||
} else {
|
||||
throw new Error("'src' must be a DOM element");
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Signal.disconnect */
|
||||
disconnect: function (ident) {
|
||||
var self = MochiKit.Signal;
|
||||
var observers = self._observers;
|
||||
var m = MochiKit.Base;
|
||||
if (arguments.length > 1) {
|
||||
// compatibility API
|
||||
var src = MochiKit.DOM.getElement(arguments[0]);
|
||||
var sig = arguments[1];
|
||||
var obj = arguments[2];
|
||||
var func = arguments[3];
|
||||
for (var i = observers.length - 1; i >= 0; i--) {
|
||||
var o = observers[i];
|
||||
if (o[0] === src && o[1] === sig && o[4] === obj && o[5] === func) {
|
||||
self._disconnect(o);
|
||||
if (!self._lock) {
|
||||
observers.splice(i, 1);
|
||||
} else {
|
||||
self._dirty = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var idx = m.findIdentical(observers, ident);
|
||||
if (idx >= 0) {
|
||||
self._disconnect(ident);
|
||||
if (!self._lock) {
|
||||
observers.splice(idx, 1);
|
||||
} else {
|
||||
self._dirty = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Signal.disconnectAllTo */
|
||||
disconnectAllTo: function (objOrFunc, /* optional */funcOrStr) {
|
||||
var self = MochiKit.Signal;
|
||||
var observers = self._observers;
|
||||
var disconnect = self._disconnect;
|
||||
var locked = self._lock;
|
||||
var dirty = self._dirty;
|
||||
if (typeof(funcOrStr) === 'undefined') {
|
||||
funcOrStr = null;
|
||||
}
|
||||
for (var i = observers.length - 1; i >= 0; i--) {
|
||||
var ident = observers[i];
|
||||
if (ident[4] === objOrFunc &&
|
||||
(funcOrStr === null || ident[5] === funcOrStr)) {
|
||||
disconnect(ident);
|
||||
if (locked) {
|
||||
dirty = true;
|
||||
} else {
|
||||
observers.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
self._dirty = dirty;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Signal.disconnectAll */
|
||||
disconnectAll: function (src/* optional */, sig) {
|
||||
src = MochiKit.DOM.getElement(src);
|
||||
var m = MochiKit.Base;
|
||||
var signals = m.flattenArguments(m.extend(null, arguments, 1));
|
||||
var self = MochiKit.Signal;
|
||||
var disconnect = self._disconnect;
|
||||
var observers = self._observers;
|
||||
var i, ident;
|
||||
var locked = self._lock;
|
||||
var dirty = self._dirty;
|
||||
if (signals.length === 0) {
|
||||
// disconnect all
|
||||
for (i = observers.length - 1; i >= 0; i--) {
|
||||
ident = observers[i];
|
||||
if (ident[0] === src) {
|
||||
disconnect(ident);
|
||||
if (!locked) {
|
||||
observers.splice(i, 1);
|
||||
} else {
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var sigs = {};
|
||||
for (i = 0; i < signals.length; i++) {
|
||||
sigs[signals[i]] = true;
|
||||
}
|
||||
for (i = observers.length - 1; i >= 0; i--) {
|
||||
ident = observers[i];
|
||||
if (ident[0] === src && ident[1] in sigs) {
|
||||
disconnect(ident);
|
||||
if (!locked) {
|
||||
observers.splice(i, 1);
|
||||
} else {
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self._dirty = dirty;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Signal.signal */
|
||||
signal: function (src, sig) {
|
||||
var self = MochiKit.Signal;
|
||||
var observers = self._observers;
|
||||
src = MochiKit.DOM.getElement(src);
|
||||
var args = MochiKit.Base.extend(null, arguments, 2);
|
||||
var errors = [];
|
||||
self._lock = true;
|
||||
for (var i = 0; i < observers.length; i++) {
|
||||
var ident = observers[i];
|
||||
if (ident[0] === src && ident[1] === sig) {
|
||||
try {
|
||||
ident[2].apply(src, args);
|
||||
} catch (e) {
|
||||
errors.push(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
self._lock = false;
|
||||
if (self._dirty) {
|
||||
self._dirty = false;
|
||||
for (var i = observers.length - 1; i >= 0; i--) {
|
||||
if (!observers[i][6]) {
|
||||
observers.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (errors.length == 1) {
|
||||
throw errors[0];
|
||||
} else if (errors.length > 1) {
|
||||
var e = new Error("Multiple errors thrown in handling 'sig', see errors property");
|
||||
e.errors = errors;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
MochiKit.Signal.EXPORT_OK = [];
|
||||
|
||||
MochiKit.Signal.EXPORT = [
|
||||
'connect',
|
||||
'disconnect',
|
||||
'signal',
|
||||
'disconnectAll',
|
||||
'disconnectAllTo'
|
||||
];
|
||||
|
||||
MochiKit.Signal.__new__ = function (win) {
|
||||
var m = MochiKit.Base;
|
||||
this._document = document;
|
||||
this._window = win;
|
||||
this._lock = false;
|
||||
this._dirty = false;
|
||||
|
||||
try {
|
||||
this.connect(window, 'onunload', this._unloadCache);
|
||||
} catch (e) {
|
||||
// pass: might not be a browser
|
||||
}
|
||||
|
||||
this.EXPORT_TAGS = {
|
||||
':common': this.EXPORT,
|
||||
':all': m.concat(this.EXPORT, this.EXPORT_OK)
|
||||
};
|
||||
|
||||
m.nameFunctions(this);
|
||||
};
|
||||
|
||||
MochiKit.Signal.__new__(this);
|
||||
|
||||
//
|
||||
// XXX: Internet Explorer blows
|
||||
//
|
||||
if (MochiKit.__export__) {
|
||||
connect = MochiKit.Signal.connect;
|
||||
disconnect = MochiKit.Signal.disconnect;
|
||||
disconnectAll = MochiKit.Signal.disconnectAll;
|
||||
signal = MochiKit.Signal.signal;
|
||||
}
|
||||
|
||||
MochiKit.Base._exportSymbols(this, MochiKit.Signal);
|
|
@ -0,0 +1,588 @@
|
|||
/***
|
||||
Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||
Mochi-ized By Thomas Herve (_firstname_@nimail.org)
|
||||
|
||||
See scriptaculous.js for full license.
|
||||
|
||||
***/
|
||||
|
||||
if (typeof(dojo) != 'undefined') {
|
||||
dojo.provide('MochiKit.DragAndDrop');
|
||||
dojo.require('MochiKit.Base');
|
||||
dojo.require('MochiKit.DOM');
|
||||
dojo.require('MochiKit.Iter');
|
||||
}
|
||||
|
||||
if (typeof(JSAN) != 'undefined') {
|
||||
JSAN.use("MochiKit.Base", []);
|
||||
JSAN.use("MochiKit.DOM", []);
|
||||
JSAN.use("MochiKit.Iter", []);
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof(MochiKit.Base) == 'undefined' ||
|
||||
typeof(MochiKit.DOM) == 'undefined' ||
|
||||
typeof(MochiKit.Iter) == 'undefined') {
|
||||
throw "";
|
||||
}
|
||||
} catch (e) {
|
||||
throw "MochiKit.DragAndDrop depends on MochiKit.Base, MochiKit.DOM and MochiKit.Iter!";
|
||||
}
|
||||
|
||||
if (typeof(MochiKit.Sortable) == 'undefined') {
|
||||
MochiKit.Sortable = {};
|
||||
}
|
||||
|
||||
MochiKit.Sortable.NAME = 'MochiKit.Sortable';
|
||||
MochiKit.Sortable.VERSION = '1.4';
|
||||
|
||||
MochiKit.Sortable.__repr__ = function () {
|
||||
return '[' + this.NAME + ' ' + this.VERSION + ']';
|
||||
};
|
||||
|
||||
MochiKit.Sortable.toString = function () {
|
||||
return this.__repr__();
|
||||
};
|
||||
|
||||
MochiKit.Sortable.EXPORT = [
|
||||
];
|
||||
|
||||
MochiKit.DragAndDrop.EXPORT_OK = [
|
||||
"Sortable"
|
||||
];
|
||||
|
||||
MochiKit.Sortable.Sortable = {
|
||||
/***
|
||||
|
||||
Manage sortables. Mainly use the create function to add a sortable.
|
||||
|
||||
***/
|
||||
sortables: {},
|
||||
|
||||
_findRootElement: function (element) {
|
||||
while (element.tagName.toUpperCase() != "BODY") {
|
||||
if (element.id && MochiKit.Sortable.Sortable.sortables[element.id]) {
|
||||
return element;
|
||||
}
|
||||
element = element.parentNode;
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.options */
|
||||
options: function (element) {
|
||||
element = MochiKit.Sortable.Sortable._findRootElement(MochiKit.DOM.getElement(element));
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
return MochiKit.Sortable.Sortable.sortables[element.id];
|
||||
},
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.destroy */
|
||||
destroy: function (element){
|
||||
var s = MochiKit.Sortable.Sortable.options(element);
|
||||
var b = MochiKit.Base;
|
||||
var d = MochiKit.DragAndDrop;
|
||||
|
||||
if (s) {
|
||||
MochiKit.Signal.disconnect(s.startHandle);
|
||||
MochiKit.Signal.disconnect(s.endHandle);
|
||||
b.map(function (dr) {
|
||||
d.Droppables.remove(dr);
|
||||
}, s.droppables);
|
||||
b.map(function (dr) {
|
||||
dr.destroy();
|
||||
}, s.draggables);
|
||||
|
||||
delete MochiKit.Sortable.Sortable.sortables[s.element.id];
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.create */
|
||||
create: function (element, options) {
|
||||
element = MochiKit.DOM.getElement(element);
|
||||
var self = MochiKit.Sortable.Sortable;
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.options */
|
||||
options = MochiKit.Base.update({
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.element */
|
||||
element: element,
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.tag */
|
||||
tag: 'li', // assumes li children, override with tag: 'tagname'
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.dropOnEmpty */
|
||||
dropOnEmpty: false,
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.tree */
|
||||
tree: false,
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.treeTag */
|
||||
treeTag: 'ul',
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.overlap */
|
||||
overlap: 'vertical', // one of 'vertical', 'horizontal'
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.constraint */
|
||||
constraint: 'vertical', // one of 'vertical', 'horizontal', false
|
||||
// also takes array of elements (or ids); or false
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.containment */
|
||||
containment: [element],
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.handle */
|
||||
handle: false, // or a CSS class
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.only */
|
||||
only: false,
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.hoverclass */
|
||||
hoverclass: null,
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.ghosting */
|
||||
ghosting: false,
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.scroll */
|
||||
scroll: false,
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.scrollSensitivity */
|
||||
scrollSensitivity: 20,
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.scrollSpeed */
|
||||
scrollSpeed: 15,
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.format */
|
||||
format: /^[^_]*_(.*)$/,
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.onChange */
|
||||
onChange: MochiKit.Base.noop,
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.onUpdate */
|
||||
onUpdate: MochiKit.Base.noop,
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.accept */
|
||||
accept: null
|
||||
}, options);
|
||||
|
||||
// clear any old sortable with same element
|
||||
self.destroy(element);
|
||||
|
||||
// build options for the draggables
|
||||
var options_for_draggable = {
|
||||
revert: true,
|
||||
ghosting: options.ghosting,
|
||||
scroll: options.scroll,
|
||||
scrollSensitivity: options.scrollSensitivity,
|
||||
scrollSpeed: options.scrollSpeed,
|
||||
constraint: options.constraint,
|
||||
handle: options.handle
|
||||
};
|
||||
|
||||
if (options.starteffect) {
|
||||
options_for_draggable.starteffect = options.starteffect;
|
||||
}
|
||||
|
||||
if (options.reverteffect) {
|
||||
options_for_draggable.reverteffect = options.reverteffect;
|
||||
} else if (options.ghosting) {
|
||||
options_for_draggable.reverteffect = function (innerelement) {
|
||||
innerelement.style.top = 0;
|
||||
innerelement.style.left = 0;
|
||||
};
|
||||
}
|
||||
|
||||
if (options.endeffect) {
|
||||
options_for_draggable.endeffect = options.endeffect;
|
||||
}
|
||||
|
||||
if (options.zindex) {
|
||||
options_for_draggable.zindex = options.zindex;
|
||||
}
|
||||
|
||||
// build options for the droppables
|
||||
var options_for_droppable = {
|
||||
overlap: options.overlap,
|
||||
containment: options.containment,
|
||||
hoverclass: options.hoverclass,
|
||||
onhover: self.onHover,
|
||||
tree: options.tree,
|
||||
accept: options.accept
|
||||
}
|
||||
|
||||
var options_for_tree = {
|
||||
onhover: self.onEmptyHover,
|
||||
overlap: options.overlap,
|
||||
containment: options.containment,
|
||||
hoverclass: options.hoverclass,
|
||||
accept: options.accept
|
||||
}
|
||||
|
||||
// fix for gecko engine
|
||||
MochiKit.DOM.removeEmptyTextNodes(element);
|
||||
|
||||
options.draggables = [];
|
||||
options.droppables = [];
|
||||
|
||||
// drop on empty handling
|
||||
if (options.dropOnEmpty || options.tree) {
|
||||
new MochiKit.DragAndDrop.Droppable(element, options_for_tree);
|
||||
options.droppables.push(element);
|
||||
}
|
||||
MochiKit.Base.map(function (e) {
|
||||
// handles are per-draggable
|
||||
var handle = options.handle ?
|
||||
MochiKit.DOM.getFirstElementByTagAndClassName(null,
|
||||
options.handle, e) : e;
|
||||
options.draggables.push(
|
||||
new MochiKit.DragAndDrop.Draggable(e,
|
||||
MochiKit.Base.update(options_for_draggable,
|
||||
{handle: handle})));
|
||||
new MochiKit.DragAndDrop.Droppable(e, options_for_droppable);
|
||||
if (options.tree) {
|
||||
e.treeNode = element;
|
||||
}
|
||||
options.droppables.push(e);
|
||||
}, (self.findElements(element, options) || []));
|
||||
|
||||
if (options.tree) {
|
||||
MochiKit.Base.map(function (e) {
|
||||
new MochiKit.DragAndDrop.Droppable(e, options_for_tree);
|
||||
e.treeNode = element;
|
||||
options.droppables.push(e);
|
||||
}, (self.findTreeElements(element, options) || []));
|
||||
}
|
||||
|
||||
// keep reference
|
||||
self.sortables[element.id] = options;
|
||||
|
||||
options.lastValue = self.serialize(element);
|
||||
options.startHandle = MochiKit.Signal.connect(MochiKit.DragAndDrop.Draggables, 'start',
|
||||
MochiKit.Base.partial(self.onStart, element));
|
||||
options.endHandle = MochiKit.Signal.connect(MochiKit.DragAndDrop.Draggables, 'end',
|
||||
MochiKit.Base.partial(self.onEnd, element));
|
||||
},
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.onStart */
|
||||
onStart: function (element, draggable) {
|
||||
var self = MochiKit.Sortable.Sortable;
|
||||
var options = self.options(element);
|
||||
options.lastValue = self.serialize(options.element);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.onEnd */
|
||||
onEnd: function (element, draggable) {
|
||||
var self = MochiKit.Sortable.Sortable;
|
||||
self.unmark();
|
||||
var options = self.options(element);
|
||||
if (options.lastValue != self.serialize(options.element)) {
|
||||
options.onUpdate(options.element);
|
||||
}
|
||||
},
|
||||
|
||||
// return all suitable-for-sortable elements in a guaranteed order
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.findElements */
|
||||
findElements: function (element, options) {
|
||||
return MochiKit.Sortable.Sortable.findChildren(
|
||||
element, options.only, options.tree ? true : false, options.tag);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.findTreeElements */
|
||||
findTreeElements: function (element, options) {
|
||||
return MochiKit.Sortable.Sortable.findChildren(
|
||||
element, options.only, options.tree ? true : false, options.treeTag);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.findChildren */
|
||||
findChildren: function (element, only, recursive, tagName) {
|
||||
if (!element.hasChildNodes()) {
|
||||
return null;
|
||||
}
|
||||
tagName = tagName.toUpperCase();
|
||||
if (only) {
|
||||
only = MochiKit.Base.flattenArray([only]);
|
||||
}
|
||||
var elements = [];
|
||||
MochiKit.Base.map(function (e) {
|
||||
if (e.tagName &&
|
||||
e.tagName.toUpperCase() == tagName &&
|
||||
(!only ||
|
||||
MochiKit.Iter.some(only, function (c) {
|
||||
return MochiKit.DOM.hasElementClass(e, c);
|
||||
}))) {
|
||||
elements.push(e);
|
||||
}
|
||||
if (recursive) {
|
||||
var grandchildren = MochiKit.Sortable.Sortable.findChildren(e, only, recursive, tagName);
|
||||
if (grandchildren && grandchildren.length > 0) {
|
||||
elements = elements.concat(grandchildren);
|
||||
}
|
||||
}
|
||||
}, element.childNodes);
|
||||
return elements;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.onHover */
|
||||
onHover: function (element, dropon, overlap) {
|
||||
if (MochiKit.DOM.isParent(dropon, element)) {
|
||||
return;
|
||||
}
|
||||
var self = MochiKit.Sortable.Sortable;
|
||||
|
||||
if (overlap > .33 && overlap < .66 && self.options(dropon).tree) {
|
||||
return;
|
||||
} else if (overlap > 0.5) {
|
||||
self.mark(dropon, 'before');
|
||||
if (dropon.previousSibling != element) {
|
||||
var oldParentNode = element.parentNode;
|
||||
element.style.visibility = 'hidden'; // fix gecko rendering
|
||||
dropon.parentNode.insertBefore(element, dropon);
|
||||
if (dropon.parentNode != oldParentNode) {
|
||||
self.options(oldParentNode).onChange(element);
|
||||
}
|
||||
self.options(dropon.parentNode).onChange(element);
|
||||
}
|
||||
} else {
|
||||
self.mark(dropon, 'after');
|
||||
var nextElement = dropon.nextSibling || null;
|
||||
if (nextElement != element) {
|
||||
var oldParentNode = element.parentNode;
|
||||
element.style.visibility = 'hidden'; // fix gecko rendering
|
||||
dropon.parentNode.insertBefore(element, nextElement);
|
||||
if (dropon.parentNode != oldParentNode) {
|
||||
self.options(oldParentNode).onChange(element);
|
||||
}
|
||||
self.options(dropon.parentNode).onChange(element);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_offsetSize: function (element, type) {
|
||||
if (type == 'vertical' || type == 'height') {
|
||||
return element.offsetHeight;
|
||||
} else {
|
||||
return element.offsetWidth;
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.onEmptyHover */
|
||||
onEmptyHover: function (element, dropon, overlap) {
|
||||
var oldParentNode = element.parentNode;
|
||||
var self = MochiKit.Sortable.Sortable;
|
||||
var droponOptions = self.options(dropon);
|
||||
|
||||
if (!MochiKit.DOM.isParent(dropon, element)) {
|
||||
var index;
|
||||
|
||||
var children = self.findElements(dropon, {tag: droponOptions.tag,
|
||||
only: droponOptions.only});
|
||||
var child = null;
|
||||
|
||||
if (children) {
|
||||
var offset = self._offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
|
||||
|
||||
for (index = 0; index < children.length; index += 1) {
|
||||
if (offset - self._offsetSize(children[index], droponOptions.overlap) >= 0) {
|
||||
offset -= self._offsetSize(children[index], droponOptions.overlap);
|
||||
} else if (offset - (self._offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
|
||||
child = index + 1 < children.length ? children[index + 1] : null;
|
||||
break;
|
||||
} else {
|
||||
child = children[index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dropon.insertBefore(element, child);
|
||||
|
||||
self.options(oldParentNode).onChange(element);
|
||||
droponOptions.onChange(element);
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.unmark */
|
||||
unmark: function () {
|
||||
var m = MochiKit.Sortable.Sortable._marker;
|
||||
if (m) {
|
||||
MochiKit.Style.hideElement(m);
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.mark */
|
||||
mark: function (dropon, position) {
|
||||
// mark on ghosting only
|
||||
var d = MochiKit.DOM;
|
||||
var self = MochiKit.Sortable.Sortable;
|
||||
var sortable = self.options(dropon.parentNode);
|
||||
if (sortable && !sortable.ghosting) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self._marker) {
|
||||
self._marker = d.getElement('dropmarker') ||
|
||||
document.createElement('DIV');
|
||||
MochiKit.Style.hideElement(self._marker);
|
||||
d.addElementClass(self._marker, 'dropmarker');
|
||||
self._marker.style.position = 'absolute';
|
||||
document.getElementsByTagName('body').item(0).appendChild(self._marker);
|
||||
}
|
||||
var offsets = MochiKit.Position.cumulativeOffset(dropon);
|
||||
self._marker.style.left = offsets.x + 'px';
|
||||
self._marker.style.top = offsets.y + 'px';
|
||||
|
||||
if (position == 'after') {
|
||||
if (sortable.overlap == 'horizontal') {
|
||||
self._marker.style.left = (offsets.x + dropon.clientWidth) + 'px';
|
||||
} else {
|
||||
self._marker.style.top = (offsets.y + dropon.clientHeight) + 'px';
|
||||
}
|
||||
}
|
||||
MochiKit.Style.showElement(self._marker);
|
||||
},
|
||||
|
||||
_tree: function (element, options, parent) {
|
||||
var self = MochiKit.Sortable.Sortable;
|
||||
var children = self.findElements(element, options) || [];
|
||||
|
||||
for (var i = 0; i < children.length; ++i) {
|
||||
var match = children[i].id.match(options.format);
|
||||
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var child = {
|
||||
id: encodeURIComponent(match ? match[1] : null),
|
||||
element: element,
|
||||
parent: parent,
|
||||
children: [],
|
||||
position: parent.children.length,
|
||||
container: self._findChildrenElement(children[i], options.treeTag.toUpperCase())
|
||||
}
|
||||
|
||||
/* Get the element containing the children and recurse over it */
|
||||
if (child.container) {
|
||||
self._tree(child.container, options, child)
|
||||
}
|
||||
|
||||
parent.children.push (child);
|
||||
}
|
||||
|
||||
return parent;
|
||||
},
|
||||
|
||||
/* Finds the first element of the given tag type within a parent element.
|
||||
Used for finding the first LI[ST] within a L[IST]I[TEM].*/
|
||||
_findChildrenElement: function (element, containerTag) {
|
||||
if (element && element.hasChildNodes) {
|
||||
containerTag = containerTag.toUpperCase();
|
||||
for (var i = 0; i < element.childNodes.length; ++i) {
|
||||
if (element.childNodes[i].tagName.toUpperCase() == containerTag) {
|
||||
return element.childNodes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.tree */
|
||||
tree: function (element, options) {
|
||||
element = MochiKit.DOM.getElement(element);
|
||||
var sortableOptions = MochiKit.Sortable.Sortable.options(element);
|
||||
options = MochiKit.Base.update({
|
||||
tag: sortableOptions.tag,
|
||||
treeTag: sortableOptions.treeTag,
|
||||
only: sortableOptions.only,
|
||||
name: element.id,
|
||||
format: sortableOptions.format
|
||||
}, options || {});
|
||||
|
||||
var root = {
|
||||
id: null,
|
||||
parent: null,
|
||||
children: new Array,
|
||||
container: element,
|
||||
position: 0
|
||||
}
|
||||
|
||||
return MochiKit.Sortable.Sortable._tree(element, options, root);
|
||||
},
|
||||
|
||||
/**
|
||||
* Specifies the sequence for the Sortable.
|
||||
* @param {Node} element Element to use as the Sortable.
|
||||
* @param {Object} newSequence New sequence to use.
|
||||
* @param {Object} options Options to use fro the Sortable.
|
||||
*/
|
||||
setSequence: function (element, newSequence, options) {
|
||||
var self = MochiKit.Sortable.Sortable;
|
||||
var b = MochiKit.Base;
|
||||
element = MochiKit.DOM.getElement(element);
|
||||
options = b.update(self.options(element), options || {});
|
||||
|
||||
var nodeMap = {};
|
||||
b.map(function (n) {
|
||||
var m = n.id.match(options.format);
|
||||
if (m) {
|
||||
nodeMap[m[1]] = [n, n.parentNode];
|
||||
}
|
||||
n.parentNode.removeChild(n);
|
||||
}, self.findElements(element, options));
|
||||
|
||||
b.map(function (ident) {
|
||||
var n = nodeMap[ident];
|
||||
if (n) {
|
||||
n[1].appendChild(n[0]);
|
||||
delete nodeMap[ident];
|
||||
}
|
||||
}, newSequence);
|
||||
},
|
||||
|
||||
/* Construct a [i] index for a particular node */
|
||||
_constructIndex: function (node) {
|
||||
var index = '';
|
||||
do {
|
||||
if (node.id) {
|
||||
index = '[' + node.position + ']' + index;
|
||||
}
|
||||
} while ((node = node.parent) != null);
|
||||
return index;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Sortable.Sortable.sequence */
|
||||
sequence: function (element, options) {
|
||||
element = MochiKit.DOM.getElement(element);
|
||||
var self = MochiKit.Sortable.Sortable;
|
||||
var options = MochiKit.Base.update(self.options(element), options || {});
|
||||
|
||||
return MochiKit.Base.map(function (item) {
|
||||
return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
|
||||
}, MochiKit.DOM.getElement(self.findElements(element, options) || []));
|
||||
},
|
||||
|
||||
/**
|
||||
* Serializes the content of a Sortable. Useful to send this content through a XMLHTTPRequest.
|
||||
* These options override the Sortable options for the serialization only.
|
||||
* @param {Node} element Element to serialize.
|
||||
* @param {Object} options Serialization options.
|
||||
*/
|
||||
serialize: function (element, options) {
|
||||
element = MochiKit.DOM.getElement(element);
|
||||
var self = MochiKit.Sortable.Sortable;
|
||||
options = MochiKit.Base.update(self.options(element), options || {});
|
||||
var name = encodeURIComponent(options.name || element.id);
|
||||
|
||||
if (options.tree) {
|
||||
return MochiKit.Base.flattenArray(MochiKit.Base.map(function (item) {
|
||||
return [name + self._constructIndex(item) + "[id]=" +
|
||||
encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
|
||||
}, self.tree(element, options).children)).join('&');
|
||||
} else {
|
||||
return MochiKit.Base.map(function (item) {
|
||||
return name + "[]=" + encodeURIComponent(item);
|
||||
}, self.sequence(element, options)).join('&');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,475 @@
|
|||
/***
|
||||
|
||||
MochiKit.Style 1.4
|
||||
|
||||
See <http://mochikit.com/> for documentation, downloads, license, etc.
|
||||
|
||||
(c) 2005-2006 Bob Ippolito, Beau Hartshorne. All rights Reserved.
|
||||
|
||||
***/
|
||||
|
||||
if (typeof(dojo) != 'undefined') {
|
||||
dojo.provide('MochiKit.Style');
|
||||
dojo.require('MochiKit.Base');
|
||||
dojo.require('MochiKit.DOM');
|
||||
}
|
||||
if (typeof(JSAN) != 'undefined') {
|
||||
JSAN.use('MochiKit.Base', []);
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof(MochiKit.Base) == 'undefined') {
|
||||
throw '';
|
||||
}
|
||||
} catch (e) {
|
||||
throw 'MochiKit.Style depends on MochiKit.Base!';
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof(MochiKit.DOM) == 'undefined') {
|
||||
throw '';
|
||||
}
|
||||
} catch (e) {
|
||||
throw 'MochiKit.Style depends on MochiKit.DOM!';
|
||||
}
|
||||
|
||||
|
||||
if (typeof(MochiKit.Style) == 'undefined') {
|
||||
MochiKit.Style = {};
|
||||
}
|
||||
|
||||
MochiKit.Style.NAME = 'MochiKit.Style';
|
||||
MochiKit.Style.VERSION = '1.4';
|
||||
MochiKit.Style.__repr__ = function () {
|
||||
return '[' + this.NAME + ' ' + this.VERSION + ']';
|
||||
};
|
||||
MochiKit.Style.toString = function () {
|
||||
return this.__repr__();
|
||||
};
|
||||
|
||||
MochiKit.Style.EXPORT_OK = [];
|
||||
|
||||
MochiKit.Style.EXPORT = [
|
||||
'setOpacity',
|
||||
'getOpacity',
|
||||
'setStyle',
|
||||
'getStyle', // temporary
|
||||
'computedStyle',
|
||||
'getElementDimensions',
|
||||
'elementDimensions', // deprecated
|
||||
'setElementDimensions',
|
||||
'getElementPosition',
|
||||
'elementPosition', // deprecated
|
||||
'setElementPosition',
|
||||
'setDisplayForElement',
|
||||
'hideElement',
|
||||
'showElement',
|
||||
'getViewportDimensions',
|
||||
'getViewportPosition',
|
||||
'Dimensions',
|
||||
'Coordinates'
|
||||
];
|
||||
|
||||
|
||||
/*
|
||||
|
||||
Dimensions
|
||||
|
||||
*/
|
||||
/** @id MochiKit.Style.Dimensions */
|
||||
MochiKit.Style.Dimensions = function (w, h) {
|
||||
this.w = w;
|
||||
this.h = h;
|
||||
};
|
||||
|
||||
MochiKit.Style.Dimensions.prototype.__repr__ = function () {
|
||||
var repr = MochiKit.Base.repr;
|
||||
return '{w: ' + repr(this.w) + ', h: ' + repr(this.h) + '}';
|
||||
};
|
||||
|
||||
MochiKit.Style.Dimensions.prototype.toString = function () {
|
||||
return this.__repr__();
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
|
||||
Coordinates
|
||||
|
||||
*/
|
||||
/** @id MochiKit.Style.Coordinates */
|
||||
MochiKit.Style.Coordinates = function (x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
};
|
||||
|
||||
MochiKit.Style.Coordinates.prototype.__repr__ = function () {
|
||||
var repr = MochiKit.Base.repr;
|
||||
return '{x: ' + repr(this.x) + ', y: ' + repr(this.y) + '}';
|
||||
};
|
||||
|
||||
MochiKit.Style.Coordinates.prototype.toString = function () {
|
||||
return this.__repr__();
|
||||
};
|
||||
|
||||
|
||||
MochiKit.Base.update(MochiKit.Style, {
|
||||
|
||||
/** @id MochiKit.Style.computedStyle */
|
||||
computedStyle: function (elem, cssProperty) {
|
||||
var dom = MochiKit.DOM;
|
||||
var d = dom._document;
|
||||
|
||||
elem = dom.getElement(elem);
|
||||
cssProperty = MochiKit.Base.camelize(cssProperty);
|
||||
|
||||
if (!elem || elem == d) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/* from YUI 0.10.0 */
|
||||
if (cssProperty == 'opacity' && elem.filters) { // IE opacity
|
||||
try {
|
||||
return elem.filters.item('DXImageTransform.Microsoft.Alpha'
|
||||
).opacity / 100;
|
||||
} catch(e) {
|
||||
try {
|
||||
return elem.filters.item('alpha').opacity / 100;
|
||||
} catch(e) {}
|
||||
}
|
||||
}
|
||||
|
||||
if (elem.currentStyle) {
|
||||
return elem.currentStyle[cssProperty];
|
||||
}
|
||||
if (typeof(d.defaultView) == 'undefined') {
|
||||
return undefined;
|
||||
}
|
||||
if (d.defaultView === null) {
|
||||
return undefined;
|
||||
}
|
||||
var style = d.defaultView.getComputedStyle(elem, null);
|
||||
if (typeof(style) == 'undefined' || style === null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var selectorCase = cssProperty.replace(/([A-Z])/g, '-$1'
|
||||
).toLowerCase(); // from dojo.style.toSelectorCase
|
||||
|
||||
return style.getPropertyValue(selectorCase);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Style.getStyle */
|
||||
getStyle: function (elem, style) {
|
||||
elem = MochiKit.DOM.getElement(elem);
|
||||
var value = elem.style[MochiKit.Base.camelize(style)];
|
||||
if (!value) {
|
||||
if (document.defaultView && document.defaultView.getComputedStyle) {
|
||||
var css = document.defaultView.getComputedStyle(elem, null);
|
||||
value = css ? css.getPropertyValue(style) : null;
|
||||
} else if (elem.currentStyle) {
|
||||
value = elem.currentStyle[MochiKit.Base.camelize(style)];
|
||||
}
|
||||
}
|
||||
|
||||
if (/Opera/.test(navigator.userAgent) && (MochiKit.Base.find(['left', 'top', 'right', 'bottom'], style) != -1)) {
|
||||
if (MochiKit.Style.getStyle(elem, 'position') == 'static') {
|
||||
value = 'auto';
|
||||
}
|
||||
}
|
||||
|
||||
return value == 'auto' ? null : value;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Style.setStyle */
|
||||
setStyle: function (elem, style) {
|
||||
elem = MochiKit.DOM.getElement(elem);
|
||||
for (name in style) {
|
||||
elem.style[MochiKit.Base.camelize(name)] = style[name];
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Style.getOpacity */
|
||||
getOpacity: function (elem) {
|
||||
var opacity;
|
||||
if (opacity = MochiKit.Style.getStyle(elem, 'opacity')) {
|
||||
return parseFloat(opacity);
|
||||
}
|
||||
if (opacity = (MochiKit.Style.getStyle(elem, 'filter') || '').match(/alpha\(opacity=(.*)\)/)) {
|
||||
if (opacity[1]) {
|
||||
return parseFloat(opacity[1]) / 100;
|
||||
}
|
||||
}
|
||||
return 1.0;
|
||||
},
|
||||
/** @id MochiKit.Style.setOpacity */
|
||||
setOpacity: function(elem, o) {
|
||||
elem = MochiKit.DOM.getElement(elem);
|
||||
var self = MochiKit.Style;
|
||||
if (o == 1) {
|
||||
var toSet = /Gecko/.test(navigator.userAgent) && !(/Konqueror|Safari|KHTML/.test(navigator.userAgent));
|
||||
self.setStyle(elem, {opacity: toSet ? 0.999999 : 1.0});
|
||||
if (/MSIE/.test(navigator.userAgent)) {
|
||||
self.setStyle(elem, {filter:
|
||||
self.getStyle(elem, 'filter').replace(/alpha\([^\)]*\)/gi, '')});
|
||||
}
|
||||
} else {
|
||||
if (o < 0.00001) {
|
||||
o = 0;
|
||||
}
|
||||
self.setStyle(elem, {opacity: o});
|
||||
if (/MSIE/.test(navigator.userAgent)) {
|
||||
self.setStyle(elem,
|
||||
{filter: self.getStyle(elem, 'filter').replace(/alpha\([^\)]*\)/gi, '') + 'alpha(opacity=' + o * 100 + ')' });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
|
||||
getElementPosition is adapted from YAHOO.util.Dom.getXY v0.9.0.
|
||||
Copyright: Copyright (c) 2006, Yahoo! Inc. All rights reserved.
|
||||
License: BSD, http://developer.yahoo.net/yui/license.txt
|
||||
|
||||
*/
|
||||
|
||||
/** @id MochiKit.Style.getElementPosition */
|
||||
getElementPosition: function (elem, /* optional */relativeTo) {
|
||||
var self = MochiKit.Style;
|
||||
var dom = MochiKit.DOM;
|
||||
elem = dom.getElement(elem);
|
||||
|
||||
if (!elem ||
|
||||
(!(elem.x && elem.y) &&
|
||||
(!elem.parentNode == null ||
|
||||
self.computedStyle(elem, 'display') == 'none'))) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var c = new self.Coordinates(0, 0);
|
||||
var box = null;
|
||||
var parent = null;
|
||||
|
||||
var d = MochiKit.DOM._document;
|
||||
var de = d.documentElement;
|
||||
var b = d.body;
|
||||
|
||||
if (!elem.parentNode && elem.x && elem.y) {
|
||||
/* it's just a MochiKit.Style.Coordinates object */
|
||||
c.x += elem.x || 0;
|
||||
c.y += elem.y || 0;
|
||||
} else if (elem.getBoundingClientRect) { // IE shortcut
|
||||
/*
|
||||
|
||||
The IE shortcut can be off by two. We fix it. See:
|
||||
http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/getboundingclientrect.asp
|
||||
|
||||
This is similar to the method used in
|
||||
MochiKit.Signal.Event.mouse().
|
||||
|
||||
*/
|
||||
box = elem.getBoundingClientRect();
|
||||
|
||||
c.x += box.left +
|
||||
(de.scrollLeft || b.scrollLeft) -
|
||||
(de.clientLeft || 0);
|
||||
|
||||
c.y += box.top +
|
||||
(de.scrollTop || b.scrollTop) -
|
||||
(de.clientTop || 0);
|
||||
|
||||
} else if (elem.offsetParent) {
|
||||
c.x += elem.offsetLeft;
|
||||
c.y += elem.offsetTop;
|
||||
parent = elem.offsetParent;
|
||||
|
||||
if (parent != elem) {
|
||||
while (parent) {
|
||||
c.x += parent.offsetLeft;
|
||||
c.y += parent.offsetTop;
|
||||
parent = parent.offsetParent;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Opera < 9 and old Safari (absolute) incorrectly account for
|
||||
body offsetTop and offsetLeft.
|
||||
|
||||
*/
|
||||
var ua = navigator.userAgent.toLowerCase();
|
||||
if ((typeof(opera) != 'undefined' &&
|
||||
parseFloat(opera.version()) < 9) ||
|
||||
(ua.indexOf('safari') != -1 &&
|
||||
self.computedStyle(elem, 'position') == 'absolute')) {
|
||||
|
||||
c.x -= b.offsetLeft;
|
||||
c.y -= b.offsetTop;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof(relativeTo) != 'undefined') {
|
||||
relativeTo = arguments.callee(relativeTo);
|
||||
if (relativeTo) {
|
||||
c.x -= (relativeTo.x || 0);
|
||||
c.y -= (relativeTo.y || 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (elem.parentNode) {
|
||||
parent = elem.parentNode;
|
||||
} else {
|
||||
parent = null;
|
||||
}
|
||||
|
||||
while (parent) {
|
||||
var tagName = parent.tagName.toUpperCase();
|
||||
if (tagName === 'BODY' || tagName === 'HTML') {
|
||||
break;
|
||||
}
|
||||
c.x -= parent.scrollLeft;
|
||||
c.y -= parent.scrollTop;
|
||||
if (parent.parentNode) {
|
||||
parent = parent.parentNode;
|
||||
} else {
|
||||
parent = null;
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Style.setElementPosition */
|
||||
setElementPosition: function (elem, newPos/* optional */, units) {
|
||||
elem = MochiKit.DOM.getElement(elem);
|
||||
if (typeof(units) == 'undefined') {
|
||||
units = 'px';
|
||||
}
|
||||
var newStyle = {};
|
||||
var isUndefNull = MochiKit.Base.isUndefinedOrNull;
|
||||
if (!isUndefNull(newPos.x)) {
|
||||
newStyle['left'] = newPos.x + units;
|
||||
}
|
||||
if (!isUndefNull(newPos.y)) {
|
||||
newStyle['top'] = newPos.y + units;
|
||||
}
|
||||
MochiKit.DOM.updateNodeAttributes(elem, {'style': newStyle});
|
||||
},
|
||||
|
||||
/** @id MochiKit.Style.getElementDimensions */
|
||||
getElementDimensions: function (elem) {
|
||||
var self = MochiKit.Style;
|
||||
var dom = MochiKit.DOM;
|
||||
if (typeof(elem.w) == 'number' || typeof(elem.h) == 'number') {
|
||||
return new self.Dimensions(elem.w || 0, elem.h || 0);
|
||||
}
|
||||
elem = dom.getElement(elem);
|
||||
if (!elem) {
|
||||
return undefined;
|
||||
}
|
||||
var disp = self.computedStyle(elem, 'display');
|
||||
// display can be empty/undefined on WebKit/KHTML
|
||||
if (disp != 'none' && disp != '' && typeof(disp) != 'undefined') {
|
||||
return new self.Dimensions(elem.offsetWidth || 0,
|
||||
elem.offsetHeight || 0);
|
||||
}
|
||||
var s = elem.style;
|
||||
var originalVisibility = s.visibility;
|
||||
var originalPosition = s.position;
|
||||
s.visibility = 'hidden';
|
||||
s.position = 'absolute';
|
||||
s.display = '';
|
||||
var originalWidth = elem.offsetWidth;
|
||||
var originalHeight = elem.offsetHeight;
|
||||
s.display = 'none';
|
||||
s.position = originalPosition;
|
||||
s.visibility = originalVisibility;
|
||||
return new self.Dimensions(originalWidth, originalHeight);
|
||||
},
|
||||
|
||||
/** @id MochiKit.Style.setElementDimensions */
|
||||
setElementDimensions: function (elem, newSize/* optional */, units) {
|
||||
elem = MochiKit.DOM.getElement(elem);
|
||||
if (typeof(units) == 'undefined') {
|
||||
units = 'px';
|
||||
}
|
||||
var newStyle = {};
|
||||
var isUndefNull = MochiKit.Base.isUndefinedOrNull;
|
||||
if (!isUndefNull(newSize.w)) {
|
||||
newStyle['width'] = newSize.w + units;
|
||||
}
|
||||
if (!isUndefNull(newSize.h)) {
|
||||
newStyle['height'] = newSize.h + units;
|
||||
}
|
||||
MochiKit.DOM.updateNodeAttributes(elem, {'style': newStyle});
|
||||
},
|
||||
|
||||
/** @id MochiKit.Style.setDisplayForElement */
|
||||
setDisplayForElement: function (display, element/*, ...*/) {
|
||||
var elements = MochiKit.Base.extend(null, arguments, 1);
|
||||
var getElement = MochiKit.DOM.getElement;
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
var element = getElement(elements[i]);
|
||||
if (element) {
|
||||
element.style.display = display;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/** @id MochiKit.Style.getViewportDimensions */
|
||||
getViewportDimensions: function () {
|
||||
var d = new MochiKit.Style.Dimensions();
|
||||
|
||||
var w = MochiKit.DOM._window;
|
||||
var b = MochiKit.DOM._document.body;
|
||||
|
||||
if (w.innerWidth) {
|
||||
d.w = w.innerWidth;
|
||||
d.h = w.innerHeight;
|
||||
} else if (b.parentElement.clientWidth) {
|
||||
d.w = b.parentElement.clientWidth;
|
||||
d.h = b.parentElement.clientHeight;
|
||||
} else if (b && b.clientWidth) {
|
||||
d.w = b.clientWidth;
|
||||
d.h = b.clientHeight;
|
||||
}
|
||||
return d;
|
||||
},
|
||||
|
||||
/** @id MochiKit.Style.getViewportPosition */
|
||||
getViewportPosition: function () {
|
||||
var c = new MochiKit.Style.Coordinates(0, 0);
|
||||
var d = MochiKit.DOM._document;
|
||||
var de = d.documentElement;
|
||||
var db = d.body;
|
||||
if (de && (de.scrollTop || de.scrollLeft)) {
|
||||
c.x = de.scrollLeft;
|
||||
c.y = de.scrollTop;
|
||||
} else if (db) {
|
||||
c.x = db.scrollLeft;
|
||||
c.y = db.scrollTop;
|
||||
}
|
||||
return c;
|
||||
},
|
||||
|
||||
__new__: function () {
|
||||
var m = MochiKit.Base;
|
||||
|
||||
this.elementPosition = this.getElementPosition;
|
||||
this.elementDimensions = this.getElementDimensions;
|
||||
|
||||
this.hideElement = m.partial(this.setDisplayForElement, 'none');
|
||||
this.showElement = m.partial(this.setDisplayForElement, 'block');
|
||||
|
||||
this.EXPORT_TAGS = {
|
||||
':common': this.EXPORT,
|
||||
':all': m.concat(this.EXPORT, this.EXPORT_OK)
|
||||
};
|
||||
|
||||
m.nameFunctions(this);
|
||||
}
|
||||
});
|
||||
|
||||
MochiKit.Style.__new__();
|
||||
MochiKit.Base._exportSymbols(this, MochiKit.Style);
|
|
@ -0,0 +1,181 @@
|
|||
/***
|
||||
|
||||
MochiKit.Test 1.4
|
||||
|
||||
See <http://mochikit.com/> for documentation, downloads, license, etc.
|
||||
|
||||
(c) 2005 Bob Ippolito. All rights Reserved.
|
||||
|
||||
***/
|
||||
|
||||
if (typeof(dojo) != 'undefined') {
|
||||
dojo.provide('MochiKit.Test');
|
||||
dojo.require('MochiKit.Base');
|
||||
}
|
||||
|
||||
if (typeof(JSAN) != 'undefined') {
|
||||
JSAN.use("MochiKit.Base", []);
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof(MochiKit.Base) == 'undefined') {
|
||||
throw "";
|
||||
}
|
||||
} catch (e) {
|
||||
throw "MochiKit.Test depends on MochiKit.Base!";
|
||||
}
|
||||
|
||||
if (typeof(MochiKit.Test) == 'undefined') {
|
||||
MochiKit.Test = {};
|
||||
}
|
||||
|
||||
MochiKit.Test.NAME = "MochiKit.Test";
|
||||
MochiKit.Test.VERSION = "1.4";
|
||||
MochiKit.Test.__repr__ = function () {
|
||||
return "[" + this.NAME + " " + this.VERSION + "]";
|
||||
};
|
||||
|
||||
MochiKit.Test.toString = function () {
|
||||
return this.__repr__();
|
||||
};
|
||||
|
||||
|
||||
MochiKit.Test.EXPORT = ["runTests"];
|
||||
MochiKit.Test.EXPORT_OK = [];
|
||||
|
||||
MochiKit.Test.runTests = function (obj) {
|
||||
if (typeof(obj) == "string") {
|
||||
obj = JSAN.use(obj);
|
||||
}
|
||||
var suite = new MochiKit.Test.Suite();
|
||||
suite.run(obj);
|
||||
};
|
||||
|
||||
MochiKit.Test.Suite = function () {
|
||||
this.testIndex = 0;
|
||||
MochiKit.Base.bindMethods(this);
|
||||
};
|
||||
|
||||
MochiKit.Test.Suite.prototype = {
|
||||
run: function (obj) {
|
||||
try {
|
||||
obj(this);
|
||||
} catch (e) {
|
||||
this.traceback(e);
|
||||
}
|
||||
},
|
||||
traceback: function (e) {
|
||||
var items = MochiKit.Iter.sorted(MochiKit.Base.items(e));
|
||||
print("not ok " + this.testIndex + " - Error thrown");
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var kv = items[i];
|
||||
if (kv[0] == "stack") {
|
||||
kv[1] = kv[1].split(/\n/)[0];
|
||||
}
|
||||
this.print("# " + kv.join(": "));
|
||||
}
|
||||
},
|
||||
print: function (s) {
|
||||
print(s);
|
||||
},
|
||||
is: function (got, expected, /* optional */message) {
|
||||
var res = 1;
|
||||
var msg = null;
|
||||
try {
|
||||
res = MochiKit.Base.compare(got, expected);
|
||||
} catch (e) {
|
||||
msg = "Can not compare " + typeof(got) + ":" + typeof(expected);
|
||||
}
|
||||
if (res) {
|
||||
msg = "Expected value did not compare equal";
|
||||
}
|
||||
if (!res) {
|
||||
return this.testResult(true, message);
|
||||
}
|
||||
return this.testResult(false, message,
|
||||
[[msg], ["got:", got], ["expected:", expected]]);
|
||||
},
|
||||
|
||||
testResult: function (pass, msg, failures) {
|
||||
this.testIndex += 1;
|
||||
if (pass) {
|
||||
this.print("ok " + this.testIndex + " - " + msg);
|
||||
return;
|
||||
}
|
||||
this.print("not ok " + this.testIndex + " - " + msg);
|
||||
if (failures) {
|
||||
for (var i = 0; i < failures.length; i++) {
|
||||
this.print("# " + failures[i].join(" "));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
isDeeply: function (got, expected, /* optional */message) {
|
||||
var m = MochiKit.Base;
|
||||
var res = 1;
|
||||
try {
|
||||
res = m.compare(got, expected);
|
||||
} catch (e) {
|
||||
// pass
|
||||
}
|
||||
if (res === 0) {
|
||||
return this.ok(true, message);
|
||||
}
|
||||
var gk = m.keys(got);
|
||||
var ek = m.keys(expected);
|
||||
gk.sort();
|
||||
ek.sort();
|
||||
if (m.compare(gk, ek)) {
|
||||
// differing keys
|
||||
var cmp = {};
|
||||
var i;
|
||||
for (i = 0; i < gk.length; i++) {
|
||||
cmp[gk[i]] = "got";
|
||||
}
|
||||
for (i = 0; i < ek.length; i++) {
|
||||
if (ek[i] in cmp) {
|
||||
delete cmp[ek[i]];
|
||||
} else {
|
||||
cmp[ek[i]] = "expected";
|
||||
}
|
||||
}
|
||||
var diffkeys = m.keys(cmp);
|
||||
diffkeys.sort();
|
||||
var gotkeys = [];
|
||||
var expkeys = [];
|
||||
while (diffkeys.length) {
|
||||
var k = diffkeys.shift();
|
||||
if (k in Object.prototype) {
|
||||
continue;
|
||||
}
|
||||
(cmp[k] == "got" ? gotkeys : expkeys).push(k);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return this.testResult((!res), msg,
|
||||
(msg ? [["got:", got], ["expected:", expected]] : undefined)
|
||||
);
|
||||
},
|
||||
|
||||
ok: function (res, message) {
|
||||
return this.testResult(res, message);
|
||||
}
|
||||
};
|
||||
|
||||
MochiKit.Test.__new__ = function () {
|
||||
var m = MochiKit.Base;
|
||||
|
||||
this.EXPORT_TAGS = {
|
||||
":common": this.EXPORT,
|
||||
":all": m.concat(this.EXPORT, this.EXPORT_OK)
|
||||
};
|
||||
|
||||
m.nameFunctions(this);
|
||||
|
||||
};
|
||||
|
||||
MochiKit.Test.__new__();
|
||||
|
||||
MochiKit.Base._exportSymbols(this, MochiKit.Test);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,19 @@
|
|||
dojo.hostenv.conditionalLoadModule({
|
||||
"common": [
|
||||
"MochiKit.Base",
|
||||
"MochiKit.Iter",
|
||||
"MochiKit.Logging",
|
||||
"MochiKit.DateTime",
|
||||
"MochiKit.Format",
|
||||
"MochiKit.Async",
|
||||
"MochiKit.Color"
|
||||
],
|
||||
"browser": [
|
||||
"MochiKit.DOM",
|
||||
"MochiKit.Style",
|
||||
"MochiKit.Signal",
|
||||
"MochiKit.LoggingPane",
|
||||
"MochiKit.Visual"
|
||||
]
|
||||
});
|
||||
dojo.hostenv.moduleLoaded("MochiKit.*");
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,113 @@
|
|||
----------------
|
||||
mochitest README
|
||||
----------------
|
||||
|
||||
Steps to get started:
|
||||
|
||||
1.) Run the runtests.pl script to start the server.
|
||||
|
||||
Currently, the test script automatically determines the location
|
||||
of *Firefox*.
|
||||
|
||||
2.) gen_template.pl will generate test templates for HTML, XUL, and XHTML.
|
||||
Read the comment at the top of the file for usage instructions.
|
||||
|
||||
3.) Write a test.
|
||||
|
||||
|
||||
Example test:
|
||||
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=345656
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 345656</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Bug 345656 **/
|
||||
//
|
||||
//add information to show on the test page
|
||||
//
|
||||
$("display").innerHTML = "doing stuff...";
|
||||
|
||||
//
|
||||
// The '$' is function is shorthand for getElementById. This is the same thing:
|
||||
//
|
||||
document.getElementById("display").innerHTML = "doing stuff...";
|
||||
|
||||
//
|
||||
// you can add content that you don't want to clutter
|
||||
// the display to the content div.
|
||||
//
|
||||
// You can write directly, or you can use MochiKit functions
|
||||
// to do it in JS like this:
|
||||
//
|
||||
appendChildNodes($("content"),
|
||||
DIV({class: "qux"},
|
||||
SPAN({id: "span42"}, "content"))
|
||||
);
|
||||
|
||||
//
|
||||
// the ok() function is like assert
|
||||
//
|
||||
ok(true, "checking to see if true is true);
|
||||
|
||||
//
|
||||
// this will fail
|
||||
//
|
||||
ok(1==2, "1 equals 2?");
|
||||
|
||||
|
||||
//
|
||||
// this will be marked as a todo.
|
||||
// When we fix 1 so it equals 2, we'll need to change this
|
||||
// function to ok() or is().
|
||||
//
|
||||
todo(1==2, "1 equals 2?");
|
||||
|
||||
//
|
||||
// is() takes two args
|
||||
//
|
||||
myVar = "foo";
|
||||
is(myVar, "foo", "checking to see if myVar is 'foo'");
|
||||
|
||||
//
|
||||
// Tests can run in event handlers.
|
||||
// Call this to tell SimpleTest to wait for SimpleTest.finish()
|
||||
//
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
//
|
||||
// event handler:
|
||||
//
|
||||
function event_fired(ev) {
|
||||
is(ev.newValue, "width: auto;", "DOMAttrModified event reports correct newValue");
|
||||
SimpleTest.finish(); // trigger the end of our test sequence
|
||||
}
|
||||
|
||||
//
|
||||
// Hook up the event. Mochikit.Signal has many conveniences for this, if you want.
|
||||
//
|
||||
$("content").addEventListener("DOMAttrModified", event_fired, false);
|
||||
|
||||
//
|
||||
// Fire the event.
|
||||
//
|
||||
$("content").style.width = "auto";
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,327 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<!-- ***** BEGIN LICENSE BLOCK *****
|
||||
- Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
-
|
||||
- The contents of this file are subject to the Mozilla Public License Version
|
||||
- 1.1 (the "License"); you may not use this file except in compliance with
|
||||
- the License. You may obtain a copy of the License at
|
||||
- http://www.mozilla.org/MPL/
|
||||
-
|
||||
- Software distributed under the License is distributed on an "AS IS" basis,
|
||||
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
- for the specific language governing rights and limitations under the
|
||||
- License.
|
||||
-
|
||||
- The Original Code is Browser Test Harness.
|
||||
-
|
||||
- The Initial Developer of the Original Code is
|
||||
- Mozilla Corporation.
|
||||
-
|
||||
- Portions created by the Initial Developer are Copyright (C) 2007
|
||||
- the Initial Developer. All Rights Reserved.
|
||||
-
|
||||
- Contributor(s):
|
||||
- Gavin Sharp <gavin@gavinsharp.com> (original author)
|
||||
- Ehsan Akhgari <ehsan.akhgari@gmail.com>
|
||||
-
|
||||
- Alternatively, the contents of this file may be used under the terms of
|
||||
- either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
- in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
- of those above. If you wish to allow use of your version of this file only
|
||||
- under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
- use your version of this file under the terms of the MPL, indicate your
|
||||
- decision by deleting the provisions above and replace them with the notice
|
||||
- and other provisions required by the LGPL or the GPL. If you do not delete
|
||||
- the provisions above, a recipient may use your version of this file under
|
||||
- the terms of any one of the MPL, the GPL or the LGPL.
|
||||
-
|
||||
- ***** END LICENSE BLOCK ***** -->
|
||||
|
||||
<window id="browserTestHarness"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="TestStart();"
|
||||
title="Browser chrome tests"
|
||||
width="1024">
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/MozillaFileLogger.js"/>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/quit.js"/>
|
||||
<style xmlns="http://www.w3.org/1999/xhtml"><![CDATA[
|
||||
#results {
|
||||
margin: 5px;
|
||||
background-color: window;
|
||||
-moz-user-select: text;
|
||||
}
|
||||
|
||||
#summary {
|
||||
color: white;
|
||||
border: 2px solid black;
|
||||
}
|
||||
|
||||
#summary.success {
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
#summary.failure {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
.failed {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.testHeader {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0.1em;
|
||||
}
|
||||
]]></style>
|
||||
<script type="application/javascript;version=1.7"><![CDATA[
|
||||
var gConfig;
|
||||
function TestStart() {
|
||||
gConfig = readConfig();
|
||||
|
||||
// If MochiTest was started with the --test-path flag specifying a subset
|
||||
// of tests to run, put that path in the label of the "Run Tests" button
|
||||
// so the tester knows which tests will run when they press that button.
|
||||
if (gConfig.testPath)
|
||||
document.getElementById("runTestsButton").label =
|
||||
"Run " + gConfig.testPath + " tests";
|
||||
|
||||
if (gConfig.autoRun)
|
||||
setTimeout(runAllTests, 0);
|
||||
}
|
||||
|
||||
function readConfig() {
|
||||
var fileLocator = Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(Ci.nsIProperties);
|
||||
var configFile = fileLocator.get("ProfD", Ci.nsIFile);
|
||||
configFile.append("testConfig.js");
|
||||
|
||||
if (!configFile.exists())
|
||||
return;
|
||||
|
||||
var fileInStream = Cc["@mozilla.org/network/file-input-stream;1"].
|
||||
createInstance(Ci.nsIFileInputStream);
|
||||
var sstream = Cc["@mozilla.org/scriptableinputstream;1"].
|
||||
createInstance(Ci.nsIScriptableInputStream);
|
||||
fileInStream.init(configFile, -1, 0, 0);
|
||||
sstream.init(fileInStream);
|
||||
|
||||
var config = "";
|
||||
var str = sstream.read(4096);
|
||||
while (str.length > 0) {
|
||||
config += str;
|
||||
str = sstream.read(4096);
|
||||
}
|
||||
|
||||
sstream.close();
|
||||
fileInStream.close();
|
||||
|
||||
return eval(config);
|
||||
}
|
||||
|
||||
function getChromeDir() {
|
||||
const Cc = Components.classes; const Ci = Components.interfaces;
|
||||
|
||||
/** Find our chrome dir **/
|
||||
var ios = Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
var chromeURI = ios.newURI("chrome://mochikit/content/",
|
||||
null, null);
|
||||
var resolvedURI = Cc["@mozilla.org/chrome/chrome-registry;1"].
|
||||
getService(Ci.nsIChromeRegistry).
|
||||
convertChromeURL(chromeURI);
|
||||
var fileHandler = Cc["@mozilla.org/network/protocol;1?name=file"].
|
||||
getService(Ci.nsIFileProtocolHandler);
|
||||
var chromeDir = fileHandler.getFileFromURLSpec(resolvedURI.spec);
|
||||
|
||||
return chromeDir.parent.QueryInterface(Ci.nsILocalFile);
|
||||
}
|
||||
|
||||
function browserTestFile(aTestFile) {
|
||||
this.path = aTestFile;
|
||||
this.tests = [];
|
||||
this.scope = null;
|
||||
}
|
||||
browserTestFile.prototype = {
|
||||
get passCount() {
|
||||
return this.tests.filter(function (t) !t.todo && t.pass).length;
|
||||
},
|
||||
get todoCount() {
|
||||
return this.tests.filter(function (t) t.todo && t.pass).length;
|
||||
},
|
||||
get failCount() {
|
||||
return this.tests.filter(function (t) !t.pass).length;
|
||||
},
|
||||
get log() {
|
||||
var path = this.path;
|
||||
return this.tests.map(function (t) {
|
||||
if (!t.pass)
|
||||
return t.msg + " - " + path;
|
||||
return t.msg;
|
||||
}).join("\n");
|
||||
},
|
||||
get htmlLog() {
|
||||
function _entityEncode(str) {
|
||||
return Cc["@mozilla.org/txttohtmlconv;1"].
|
||||
getService(Ci.mozITXTToHTMLConv).
|
||||
scanTXT(str, Ci.mozITXTToHTMLConv.kEntities);
|
||||
}
|
||||
var path = _entityEncode( this.path );
|
||||
return this.tests.map(function (t) {
|
||||
var result = "<p class=\"result ";
|
||||
result += t.pass ? "passed" : "failed";
|
||||
result += "\">" + t.result + " | " + path +
|
||||
" | " + _entityEncode( t.msg ) + "</p>";
|
||||
return result;
|
||||
}).join("\n");
|
||||
}
|
||||
};
|
||||
|
||||
// Returns an array of chrome:// URLs to all the test files
|
||||
function listTests() {
|
||||
const Cc = Components.classes; const Ci = Components.interfaces;
|
||||
|
||||
var ioSvc = Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
|
||||
var testsDir = getChromeDir();
|
||||
testsDir.appendRelativePath("browser");
|
||||
|
||||
var requestPath = "chrome://mochikit/content/browser";
|
||||
var fileNameRegexp = /browser_.+\.js$/;
|
||||
|
||||
if (gConfig.testPath) {
|
||||
var testsDirURI = ioSvc.newFileURI(testsDir);
|
||||
testsDir = ioSvc.newURI(gConfig.testPath, null, testsDirURI)
|
||||
.QueryInterface(Ci.nsIFileURL).file;
|
||||
|
||||
// Invalid testPath...
|
||||
if (!testsDir.exists())
|
||||
return [];
|
||||
|
||||
// If we were passed a specific file, run only that test.
|
||||
if (testsDir.isFile()) {
|
||||
if (fileNameRegexp.test(testsDir.leafName))
|
||||
return [new browserTestFile(requestPath + "/" + gConfig.testPath)];
|
||||
|
||||
// We were passed a file that's not a test...
|
||||
return [];
|
||||
}
|
||||
|
||||
// otherwise, we were passed a directory of tests
|
||||
requestPath += "/" + gConfig.testPath;
|
||||
}
|
||||
|
||||
// load server.js in so we can share template functions
|
||||
var scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Ci.mozIJSSubScriptLoader);
|
||||
var srvScope = {};
|
||||
scriptLoader.loadSubScript("chrome://mochikit/content/server.js", srvScope);
|
||||
|
||||
var [links, ] = srvScope.list(requestPath, testsDir, true);
|
||||
var fileNames = [];
|
||||
srvScope.arrayOfTestFiles(links, fileNames, fileNameRegexp);
|
||||
|
||||
return fileNames.map(function (f) new browserTestFile(f));
|
||||
}
|
||||
|
||||
function setStatus(aStatusString) {
|
||||
document.getElementById("status").value = aStatusString;
|
||||
}
|
||||
|
||||
function runAllTests() {
|
||||
var windowMediator = Cc['@mozilla.org/appshell/window-mediator;1'].
|
||||
getService(Ci.nsIWindowMediator);
|
||||
var testWin = windowMediator.getMostRecentWindow("navigator:browser");
|
||||
|
||||
setStatus("Running...");
|
||||
testWin.focus();
|
||||
|
||||
var Tester = new testWin.Tester(listTests(), testsFinished);
|
||||
Tester.start();
|
||||
}
|
||||
|
||||
function sum(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
function getHTMLLogFromTests(aTests) {
|
||||
if (!aTests.length)
|
||||
return "<div id=\"summary\" class=\"success\">No tests to run. " +
|
||||
"Did you pass an invalid --test-path?</div>";
|
||||
|
||||
var log = "";
|
||||
|
||||
var passCount = aTests.map(function (f) f.passCount).reduce(sum);
|
||||
var failCount = aTests.map(function (f) f.failCount).reduce(sum);
|
||||
var todoCount = aTests.map(function (f) f.todoCount).reduce(sum);
|
||||
log += "<div id=\"summary\" class=\"";
|
||||
log += failCount == 0 ? "success" : "failure";
|
||||
log += "\">\n<p>Passed: " + passCount + "</p>\n" +
|
||||
"<p>Failed: " + failCount + "</p>\n" +
|
||||
"<p>Todo: " + todoCount + "</p>\n</div>\n<div id=\"log\">\n";
|
||||
|
||||
return log + aTests.map(function (f) {
|
||||
return "<p class=\"testHeader\">" + f.path + "</p>\n" + f.htmlLog;
|
||||
}).join("\n") + "</div>";
|
||||
}
|
||||
|
||||
function getLogFromTests(aTests) {
|
||||
if (!aTests.length)
|
||||
return "PASS - No tests to run";
|
||||
|
||||
var log = aTests.map(function (f) {
|
||||
var output = f.path + "\n";
|
||||
if (f.log)
|
||||
output += f.log + "\n";
|
||||
return output;
|
||||
}).join("");
|
||||
log += "\nBrowser Chrome Test Summary\n";
|
||||
|
||||
var passCount = aTests.map(function (f) f.passCount).reduce(sum);
|
||||
var failCount = aTests.map(function (f) f.failCount).reduce(sum);
|
||||
var todoCount = aTests.map(function (f) f.todoCount).reduce(sum);
|
||||
log += "\tPass: " + passCount + "\n\tFail: " + failCount + "\n\tTodo: " + todoCount + "\n";
|
||||
|
||||
return log;
|
||||
}
|
||||
|
||||
function testsFinished(aTests) {
|
||||
// Focus our window, to display the results
|
||||
window.focus();
|
||||
|
||||
var start = "*** Start BrowserChrome Test Results ***\n";
|
||||
var end = "\n*** End BrowserChrome Test Results ***\n";
|
||||
var output = start + getLogFromTests(aTests) + end;
|
||||
|
||||
// Output to stdout
|
||||
dump(output);
|
||||
|
||||
// Output to file
|
||||
if (gConfig.logPath) {
|
||||
MozillaFileLogger.init(gConfig.logPath);
|
||||
MozillaFileLogger._foStream.write(output, output.length);
|
||||
MozillaFileLogger.close();
|
||||
}
|
||||
|
||||
if (gConfig.closeWhenDone) {
|
||||
goQuitApplication();
|
||||
return;
|
||||
}
|
||||
|
||||
// UI
|
||||
document.getElementById("results").innerHTML = getHTMLLogFromTests(aTests);
|
||||
setStatus("Done.");
|
||||
}
|
||||
]]></script>
|
||||
<button id="runTestsButton" onclick="runAllTests();" label="Run All Tests"/>
|
||||
<label id="status"/>
|
||||
<scrollbox flex="1" style="overflow: auto" align="stretch">
|
||||
<div id="results" xmlns="http://www.w3.org/1999/xhtml" flex="1"/>
|
||||
</scrollbox>
|
||||
</window>
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<!-- ***** BEGIN LICENSE BLOCK *****
|
||||
- Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
-
|
||||
- The contents of this file are subject to the Mozilla Public License Version
|
||||
- 1.1 (the "License"); you may not use this file except in compliance with
|
||||
- the License. You may obtain a copy of the License at
|
||||
- http://www.mozilla.org/MPL/
|
||||
-
|
||||
- Software distributed under the License is distributed on an "AS IS" basis,
|
||||
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
- for the specific language governing rights and limitations under the
|
||||
- License.
|
||||
-
|
||||
- The Original Code is Browser Test Harness.
|
||||
-
|
||||
- The Initial Developer of the Original Code is
|
||||
- Mozilla Corporation.
|
||||
-
|
||||
- Portions created by the Initial Developer are Copyright (C) 2007
|
||||
- the Initial Developer. All Rights Reserved.
|
||||
-
|
||||
- Contributor(s):
|
||||
- Gavin Sharp <gavin@gavinsharp.com> (original author)
|
||||
-
|
||||
- Alternatively, the contents of this file may be used under the terms of
|
||||
- either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
- in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
- of those above. If you wish to allow use of your version of this file only
|
||||
- under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
- use your version of this file under the terms of the MPL, indicate your
|
||||
- decision by deleting the provisions above and replace them with the notice
|
||||
- and other provisions required by the LGPL or the GPL. If you do not delete
|
||||
- the provisions above, a recipient may use your version of this file under
|
||||
- the terms of any one of the MPL, the GPL or the LGPL.
|
||||
-
|
||||
- ***** END LICENSE BLOCK ***** -->
|
||||
|
||||
<overlay id="browserTestOverlay"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/browser-test.js"/>
|
||||
</overlay>
|
|
@ -0,0 +1,198 @@
|
|||
if (Cc === undefined) {
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
}
|
||||
window.addEventListener("load", testOnLoad, false);
|
||||
|
||||
function testOnLoad() {
|
||||
// Make sure to launch the test harness for the first opened window only
|
||||
var prefs = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefBranch);
|
||||
if (prefs.prefHasUserValue("testing.browserTestHarness.running"))
|
||||
return;
|
||||
|
||||
prefs.setBoolPref("testing.browserTestHarness.running", true);
|
||||
|
||||
var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
|
||||
getService(Ci.nsIWindowWatcher);
|
||||
var sstring = Cc["@mozilla.org/supports-string;1"].
|
||||
createInstance(Ci.nsISupportsString);
|
||||
sstring.data = location.search;
|
||||
ww.openWindow(window, "chrome://mochikit/content/browser-harness.xul", "browserTest",
|
||||
"chrome,centerscreen,dialog,resizable,titlebar,toolbar=no,width=800,height=600", sstring);
|
||||
}
|
||||
|
||||
function Tester(aTests, aCallback) {
|
||||
this.tests = aTests;
|
||||
this.callback = aCallback;
|
||||
}
|
||||
Tester.prototype = {
|
||||
checker: null,
|
||||
currentTestIndex: -1,
|
||||
get currentTest() {
|
||||
return this.tests[this.currentTestIndex];
|
||||
},
|
||||
get done() {
|
||||
return this.currentTestIndex == this.tests.length - 1;
|
||||
},
|
||||
step: function Tester_step() {
|
||||
this.currentTestIndex++;
|
||||
},
|
||||
|
||||
start: function Tester_start() {
|
||||
if (this.tests.length)
|
||||
this.execTest();
|
||||
else
|
||||
this.finish();
|
||||
},
|
||||
|
||||
finish: function Tester_finish() {
|
||||
// Tests complete, notify the callback and return
|
||||
this.callback(this.tests);
|
||||
this.callback = null;
|
||||
this.tests = null;
|
||||
},
|
||||
|
||||
execTest: function Tester_execTest() {
|
||||
if (this.done) {
|
||||
this.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// Move to the next test (or first test).
|
||||
this.step();
|
||||
|
||||
// Load the tests into a testscope
|
||||
this.currentTest.scope = new testScope(this.currentTest.tests);
|
||||
|
||||
var scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Ci.mozIJSSubScriptLoader);
|
||||
try {
|
||||
scriptLoader.loadSubScript(this.currentTest.path, this.currentTest.scope);
|
||||
|
||||
// Run the test
|
||||
this.currentTest.scope.test();
|
||||
} catch (ex) {
|
||||
this.currentTest.tests.push(new testResult(false, "Exception thrown", ex, false));
|
||||
this.currentTest.scope.done = true;
|
||||
}
|
||||
|
||||
// If the test ran synchronously, move to the next test,
|
||||
// otherwise start a poller to monitor it's progress.
|
||||
if (this.currentTest.scope.done) {
|
||||
this.execTest();
|
||||
} else {
|
||||
var self = this;
|
||||
this.checker = new resultPoller(this.currentTest, function () { self.execTest(); });
|
||||
this.checker.start();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function testResult(aCondition, aName, aDiag, aIsTodo) {
|
||||
aName = aName || "";
|
||||
|
||||
this.pass = !!aCondition;
|
||||
this.todo = aIsTodo;
|
||||
if (this.pass) {
|
||||
if (aIsTodo)
|
||||
this.msg = "\tTODO PASS - " + aName;
|
||||
else
|
||||
this.msg = "\tPASS - " + aName;
|
||||
} else {
|
||||
this.msg = "\tFAIL - ";
|
||||
if (aIsTodo)
|
||||
this.msg += "TODO Worked? - ";
|
||||
this.msg += aName;
|
||||
if (aDiag)
|
||||
this.msg += " - " + aDiag;
|
||||
}
|
||||
}
|
||||
|
||||
function testScope(aTests) {
|
||||
var scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Ci.mozIJSSubScriptLoader);
|
||||
scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", this.EventUtils);
|
||||
|
||||
this.tests = aTests;
|
||||
|
||||
var self = this;
|
||||
this.ok = function test_ok(condition, name, diag) {
|
||||
self.tests.push(new testResult(condition, name, diag, false));
|
||||
}
|
||||
this.is = function test_is(a, b, name) {
|
||||
self.ok(a == b, name, "Got " + a + ", expected " + b);
|
||||
}
|
||||
this.isnot = function test_isnot(a, b, name) {
|
||||
self.ok(a != b, name, "Didn't expect " + a + ", but got it");
|
||||
}
|
||||
this.todo = function test_todo(condition, name, diag) {
|
||||
self.tests.push(new testResult(!condition, name, diag, true));
|
||||
}
|
||||
this.todo_is = function test_todo_is(a, b, name) {
|
||||
self.todo(a == b, name, "Got " + a + ", expected " + b);
|
||||
},
|
||||
this.todo_isnot = function test_todo_isnot(a, b, name) {
|
||||
self.todo(a != b, name, "Didn't expect " + a + ", but got it");
|
||||
},
|
||||
|
||||
this.executeSoon = function test_executeSoon(func) {
|
||||
let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
|
||||
|
||||
tm.mainThread.dispatch({
|
||||
run: function() {
|
||||
func();
|
||||
}
|
||||
}, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
};
|
||||
|
||||
this.waitForExplicitFinish = function test_WFEF() {
|
||||
self.done = false;
|
||||
}
|
||||
this.finish = function test_finish() {
|
||||
self.done = true;
|
||||
}
|
||||
}
|
||||
testScope.prototype = {
|
||||
done: true,
|
||||
|
||||
EventUtils: {}
|
||||
};
|
||||
|
||||
// Check whether the test has completed every 3 seconds
|
||||
const CHECK_INTERVAL = 3000;
|
||||
// Test timeout (seconds)
|
||||
const TIMEOUT_SECONDS = 30;
|
||||
|
||||
const MAX_LOOP_COUNT = (TIMEOUT_SECONDS * 1000) / CHECK_INTERVAL;
|
||||
|
||||
function resultPoller(aTest, aCallback) {
|
||||
this.test = aTest;
|
||||
this.callback = aCallback;
|
||||
}
|
||||
resultPoller.prototype = {
|
||||
loopCount: 0,
|
||||
interval: 0,
|
||||
|
||||
start: function resultPoller_start() {
|
||||
var self = this;
|
||||
function checkDone() {
|
||||
self.loopCount++;
|
||||
|
||||
if (self.loopCount > MAX_LOOP_COUNT) {
|
||||
self.test.tests.push(new testResult(false, "Timed out", "", false));
|
||||
self.test.scope.done = true;
|
||||
}
|
||||
|
||||
if (self.test.scope.done) {
|
||||
clearInterval(self.interval);
|
||||
|
||||
// Notify the callback
|
||||
self.callback();
|
||||
self.callback = null;
|
||||
self.test = null;
|
||||
}
|
||||
}
|
||||
this.interval = setInterval(checkDone, CHECK_INTERVAL);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,51 @@
|
|||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Netscape Communications Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 1998
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = testing/mochitest/chrome
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_STATIC_FILES = test_sample.xul \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_STATIC_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/$(relativesrcdir)
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=8675309
|
||||
-->
|
||||
<window title="Mozilla Bug 8675309"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<title>Test for Bug 8675309</title>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=8675309">Mozilla Bug 8675309</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
<script class="testbody" type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
/** Test for Bug 8675309 **/
|
||||
|
||||
is(1, "1", "sanity check");
|
||||
ok(true, "sanity check");
|
||||
|
||||
|
||||
|
||||
]]>
|
||||
</script>
|
||||
|
||||
</window>
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/perl
|
||||
#
|
||||
# gen_template.pl
|
||||
# Makes test case templates.
|
||||
# Takes two arguments:
|
||||
#
|
||||
# -b : a bugnumber
|
||||
# -type : template type. {html|xhtml|xul}. defaults to html.
|
||||
#
|
||||
# perl gen_template.pl -b 345876 -type xul
|
||||
#
|
||||
# sends a test case template for bug 345876 to stdout
|
||||
use FindBin;
|
||||
use Getopt::Long;
|
||||
GetOptions("b=i"=> \$bug_number,
|
||||
"type:s"=> \$template_type);
|
||||
|
||||
if ($template_type eq "xul") {
|
||||
$template_type = "$FindBin::RealBin/static/xul.template.txt";
|
||||
} elsif ($template_type eq "xhtml") {
|
||||
$template_type = "$FindBin::RealBin/static/xhtml.template.txt";
|
||||
} elsif ($template_type eq "chrome") {
|
||||
$template_type = "$FindBin::RealBin/static/chrome.template.txt";
|
||||
} else {
|
||||
$template_type = "$FindBin::RealBin/static/test.template.txt";
|
||||
}
|
||||
|
||||
open(IN,$template_type) or die("Failed to open myfile for reading.");
|
||||
while((defined(IN)) && ($line = <IN>)) {
|
||||
$line =~ s/{BUGNUMBER}/$bug_number/g;
|
||||
print STDOUT $line;
|
||||
}
|
||||
close(IN);
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/static/harness.css"
|
||||
type="text/css"?>
|
||||
|
||||
<?xul-overlay href="chrome://mochikit/content/harness-overlay.xul"?>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="Accessibility Chrome Test Harness"
|
||||
directory="a11y">
|
||||
</window>
|
|
@ -0,0 +1,113 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
|
||||
<overlay id="browserTestOverlay"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<window>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js" />
|
||||
<script type="text/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/TestRunner.js"/>
|
||||
<script type="text/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/MozillaFileLogger.js"/>
|
||||
<script type="text/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/quit.js" />
|
||||
<script type="text/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/setup.js" />
|
||||
<script type="application/javascript;version=1.7"><![CDATA[
|
||||
function loadTests()
|
||||
{
|
||||
var dir = document.documentElement.getAttribute('directory');
|
||||
// Find our chrome dir
|
||||
var ios = Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
var chromeURI = ios.newURI("chrome://mochikit/content/",
|
||||
null, null);
|
||||
var resolvedURI = Cc["@mozilla.org/chrome/chrome-registry;1"].
|
||||
getService(Ci.nsIChromeRegistry).
|
||||
convertChromeURL(chromeURI);
|
||||
var fileHandler = Cc["@mozilla.org/network/protocol;1?name=file"].
|
||||
getService(Ci.nsIFileProtocolHandler);
|
||||
var chromeDir = fileHandler.getFileFromURLSpec(resolvedURI.spec);
|
||||
chromeDir = chromeDir.parent.QueryInterface(Ci.nsILocalFile);
|
||||
chromeDir.appendRelativePath(dir);
|
||||
|
||||
// load server.js in so we can share template functions
|
||||
var scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Ci.mozIJSSubScriptLoader);
|
||||
var srvScope = {};
|
||||
scriptLoader.loadSubScript("chrome://mochikit/content/server.js",
|
||||
srvScope);
|
||||
|
||||
// generate our test list
|
||||
srvScope.makeTags();
|
||||
var url = "chrome://mochikit/content/" + dir + "/";
|
||||
var [links, count] = srvScope.list(url, chromeDir, true);
|
||||
var listContent = srvScope.linksToListItems(links);
|
||||
var tableContent = srvScope.linksToTableRows(links);
|
||||
function populate() {
|
||||
$("list-holder").setAttribute("rowspan", 1 + count);
|
||||
$("test-list").innerHTML += listContent;
|
||||
$("test-table").innerHTML += tableContent;
|
||||
$("wrapper").innerHTML += " "; // redraw the table
|
||||
}
|
||||
gTestList = eval(srvScope.jsonArrayOfTestFiles(links));
|
||||
populate();
|
||||
hookup();
|
||||
|
||||
// if we got passed a test path, just run that single test
|
||||
if ("testPath" in params && params.testPath)
|
||||
window.location.href = url + params.testPath;
|
||||
}
|
||||
window.addEventListener("load", loadTests, false)
|
||||
]]>
|
||||
</script>
|
||||
|
||||
<vbox>
|
||||
<button label="Run Chrome Tests" id="runtests" flex="1"/>
|
||||
|
||||
<body xmlns="http://www.w3.org/1999/xhtml" id="xulharness">
|
||||
<div class="container">
|
||||
<p style="float:right;">
|
||||
<small>Based on the MochiKit unit tests.</small>
|
||||
</p>
|
||||
<div class="status">
|
||||
<h1 id="indicator">Status</h1>
|
||||
<h2 id="pass">Passed: <span id="pass-count">0</span></h2>
|
||||
<h2 id="fail">Failed: <span id="fail-count">0</span></h2>
|
||||
<h2 id="fail">Todo: <span id="todo-count">0</span></h2>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
<div id="current-test">
|
||||
<b>Currently Executing: <span id="current-test-path">_</span></b>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
<div class="frameholder">
|
||||
<iframe scrolling="no" id="testframe" width="500"></iframe>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
<div class="toggle">
|
||||
<a href="#" id="toggleNonTests">Show Non-Tests</a>
|
||||
<br />
|
||||
</div>
|
||||
<div id="wrapper">
|
||||
<table cellpadding="0" cellspacing="0" id="test-table">
|
||||
<tr>
|
||||
<td>Passed</td>
|
||||
<td>Failed</td>
|
||||
<td>Todo</td>
|
||||
<td id="list-holder">
|
||||
<ul class="top" id="test-list"><li><b>Test Files</b></li></ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</vbox>
|
||||
</window>
|
||||
|
||||
</overlay>
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/static/harness.css"
|
||||
type="text/css"?>
|
||||
|
||||
<?xul-overlay href="chrome://mochikit/content/harness-overlay.xul"?>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="Chrome Test Harness"
|
||||
directory="chrome">
|
||||
</window>
|
|
@ -0,0 +1,99 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=413310
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 413310</title>
|
||||
<script type="text/javascript" src="MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=413310">Mozilla Bug 413310</a>
|
||||
<p id="display">
|
||||
<iframe id="i" src="bug413310-subframe.html" onload="setTimeout(doNextStep, 20)">
|
||||
</iframe>
|
||||
</p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Bug 413310 **/
|
||||
|
||||
// NOTE: If we ever make subframes do bfcache stuff, this test will need to be
|
||||
// modified accordingly! It assumes that subframes do not get bfcached.
|
||||
var onloadCount = 0;
|
||||
|
||||
var step = -1; // One increment will come from the initial subframe onload.
|
||||
|
||||
var textContent;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(doNextStep);
|
||||
|
||||
function doNextStep() {
|
||||
++step;
|
||||
switch (step) {
|
||||
case 1:
|
||||
is(onloadCount, 1, "Loaded initial page");
|
||||
is($("i").contentWindow.location.href,
|
||||
location.href.replace(/test_bug413310.html/,
|
||||
"bug413310-subframe.html"),
|
||||
"Unexpected subframe location after initial load");
|
||||
$("i").contentDocument.forms[0].submit();
|
||||
break;
|
||||
case 2:
|
||||
is(onloadCount, 2, "Loaded POST result");
|
||||
|
||||
is($("i").contentWindow.location.href,
|
||||
location.href.replace(/test_bug413310.html/,
|
||||
"bug413310-post.sjs"),
|
||||
"Unexpected subframe location after POST load");
|
||||
|
||||
textContent = $("i").contentDocument.body.textContent;
|
||||
isDeeply(textContent.match(/^POST /), ["POST "], "Not a POST?");
|
||||
|
||||
$("i").contentWindow.location.hash = "foo";
|
||||
setTimeout(doNextStep, 0);
|
||||
break;
|
||||
case 3:
|
||||
is(onloadCount, 2, "Anchor scroll should not fire onload");
|
||||
is($("i").contentWindow.location.href,
|
||||
location.href.replace(/test_bug413310.html/,
|
||||
"bug413310-post.sjs#foo"),
|
||||
"Unexpected subframe location after anchor scroll");
|
||||
is(textContent, $("i").contentDocument.body.textContent,
|
||||
"Did a load when scrolling?");
|
||||
$("i").contentWindow.location.href = "bug413310-subframe.html";;
|
||||
break;
|
||||
case 4:
|
||||
is(onloadCount, 3, "Done new load");
|
||||
is($("i").contentWindow.location.href,
|
||||
location.href.replace(/test_bug413310.html/,
|
||||
"bug413310-subframe.html"),
|
||||
"Unexpected subframe location after new load");
|
||||
history.back();
|
||||
break;
|
||||
case 5:
|
||||
is(onloadCount, 4,
|
||||
"History traversal didn't fire onload: bfcache issues!");
|
||||
is($("i").contentWindow.location.href,
|
||||
location.href.replace(/test_bug413310.html/,
|
||||
"bug413310-post.sjs#foo"),
|
||||
"Unexpected subframe location");
|
||||
is(textContent, $("i").contentDocument.body.textContent,
|
||||
"Did a load when going back?");
|
||||
SimpleTest.finish();
|
||||
break;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Testing Mixed Up Microformat APIs</title>
|
||||
<script type="text/javascript" src="MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="tests/SimpleTest/test.css"></link>
|
||||
</head>
|
||||
|
||||
<body id="contentbody">
|
||||
<div id="content">
|
||||
<!-- hCard -->
|
||||
<p class="vcard" id="23-abbr-title-everything">
|
||||
<!-- perhaps the most annoying test ever -->
|
||||
<abbr class="fn" title="John Doe">foo</abbr>
|
||||
<span class="n">
|
||||
<abbr class="honorific-prefix" title="Mister">Mr.</abbr>
|
||||
|
||||
<abbr class="given-name" title="Jonathan">John</abbr>
|
||||
<abbr class="additional-name" title="John">J</abbr>
|
||||
<abbr class="family-name" title="Doe-Smith">Doe</abbr>
|
||||
<abbr class="honorific-suffix" title="Medical Doctor">M.D</abbr>
|
||||
</span>
|
||||
<abbr class="nickname" title="JJ">jj</abbr>
|
||||
|
||||
<abbr class="bday" title="2006-04-04">April 4, 2006</abbr>
|
||||
<span class="adr">
|
||||
<abbr class="post-office-box" title="Box 1234">B. 1234</abbr>
|
||||
<abbr class="extended-address" title="Suite 100">Ste. 100</abbr>
|
||||
<abbr class="street-address" title="123 Fake Street">123 Fake St.</abbr>
|
||||
<abbr class="locality" title="San Francisco">San Fran</abbr>
|
||||
|
||||
<abbr class="region" title="California">CA</abbr>
|
||||
<abbr class="postal-code" title="12345-6789">12345</abbr>
|
||||
<abbr class="country-name" title="United States of America">USA</abbr>
|
||||
<abbr class="type" title="work">workplace</abbr>
|
||||
</span>
|
||||
<abbr class="tel" title="415.555.1234">1234</abbr>
|
||||
|
||||
<abbr class="tel-type-value" title="work">workplace</abbr>
|
||||
<!-- mailer -->
|
||||
<abbr class="tz" title="-0700">Pacific Time</abbr>
|
||||
<span class="geo">
|
||||
<abbr class="latitude" title="37.77">Northern</abbr>
|
||||
<abbr class="longitude" title="-122.41">California</abbr>
|
||||
</span>
|
||||
|
||||
<abbr class="title" title="President">pres.</abbr> and
|
||||
<abbr class="role" title="Chief">cat wrangler</abbr>
|
||||
<!-- <span class="agent"></span> -->
|
||||
<span class="org">
|
||||
<abbr class="organization-name" title="Intellicorp">foo</abbr>
|
||||
<abbr class="organization-unit" title="Intelligence">bar</abbr>
|
||||
</span>
|
||||
|
||||
<!-- <abbr class="category" title=""></abbr> -->
|
||||
<abbr class="note" title="this is a note">this is not a note</abbr>
|
||||
<!-- <abbr class="rev" title=""></abbr> (revision datetime) -->
|
||||
<!-- <abbr class="sort-string" title=""></abbr> -->
|
||||
<abbr class="uid" title="abcdefghijklmnopqrstuvwxyz">alpha</abbr>
|
||||
<abbr class="class" title="public">pub</abbr>
|
||||
<!-- <abbr class="key" title=""></abbr> -->
|
||||
|
||||
</p>
|
||||
|
||||
<!-- hCalendar -->
|
||||
<frameset>
|
||||
<frame id="frame1">
|
||||
<div>
|
||||
<span class="notAMicroformat" id="notme">
|
||||
<abbr> class="foo">I am not a microformat</abbr>
|
||||
|
||||
<abbr> class="title">Foolish title, not a format</abbr>
|
||||
</span>
|
||||
</div>
|
||||
</frame>
|
||||
<frame id="frame3">
|
||||
<span class="geo" id="02-geo-abbr-latlong" >
|
||||
<abbr class="latitude" title="75.77">Far Northern</abbr>
|
||||
|
||||
<abbr class="longitude" title="-122.41">Canada</abbr>
|
||||
</span>
|
||||
<frame id="frame2">
|
||||
<div class="vcalendar">
|
||||
<span class="vevent" id="15-calendar-xml-lang">
|
||||
<a class="url" href="http://www.web2con.com/">
|
||||
<span class="summary">Web 2.0 Conference</span>:
|
||||
<abbr class="dtstart" title="2005-10-05">October 5</abbr>-
|
||||
<abbr class="dtend" title="2005-10-08">7</abbr>,
|
||||
at the <span class="location">Argent Hotel, San Francisco, CA</span>
|
||||
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</frame>
|
||||
</frameset>
|
||||
|
||||
<div id="secondnode">
|
||||
<span>some interesting content</span>
|
||||
|
||||
<!-- Geo Fire test once we know this is loaded.-->
|
||||
<iframe src="geo.html" onload="test_MicroformatsAPI();">
|
||||
</iframe>
|
||||
|
||||
<!-- adr -->
|
||||
<div class="adr" id="01-extended-address">
|
||||
<span class="extended-address">Park Bench</span>
|
||||
</div>
|
||||
|
||||
<div class="vcalendar">
|
||||
|
||||
<span class="vevent" id="16-calendar-xml-lang">
|
||||
<a class="url" href="http://www.foo.com/">
|
||||
<span class="summary">Pseudo Conference</span>:
|
||||
<abbr class="dtstart" title="2008-04-01">April 1</abbr>-
|
||||
<abbr class="dtend" title="2008-04-03">April 3</abbr>,
|
||||
at the <span class="location">Argent Hotel, San Francisco, CA</span>
|
||||
</a>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Ok, the test, here we go -->
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
// Called from onload in iframe
|
||||
function test_MicroformatsAPI() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
Components.utils.import("resource://gre/modules/Microformats.js");
|
||||
|
||||
// Test that we can get them all
|
||||
var mfs = [];
|
||||
mfs = Microformats.get("adr",
|
||||
document.getElementById("content"),
|
||||
{showHidden: true});
|
||||
|
||||
is(mfs.length, 2, "Two adr's in our array");
|
||||
|
||||
mfs = Microformats.get("geo",
|
||||
document.getElementById("content"),
|
||||
{recurseExternalFrames: true});
|
||||
is(mfs.length, 3, "Three geo's in our array");
|
||||
|
||||
mfs = Microformats.get("hCalendar",
|
||||
document.getElementById("content"),
|
||||
{recurseExternalFrames: false});
|
||||
// Should get the hCalendar whether we recurseExternalFrames or not.
|
||||
is(mfs.length, 2, "Two hCalendar returned not recursing frames");
|
||||
|
||||
mfs = Microformats.get("hCalendar",
|
||||
document.getElementById("content"),
|
||||
{recurseExternalFrames: true});
|
||||
is(mfs.length, 2, "Two hCalendars returned recursing frames");
|
||||
|
||||
mfs = Microformats.get("hCard",
|
||||
document.getElementById("content"),
|
||||
{recurseExternalFrames: true},
|
||||
mfs);
|
||||
is(mfs.length, 3, "Two hCalendars and one hCard");
|
||||
|
||||
mfs = Microformats.get("hCalendar", document.getElementById("secondnode"));
|
||||
|
||||
is(mfs[0].summary, "Pseudo Conference",
|
||||
"Make sure we get the proper hCalendar from the second level node");
|
||||
is(mfs.length, 1, "And we should only get one hCalendar not two from this node.");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,70 @@
|
|||
<!-- Demonstrates that we are always counting MF's in a <frame> tag, and NEVER
|
||||
counting MF's in an <iframe> regardless of what the "frame recursion" bit
|
||||
is set to. -->
|
||||
<html>
|
||||
<head>
|
||||
<title>Testing Mixed Up Microformat APIs</title>
|
||||
<script type="text/javascript" src="MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="tests/SimpleTest/test.css"></link>
|
||||
|
||||
</head>
|
||||
<body id="contentbody">
|
||||
|
||||
<frameset>
|
||||
<frame id="frame1">
|
||||
<div>
|
||||
<span class="notAMicroformat" id="notme">
|
||||
<abbr class="foo">I am not a microformat</abbr>
|
||||
<abbr class="title">Foolish title, not a format</abbr>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</frame>
|
||||
<frame id="frame3">
|
||||
<span class="geo" id="02-geo-abbr-latlong" >
|
||||
<abbr class="latitude" title="75.77">Far Northern</abbr>
|
||||
<abbr class="longitude" title="-122.41">Canada</abbr>
|
||||
</span>
|
||||
|
||||
</frame>
|
||||
<frame id="frame2">
|
||||
<div class="stuff">
|
||||
<span>Testing is Fun!</span>
|
||||
</div>
|
||||
</frame>
|
||||
</frameset>
|
||||
|
||||
<!-- Geo -->
|
||||
|
||||
<iframe id="iframe" src="geo.html" onload="onLoad();">
|
||||
</iframe>
|
||||
|
||||
<!-- Ok, the test, here we go -->
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
// Start the test once the iFrame loads
|
||||
function onLoad() {
|
||||
test_MicroformatsAPI();
|
||||
}
|
||||
|
||||
function test_MicroformatsAPI() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
Components.utils.import("resource://gre/modules/Microformats.js");
|
||||
|
||||
count = Microformats.count("geo",
|
||||
document.getElementById("contentbody"),
|
||||
{recurseExternalFrames: false},
|
||||
0);
|
||||
is(count, 1, "Only one geo - we don't count external frames");
|
||||
|
||||
count = Microformats.count("geo",
|
||||
document.getElementById("contentbody"),
|
||||
{recurseExternalFrames: true});
|
||||
is(count, 2, "Two Geo's - one in frame and one in iframe");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,93 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=620295
|
||||
-->
|
||||
<head>
|
||||
<title>Test that activating SVG 'a' elements navigate to their xlink:href</title>
|
||||
<script type="text/javascript" src="MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=620295">Mozilla Bug 620295</a>
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content" style="visibility: hidden">
|
||||
<iframe id="iframe1" src="a_href_helper_01.svg" onload="frameLoaded()"></iframe>
|
||||
<iframe id="iframe2" src="a_href_helper_02_03.svg" onload="frameLoaded()"></iframe>
|
||||
<iframe id="iframe3" src="a_href_helper_02_03.svg" onload="frameLoaded()"></iframe>
|
||||
<iframe id="iframe4" src="a_href_helper_04.svg" onload="frameLoaded()"></iframe>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript"><![CDATA[
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var testCount = 4;
|
||||
var didWindowLoad = false;
|
||||
var frameLoadCount = 0;
|
||||
var navigationCount = 0;
|
||||
|
||||
function endsWith(s1, s2) {
|
||||
s1 = String(s1);
|
||||
return s1.length >= s2.length && s1.substring(s1.length - s2.length) == s2;
|
||||
}
|
||||
|
||||
function windowLoaded() {
|
||||
didWindowLoad = true;
|
||||
doNavigationIfReady();
|
||||
}
|
||||
|
||||
function frameLoaded() {
|
||||
frameLoadCount++;
|
||||
doNavigationIfReady();
|
||||
}
|
||||
|
||||
function doNavigationIfReady() {
|
||||
if (didWindowLoad && frameLoadCount == testCount) {
|
||||
doNavigation();
|
||||
}
|
||||
}
|
||||
|
||||
function doNavigation() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserWrite");
|
||||
|
||||
// Test clicking on an unmodified <a>.
|
||||
doNavigationTest(1, "a_href_helper_01.svg");
|
||||
// Test clicking on an <a> whose xlink:href is modified by assigning to href.baseVal.
|
||||
doNavigationTest(2, "a_href_helper_02_03.svg", function(a) { a.href.baseVal = "a_href_destination.svg"; });
|
||||
// Test clicking on an <a> whose xlink:href is modified by a setAttributeNS call.
|
||||
doNavigationTest(3, "a_href_helper_02_03.svg", function(a) { a.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", "a_href_destination.svg"); });
|
||||
// Test clicking on an <a> whose xlink:href is modified by animation.
|
||||
doNavigationTest(4, "a_href_helper_04.svg");
|
||||
}
|
||||
|
||||
function doNavigationTest(testNumber, initialHref, f) {
|
||||
var iframe = document.getElementById("iframe" + testNumber);
|
||||
var a = iframe.contentDocument.getElementById("a");
|
||||
ok(endsWith(iframe.contentWindow.location, initialHref), "Initial href of test " + testNumber);
|
||||
iframe.onload = function() {
|
||||
ok(endsWith(iframe.contentWindow.location, "a_href_destination.svg"), "Final href of test " + testNumber);
|
||||
if (++navigationCount == testCount) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
};
|
||||
if (f) {
|
||||
f(a);
|
||||
}
|
||||
dispatchClickEvent(a);
|
||||
}
|
||||
|
||||
function dispatchClickEvent(element) {
|
||||
var event = element.ownerDocument.createEvent("MouseEvent");
|
||||
event.initMouseEvent("click", true, true, element.ownerDocument.defaultView, 1, 0, 0, 0, 0, false, false,
|
||||
false, false, 0, null);
|
||||
element.dispatchEvent(event);
|
||||
}
|
||||
|
||||
window.onload = windowLoaded;
|
||||
|
||||
]]></script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,14 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>redirecting...</title>
|
||||
|
||||
<script type="text/javascript" src="redirect.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
redirect("chrome://mochikit/content/harness-a11y.xul");
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
redirecting...
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,14 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>redirecting...</title>
|
||||
|
||||
<script type="text/javascript" src="redirect.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
redirect("chrome://mochikit/content/harness.xul");
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
redirecting...
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,53 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is MozJSHTTP code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Robert Sayre <sayrer@gmail.com>
|
||||
* Alexander Surkov <surkov.alexander@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function redirect(aURL)
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
var windowMediator = Cc['@mozilla.org/appshell/window-mediator;1'].
|
||||
getService(Ci.nsIWindowMediator);
|
||||
var win = windowMediator.getMostRecentWindow("navigator:browser");
|
||||
win.getWebNavigation().loadURI(aURL + location.search,
|
||||
null, null, null, null);
|
||||
}
|
|
@ -0,0 +1,703 @@
|
|||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Mozilla Foundation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 1998
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Robert Sayre <sayrer@gmail.com>
|
||||
# Jeff Walden <jwalden+bmo@mit.edu>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
# Win32 path munging for msys courtesy the Curl project under an
|
||||
# MIT/X license http://curl.haxx.se/
|
||||
#
|
||||
# Copyright (c) 1996 - 2007, Daniel Stenberg, <daniel@haxx.se>.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
# above copyright notice and this permission notice appear in all
|
||||
# copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE AUTHORS
|
||||
# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||
# OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Except as contained in this notice, the name of a copyright holder
|
||||
# shall not be used in advertising or otherwise to promote the sale,
|
||||
# use or other dealings in this Software without prior written
|
||||
# authorization of the copyright holder.
|
||||
|
||||
# Perl script to start server and browser
|
||||
# For usage instructions, run:
|
||||
# perl runtests.pl --help
|
||||
|
||||
use FindBin;
|
||||
use File::Path;
|
||||
use File::Spec;
|
||||
use File::Basename;
|
||||
use Getopt::Long;
|
||||
use Cwd 'abs_path';
|
||||
use POSIX qw(sys_wait_h strftime);
|
||||
|
||||
use strict;
|
||||
|
||||
# URL parameters to test URL:
|
||||
#
|
||||
# autorun -- kick off tests automatically
|
||||
# closeWhenDone -- runs quit.js after tests
|
||||
# logFile -- logs test run to an absolute path
|
||||
#
|
||||
|
||||
# consoleLevel, fileLevel: set the logging level of the console and
|
||||
# file logs, if activated.
|
||||
# <http://mochikit.com/doc/html/MochiKit/Logging.html>
|
||||
|
||||
# Path to the test script on the server
|
||||
use constant TEST_SERVER_HOST => "localhost:8888";
|
||||
use constant TEST_PATH => "/tests/";
|
||||
use constant CHROME_PATH => "/redirect.html";
|
||||
use constant A11Y_PATH => "/redirect-a11y.html";
|
||||
use constant TESTS_URL => "http://" . TEST_SERVER_HOST . TEST_PATH;
|
||||
use constant CHROMETESTS_URL => "http://" . TEST_SERVER_HOST . CHROME_PATH;
|
||||
use constant A11YTESTS_URL => "http://" . TEST_SERVER_HOST . A11Y_PATH;
|
||||
# main browser chrome URL, same as browser.chromeURL pref
|
||||
#ifdef MOZ_SUITE
|
||||
use constant BROWSER_CHROME_URL => "chrome://navigator/content/navigator.xul";
|
||||
#else
|
||||
use constant BROWSER_CHROME_URL => "chrome://browser/content/browser.xul";
|
||||
#endif
|
||||
|
||||
# Max time in seconds to wait for server startup before tests will fail -- if
|
||||
# this seems big, it's mostly for debug machines where cold startup
|
||||
# (particularly after a build) takes forever.
|
||||
use constant SERVER_STARTUP_TIMEOUT => 45;
|
||||
|
||||
|
||||
# Since some tests require cross-domain support in Mochitest, across ports,
|
||||
# domains, subdomains, etc. we use a proxy autoconfig hack to map a bunch of
|
||||
# servers onto localhost:8888. We have to grant them the same privileges as
|
||||
# localhost:8888 here, since the browser only knows them as the URLs they're
|
||||
# pretending to be.
|
||||
my @servers = (
|
||||
"localhost:8888", # MUST be first -- see PAC pref-setting code
|
||||
"example.org:80",
|
||||
"test1.example.org:80",
|
||||
"test2.example.org:80",
|
||||
"sub1.test1.example.org:80",
|
||||
"sub1.test2.example.org:80",
|
||||
"sub2.test1.example.org:80",
|
||||
"sub2.test2.example.org:80",
|
||||
"example.org:8000",
|
||||
"test1.example.org:8000",
|
||||
"test2.example.org:8000",
|
||||
"sub1.test1.example.org:8000",
|
||||
"sub1.test2.example.org:8000",
|
||||
"sub2.test1.example.org:8000",
|
||||
"sub2.test2.example.org:8000",
|
||||
"example.com:80",
|
||||
"test1.example.com:80",
|
||||
"test2.example.com:80",
|
||||
"sub1.test1.example.com:80",
|
||||
"sub1.test2.example.com:80",
|
||||
"sub2.test1.example.com:80",
|
||||
"sub2.test2.example.com:80",
|
||||
"sectest1.example.org:80",
|
||||
"sub.sectest2.example.org:80",
|
||||
"sub1.xn--lt-uia.example.org:8000", # U+00E4 U+006C U+0074
|
||||
"sub2.xn--lt-uia.example.org:80", # U+00E4 U+006C U+0074
|
||||
"xn--exmple-cua.test:80",
|
||||
"sub1.xn--exmple-cua.test:80",
|
||||
"xn--hxajbheg2az3al.xn--jxalpdlp:80", # Greek IDN for example.test
|
||||
"sub1.xn--hxajbheg2az3al.xn--jxalpdlp:80",
|
||||
);
|
||||
|
||||
|
||||
my $profile = "mochitesttestingprofile";
|
||||
my $profile_dir = "$FindBin::Bin/$profile";
|
||||
|
||||
# These are generated in mozilla/testing/mochitest/Makefile.in
|
||||
#expand my $app = "$FindBin::Bin/" . __BROWSER_PATH__;
|
||||
#expand my $dist_bin = "$FindBin::Bin/" . __XPC_BIN_PATH__;
|
||||
#ifdef WIN32
|
||||
#expand my $is_win32 = __WIN32__;
|
||||
#else
|
||||
my $is_win32 = 0;
|
||||
#endif
|
||||
my $is_mac = ($^O =~ m/darwin/);
|
||||
my $unixish = (!($is_win32) && !($is_mac));
|
||||
|
||||
# Do everything.
|
||||
main();
|
||||
|
||||
|
||||
#################
|
||||
# MAIN FUNCTION #
|
||||
#################
|
||||
|
||||
sub main {
|
||||
my ($close_when_done, $appoverride, $log_path, $autorun,
|
||||
$console_level, $file_level, $help, $do_chrome, $test_path,
|
||||
$do_browser_chrome, $do_a11y, %browser_env, %browser_args);
|
||||
GetOptions("close-when-done!"=> \$close_when_done,
|
||||
"appname:s"=> \$appoverride,
|
||||
"log-file:s" => \$log_path,
|
||||
"autorun!" => \$autorun,
|
||||
"console-level:s" => \$console_level,
|
||||
"file-level:s" => \$file_level,
|
||||
"chrome!" => \$do_chrome,
|
||||
"test-path:s" => \$test_path,
|
||||
"browser-chrome!" => \$do_browser_chrome,
|
||||
"a11y!" => \$do_a11y,
|
||||
"setenv=s%" => \%browser_env,
|
||||
"browser-arg=s%" => \%browser_args,
|
||||
"help!" => \$help);
|
||||
|
||||
# if the switches include --help, exit and print directions
|
||||
if ($help) {
|
||||
usage_and_exit();
|
||||
}
|
||||
|
||||
# we were passed an explicit path to the app
|
||||
if ($appoverride) {
|
||||
$app = $appoverride;
|
||||
}
|
||||
|
||||
# make sure the application we're going to use exists
|
||||
unless (-e $app) {
|
||||
my $error_message = "\nError: Path \"$app\" doesn't exist.\n";
|
||||
$error_message .= "Are you executing ";
|
||||
$error_message .= "\$objdir/_tests/testing/mochitest/runtests.pl?\n\n";
|
||||
die $error_message;
|
||||
}
|
||||
|
||||
my $manifest = initializeProfile($app, $do_browser_chrome);
|
||||
my $serverPid = startServer($close_when_done);
|
||||
|
||||
# If we're lucky, the server has fully started by now, and all paths are
|
||||
# ready, etc. However, xpcshell cold start times suck, at least for debug
|
||||
# builds. We'll try to connect to the server for 30 seconds or until we
|
||||
# succeed, whichever is first. If we succeed, then we continue with
|
||||
# execution. If we fail, we try to kill the server and exit with an error.
|
||||
wait_for_server_startup($serverPid, SERVER_STARTUP_TIMEOUT);
|
||||
|
||||
my $url;
|
||||
if ($do_chrome) {
|
||||
$url = CHROMETESTS_URL . "?" . ($test_path ? "testPath=" . $test_path : "");
|
||||
} elsif ($do_browser_chrome) {
|
||||
# Tests will run from an overlay, no need to load any URL. We'll include
|
||||
# the test path in the config file so the browser chrome harness can use it.
|
||||
$url = "about:blank";
|
||||
} elsif ($do_a11y) {
|
||||
$url = A11YTESTS_URL . "?" . ($test_path ? "testPath=" . $test_path : "");
|
||||
} else {
|
||||
$url = TESTS_URL . ($test_path ? $test_path : "") . "?";
|
||||
}
|
||||
|
||||
if ($do_browser_chrome) {
|
||||
generate_test_config($autorun, $close_when_done, $log_path, $test_path);
|
||||
} else {
|
||||
if ($autorun) {
|
||||
$url .= "&autorun=1";
|
||||
}
|
||||
if ($close_when_done) {
|
||||
$url .= "&closeWhenDone=1";
|
||||
}
|
||||
if ($log_path) {
|
||||
$url .= "&logFile=$log_path";
|
||||
}
|
||||
if ($file_level) {
|
||||
$url .= "&fileLevel=$file_level";
|
||||
}
|
||||
if ($console_level) {
|
||||
$url .= "&consoleLevel=$console_level";
|
||||
}
|
||||
}
|
||||
|
||||
my $test_start = runTests($url, \%browser_env, \%browser_args);
|
||||
|
||||
shutdownServer($serverPid);
|
||||
|
||||
# print test run times
|
||||
my $test_finish = localtime();
|
||||
print " started: $test_start\n";
|
||||
print "finished: $test_finish\n";
|
||||
|
||||
# delete the profile and manifest
|
||||
# rmtree($profile_dir, 0, 0);
|
||||
unlink($manifest);
|
||||
}
|
||||
|
||||
#######################
|
||||
# COMMANDLINE USAGE #
|
||||
#######################
|
||||
|
||||
sub usage_and_exit {
|
||||
print "\n";
|
||||
print "Usage instructons for runtests.pl.\n";
|
||||
print "If --log-file is specified, --file-level must be specified as well.\n";
|
||||
print "If --chrome is specified, chrome tests will be run instead of web content tests.\n";
|
||||
print "If --browser-chrome is specified, browser-chrome tests will be run instead of web content tests.\n";
|
||||
print "If --a11y is specified, a11y tests will be run instead of web content tests.";
|
||||
print "\n\n";
|
||||
print "Syntax:\n";
|
||||
print " runtests.pl \\\n";
|
||||
print " [--autorun] \\\n";
|
||||
print " [--chrome] \\\n";
|
||||
print " [--browser-chrome] \\\n";
|
||||
print " [--a11y] \\\n";
|
||||
print " [--close-when-done] \\\n";
|
||||
print " [--appname=/path/to/app] \\\n";
|
||||
print " [--log-file=/path/to/logfile] \\\n";
|
||||
print " [--test-path=relative/path/to/tests] \\\n";
|
||||
print " [--setenv=VAR=value] \\\n";
|
||||
print " [--browser-arg=VAR=value] \\\n";
|
||||
print " [--file-level=DEBUG|INFO|ERROR|FATAL|WARNING] \\\n";
|
||||
print " [--console-level=DEBUG|INFO|ERROR|FATAL|WARNING] \n\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#######################
|
||||
# MAKE A WINDOWS PATH #
|
||||
#######################
|
||||
|
||||
#ifdef IS_CYGWIN
|
||||
|
||||
sub winPathFromDir {
|
||||
my ($path) = abs_path(@_);
|
||||
|
||||
# Use external cygpath command to get the windows-like path
|
||||
$path = `cygpath -w $path`;
|
||||
|
||||
# Just remove the traling CR char
|
||||
chop($path);
|
||||
return $path;
|
||||
}
|
||||
|
||||
#else
|
||||
# Non-cygwin version
|
||||
|
||||
sub winPathFromDir {
|
||||
my ($path) = abs_path(@_);
|
||||
|
||||
# This is a windows mingw32 build, we need to translate the
|
||||
# given path to the "actual" windows path.
|
||||
|
||||
my @m = `mount`;
|
||||
my $matchlen;
|
||||
my $bestmatch;
|
||||
my $mount;
|
||||
|
||||
#example mount output:
|
||||
# C:\DOCUME~1\Temp on /tmp type user (binmode,noumount)
|
||||
# c:\ActiveState\perl on /perl type user (binmode)
|
||||
# C:\msys\1.0\bin on /usr/bin type user (binmode,cygexec,noumount)
|
||||
# C:\msys\1.0\bin on /bin type user (binmode,cygexec,noumount)
|
||||
foreach $mount (@m) {
|
||||
if ( $mount =~ /(.*) on ([^ ]*) type /) {
|
||||
my ($mingw, $real)=($2, $1);
|
||||
if ($path =~ /^$mingw/i) {
|
||||
# the path starts with the path we
|
||||
# found on this line in the mount output
|
||||
my $len = length($mingw);
|
||||
if ($len > $matchlen) {
|
||||
# we remember the match that is the longest
|
||||
$matchlen = $len;
|
||||
$bestmatch = $real;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$matchlen) {
|
||||
die "Serious error, can't find our \"real\" path!\n";
|
||||
}
|
||||
|
||||
my ($volume,$directories,$file) =
|
||||
File::Spec->splitpath(substr($path, $matchlen), 1);
|
||||
my @dirs = File::Spec->splitdir( $directories );
|
||||
return $bestmatch . "\\" . join "\\", @dirs;
|
||||
}
|
||||
|
||||
# End of non-cygwin version
|
||||
#endif
|
||||
|
||||
##################
|
||||
# SERVER STARTUP #
|
||||
##################
|
||||
|
||||
# Start up the server, and let the server script handle shutdown if
|
||||
# we're closing when done. (We'll kill it later if it goes zombie
|
||||
# somehow, but that shouldn't be the way it happens except if
|
||||
# something really breaks.)
|
||||
|
||||
sub startServer {
|
||||
my ($close_when_done) = @_;
|
||||
my $pid = fork();
|
||||
if ($pid == 0) {
|
||||
# Run the server
|
||||
my $status = 0;
|
||||
|
||||
if ($close_when_done) {
|
||||
$ENV{'CLOSE_WHEN_DONE'} = '1';
|
||||
}
|
||||
|
||||
if ($unixish) {
|
||||
$ENV{'LD_LIBRARY_PATH'} = $dist_bin;
|
||||
$ENV{'MOZILLA_FIVE_HOME'} = $dist_bin;
|
||||
}
|
||||
|
||||
my @runargs = ("$dist_bin/xpcshell", "-v", "170");
|
||||
|
||||
# this path is passed as a string, so we need to convert it on win32
|
||||
if ($is_win32) {
|
||||
push(@runargs, "-f", winPathFromDir($FindBin::Bin) . "\\httpd.js");
|
||||
push(@runargs, "-f", winPathFromDir($FindBin::Bin) . "\\server.js");
|
||||
} else {
|
||||
push(@runargs, "-f", $FindBin::Bin . "/httpd.js");
|
||||
push(@runargs, "-f", $FindBin::Bin . "/server.js");
|
||||
}
|
||||
print "@runargs\n";
|
||||
exec @runargs or die("Error running server: $!\n");
|
||||
}
|
||||
|
||||
return ($pid);
|
||||
}
|
||||
|
||||
|
||||
##############
|
||||
# TEST SETUP #
|
||||
##############
|
||||
|
||||
sub generate_test_config {
|
||||
my ($autorun, $close_when_done, $log_path, $test_path) = @_;
|
||||
$autorun = $autorun || 0;
|
||||
$close_when_done = $close_when_done || 0;
|
||||
$log_path = $log_path || "";
|
||||
$log_path =~ s/\\/\\\\/g;
|
||||
$test_path = $test_path || "";
|
||||
$test_path =~ s/\\/\\\\/g;
|
||||
|
||||
my $config_content = <<CONFIGEND;
|
||||
({
|
||||
autoRun: $autorun,
|
||||
closeWhenDone: $close_when_done,
|
||||
logPath: "$log_path",
|
||||
testPath: "$test_path"
|
||||
})
|
||||
CONFIGEND
|
||||
|
||||
open(CONFIGOUTFILE, ">$profile_dir/testConfig.js") ||
|
||||
die("Could not open testConfig.js file $!");
|
||||
print CONFIGOUTFILE ($config_content);
|
||||
close(CONFIGOUTFILE);
|
||||
}
|
||||
|
||||
sub initializeProfile {
|
||||
my ($app_path, $do_browser_tests) = @_;
|
||||
my $pref_content = <<PREFEND;
|
||||
user_pref("browser.dom.window.dump.enabled", true);
|
||||
user_pref("dom.disable_open_during_load", false);
|
||||
user_pref("dom.max_script_run_time", 0); // no slow script dialogs
|
||||
user_pref("signed.applets.codebase_principal_support", true);
|
||||
user_pref("security.warn_submit_insecure", false);
|
||||
user_pref("browser.shell.checkDefaultBrowser", false);
|
||||
user_pref("browser.warnOnQuit", false);
|
||||
user_pref("accessibility.typeaheadfind.autostart", false);
|
||||
user_pref("javascript.options.showInConsole", true);
|
||||
user_pref("layout.debug.enable_data_xbl", true);
|
||||
user_pref("browser.EULA.override", true);
|
||||
PREFEND
|
||||
|
||||
# Grant God-power to all the servers on which tests can run.
|
||||
my $i = 1;
|
||||
my $server;
|
||||
foreach $server (@servers) {
|
||||
$pref_content .= <<SERVERPREFEND;
|
||||
user_pref("capability.principal.codebase.p$i.granted", "UniversalXPConnect UniversalBrowserRead UniversalBrowserWrite UniversalPreferencesRead UniversalPreferencesWrite UniversalFileRead");
|
||||
user_pref("capability.principal.codebase.p$i.id", "http://$server");
|
||||
user_pref("capability.principal.codebase.p$i.subjectName", "");
|
||||
SERVERPREFEND
|
||||
$i++;
|
||||
}
|
||||
|
||||
# Now add the two servers that do NOT have God-power so we can properly test
|
||||
# the granting and receiving of God-power
|
||||
push(@servers,
|
||||
"sectest2.example.org:80",
|
||||
"sub.sectest1.example.org:80");
|
||||
|
||||
|
||||
# Now actually create the preference to make the proxying happen, stripping
|
||||
# off the first server because it's the one to which we proxy all the others.
|
||||
my $quotedServers = join(", ", map("'" . $_ . "'", @servers[1 .. $#servers]));
|
||||
|
||||
my $pacURL = "data:text/plain,";
|
||||
$pacURL .= "function FindProxyForURL(url, host) ";
|
||||
$pacURL .= "{ ";
|
||||
$pacURL .= " var servers = [$quotedServers]; ";
|
||||
$pacURL .= " var regex = ";
|
||||
$pacURL .= " new RegExp('http://(?:[^/@]*@)?(.*?(:\\\\\\\\d+)?)/'); ";
|
||||
$pacURL .= " var matches = regex.exec(url); ";
|
||||
$pacURL .= " if (!matches) ";
|
||||
$pacURL .= " return 'DIRECT'; ";
|
||||
$pacURL .= " var hostport = matches[1], port = matches[2]; ";
|
||||
$pacURL .= " if (!port) ";
|
||||
$pacURL .= " hostport += ':80'; ";
|
||||
$pacURL .= " if (servers.indexOf(hostport) >= 0) ";
|
||||
$pacURL .= " return 'PROXY localhost:8888'; ";
|
||||
$pacURL .= " return 'DIRECT'; ";
|
||||
$pacURL .= "}";
|
||||
|
||||
$pref_content .= <<PROXYPREFEND;
|
||||
user_pref("network.proxy.type", 2);
|
||||
user_pref("network.proxy.autoconfig_url", "$pacURL");
|
||||
PROXYPREFEND
|
||||
|
||||
|
||||
my $chrome_content = <<CHROMEEND;
|
||||
\@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); /* set default namespace to XUL */
|
||||
toolbar,
|
||||
toolbarpalette {
|
||||
background-color: rgb(235, 235, 235) !important;
|
||||
}
|
||||
toolbar#nav-bar {
|
||||
background-image: none !important;
|
||||
}
|
||||
CHROMEEND
|
||||
|
||||
# in case we died for some reason on the last run
|
||||
rmtree($profile_dir, 0, 0);
|
||||
|
||||
my $chrome_dir = "$profile_dir/chrome";
|
||||
mkdir($profile_dir);
|
||||
mkdir($chrome_dir);
|
||||
|
||||
# append magic prefs to user.js
|
||||
open(PREFOUTFILE, ">>$profile_dir/user.js") ||
|
||||
die("Could not open user.js file $!\n");
|
||||
print PREFOUTFILE ($pref_content);
|
||||
close(PREFOUTFILE) or die("Couldn't close user.js file: $!\n");
|
||||
|
||||
# add userChrome.css
|
||||
open(CHROMEOUTFILE, ">>$chrome_dir/userChrome.css") ||
|
||||
die("Could not open userChrome.css file $!");
|
||||
print CHROMEOUTFILE ($chrome_content);
|
||||
close(CHROMEOUTFILE);
|
||||
|
||||
# register our chrome dir
|
||||
my $chrometest_dir = "$FindBin::Bin/";
|
||||
if ($is_win32) {
|
||||
# we don't have LWP, so we can't use its file url stuff
|
||||
$chrometest_dir = winPathFromDir($chrometest_dir) . "\\";
|
||||
$chrometest_dir =~ s/\\/\//g;
|
||||
$chrometest_dir = "file:///$chrometest_dir";
|
||||
}
|
||||
|
||||
my($filename, $directories, $suffix) = fileparse($app_path);
|
||||
my $manifest = $directories . "chrome/mochikit.manifest";
|
||||
open(MANIFEST, ">$manifest") ||
|
||||
die("Could not open manifest file $!");
|
||||
print MANIFEST ("content mochikit $chrometest_dir contentaccessible=yes\n");
|
||||
if ($do_browser_tests) {
|
||||
print MANIFEST ("overlay " . BROWSER_CHROME_URL . " chrome://mochikit/content/browser-test-overlay.xul\n");
|
||||
}
|
||||
close(MANIFEST);
|
||||
|
||||
return $manifest;
|
||||
}
|
||||
|
||||
###################
|
||||
# WAIT FOR SERVER #
|
||||
###################
|
||||
|
||||
sub wait_for_server_startup {
|
||||
my ($pid, $timeout) = @_;
|
||||
|
||||
die ("Invalid timeout value passed to wait_for_server_startup()\n")
|
||||
if ($timeout <= 0);
|
||||
|
||||
eval {
|
||||
my $loop_count = 0;
|
||||
while ($loop_count++ < $timeout) {
|
||||
last if (-e "$profile_dir/server_alive.txt");
|
||||
sleep 1;
|
||||
}
|
||||
|
||||
die "timeout" if ($loop_count >= $timeout);
|
||||
return "done";
|
||||
};
|
||||
|
||||
my $time_out_message;
|
||||
if ($@) {
|
||||
if ($@ =~ /timeout/) {
|
||||
$time_out_message = "\nError: ";
|
||||
$time_out_message = "Timed out while waiting for server startup.\n";
|
||||
} else {
|
||||
# Died for some other reason.
|
||||
$time_out_message = "An unknown error occurred ";
|
||||
$time_out_message .= "while waiting for server startup.\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($time_out_message) {
|
||||
kill_process($pid);
|
||||
print $time_out_message;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
sub kill_process {
|
||||
my ($target_pid) = @_;
|
||||
my $start_time = time();
|
||||
|
||||
# Try to kill and wait 10 seconds, then try a kill -9
|
||||
my $sig;
|
||||
for $sig ('TERM', 'KILL') {
|
||||
print "kill $sig $target_pid\n";
|
||||
kill $sig => $target_pid;
|
||||
my $interval_start = time;
|
||||
while (time - $interval_start < 10) {
|
||||
# the following will work with 'cygwin' perl on win32, but not
|
||||
# with 'MSWin32' (ActiveState) perl
|
||||
my $pid = waitpid($target_pid, POSIX::WNOHANG());
|
||||
if (($pid == $target_pid and POSIX::WIFEXITED($?)) or $pid == -1) {
|
||||
my $secs = time - $start_time;
|
||||
$secs = $secs == 1 ? '1 second' : "$secs seconds";
|
||||
print "Process killed. Took $secs to die.\n";
|
||||
return;
|
||||
}
|
||||
sleep 1;
|
||||
}
|
||||
}
|
||||
die "Unable to kill process: $target_pid";
|
||||
}
|
||||
|
||||
##################
|
||||
# TEST EXECUTION #
|
||||
##################
|
||||
|
||||
sub runTests {
|
||||
my ($test_url, $browser_env, $browser_args) = @_;
|
||||
|
||||
# mark the start
|
||||
my $test_start = localtime();
|
||||
|
||||
# set env vars so Firefox doesn't quit weirdly and break the script
|
||||
$ENV{'NO_EM_RESTART'} = '1';
|
||||
$ENV{'XPCOM_DEBUG_BREAK'} = 'warn';
|
||||
|
||||
if ($unixish) {
|
||||
$ENV{'LD_LIBRARY_PATH'} = $dist_bin;
|
||||
$ENV{'MOZILLA_FIVE_HOME'} = $dist_bin;
|
||||
}
|
||||
|
||||
for my $key (keys(%{$browser_env})) {
|
||||
$ENV{$key} = $browser_env->{$key};
|
||||
}
|
||||
|
||||
my $profile_arg = "$profile_dir";
|
||||
if ($is_win32) {
|
||||
$profile_arg = winPathFromDir($profile_dir);
|
||||
}
|
||||
|
||||
# now run with the profile we created
|
||||
|
||||
# On Windows and Linux, the application is focused for us. On OS X, we
|
||||
# need to use applescript to focus the app and then set the url.
|
||||
my $rc = -1;
|
||||
if (!$is_mac) {
|
||||
my @runargs = ($app, '-no-remote', '-profile', $profile_arg);
|
||||
if ($browser_args) {
|
||||
foreach my $key (keys %$browser_args) {
|
||||
push(@runargs, ($key,
|
||||
$browser_args->{$key})
|
||||
);
|
||||
}
|
||||
}
|
||||
push(@runargs, $test_url);
|
||||
$rc = 0xffff & system @runargs;
|
||||
} else {
|
||||
$rc = executeMac($profile_arg, $test_url, $browser_args);
|
||||
}
|
||||
|
||||
if ($rc != 0) {
|
||||
print "FAIL Exited with code $rc during test run\n";
|
||||
}
|
||||
|
||||
return $test_start;
|
||||
}
|
||||
|
||||
sub executeMac {
|
||||
my ($profile_arg, $test_url, $browser_args) = @_;
|
||||
my $pid = fork();
|
||||
if (not defined $pid) {
|
||||
die "cannot fork: $!";
|
||||
} elsif ($pid == 0) {
|
||||
# run only the executable so we get a pid we can focus
|
||||
if ($app !~ /-bin$/) {
|
||||
$app .= "-bin";
|
||||
}
|
||||
my @runargs = ($app, '-foreground', '-no-remote', '-profile', $profile_arg);
|
||||
if ($browser_args) {
|
||||
foreach my $key (keys %$browser_args) {
|
||||
push(@runargs, ($key,
|
||||
$browser_args->{$key})
|
||||
);
|
||||
}
|
||||
}
|
||||
push(@runargs, $test_url);
|
||||
|
||||
# redirect stderr to stdout for easier buildbot / tinderbox logging
|
||||
#$ENV{'XPCOM_DEBUG_BREAK'} = 'stack';
|
||||
open (STDERR, '>&', \*STDOUT) || die $!;
|
||||
exec @runargs or die("Error starting application: $!\n");
|
||||
} else {
|
||||
waitpid($pid,0);
|
||||
}
|
||||
|
||||
# return the exit code we received from waitpid
|
||||
return $?;
|
||||
}
|
||||
|
||||
##################
|
||||
# SHUT DOWN #
|
||||
##################
|
||||
|
||||
sub shutdownServer {
|
||||
my ($pid) = @_;
|
||||
kill_process($pid);
|
||||
}
|
|
@ -0,0 +1,504 @@
|
|||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Mozilla Foundation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 1998
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Robert Sayre <sayrer@gmail.com>
|
||||
# Jeff Walden <jwalden+bmo@mit.edu>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
"""
|
||||
Runs the Mochitest test harness.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
from urllib import quote_plus as encodeURIComponent
|
||||
import urllib2
|
||||
import commands
|
||||
import automation
|
||||
|
||||
# Path to the test script on the server
|
||||
TEST_SERVER_HOST = "localhost:8888"
|
||||
TEST_PATH = "/tests/"
|
||||
CHROME_PATH = "/redirect.html";
|
||||
A11Y_PATH = "/redirect-a11y.html"
|
||||
TESTS_URL = "http://" + TEST_SERVER_HOST + TEST_PATH
|
||||
CHROMETESTS_URL = "http://" + TEST_SERVER_HOST + CHROME_PATH
|
||||
A11YTESTS_URL = "http://" + TEST_SERVER_HOST + A11Y_PATH
|
||||
SERVER_SHUTDOWN_URL = "http://" + TEST_SERVER_HOST + "/server/shutdown"
|
||||
# main browser chrome URL, same as browser.chromeURL pref
|
||||
#ifdef MOZ_SUITE
|
||||
BROWSER_CHROME_URL = "chrome://navigator/content/navigator.xul"
|
||||
#else
|
||||
BROWSER_CHROME_URL = "chrome://browser/content/browser.xul"
|
||||
#endif
|
||||
|
||||
# Max time in seconds to wait for server startup before tests will fail -- if
|
||||
# this seems big, it's mostly for debug machines where cold startup
|
||||
# (particularly after a build) takes forever.
|
||||
SERVER_STARTUP_TIMEOUT = 45
|
||||
|
||||
SCRIPT_DIRECTORY = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
|
||||
os.chdir(SCRIPT_DIRECTORY)
|
||||
|
||||
PROFILE_DIRECTORY = os.path.abspath("./mochitesttestingprofile")
|
||||
|
||||
LEAK_REPORT_FILE = PROFILE_DIRECTORY + "/" + "leaks-report.log"
|
||||
|
||||
log = logging.getLogger()
|
||||
|
||||
|
||||
#######################
|
||||
# COMMANDLINE OPTIONS #
|
||||
#######################
|
||||
|
||||
class MochitestOptions(optparse.OptionParser):
|
||||
"""Parses Mochitest commandline options."""
|
||||
def __init__(self, **kwargs):
|
||||
optparse.OptionParser.__init__(self, **kwargs)
|
||||
defaults = {}
|
||||
|
||||
self.add_option("--close-when-done",
|
||||
action = "store_true", dest = "closeWhenDone",
|
||||
help = "close the application when tests are done running")
|
||||
defaults["closeWhenDone"] = False
|
||||
|
||||
self.add_option("--appname",
|
||||
action = "store", type = "string", dest = "app",
|
||||
help = "absolute path to application, overriding default")
|
||||
defaults["app"] = automation.DEFAULT_APP
|
||||
|
||||
self.add_option("--log-file",
|
||||
action = "store", type = "string",
|
||||
dest = "logFile", metavar = "FILE",
|
||||
help = "file to which logging occurs")
|
||||
defaults["logFile"] = ""
|
||||
|
||||
self.add_option("--autorun",
|
||||
action = "store_true", dest = "autorun",
|
||||
help = "start running tests when the application starts")
|
||||
defaults["autorun"] = False
|
||||
|
||||
LOG_LEVELS = ("DEBUG", "INFO", "WARNING", "ERROR", "FATAL")
|
||||
LEVEL_STRING = ", ".join(LOG_LEVELS)
|
||||
|
||||
self.add_option("--console-level",
|
||||
action = "store", type = "choice", dest = "consoleLevel",
|
||||
choices = LOG_LEVELS, metavar = "LEVEL",
|
||||
help = "one of %s to determine the level of console "
|
||||
"logging" % LEVEL_STRING)
|
||||
defaults["consoleLevel"] = None
|
||||
|
||||
self.add_option("--file-level",
|
||||
action = "store", type = "choice", dest = "fileLevel",
|
||||
choices = LOG_LEVELS, metavar = "LEVEL",
|
||||
help = "one of %s to determine the level of file "
|
||||
"logging if a file has been specified, defaulting "
|
||||
"to INFO" % LEVEL_STRING)
|
||||
defaults["fileLevel"] = "INFO"
|
||||
|
||||
self.add_option("--chrome",
|
||||
action = "store_true", dest = "chrome",
|
||||
help = "run chrome Mochitests")
|
||||
defaults["chrome"] = False
|
||||
|
||||
self.add_option("--test-path",
|
||||
action = "store", type = "string", dest = "testPath",
|
||||
help = "start in the given directory's tests")
|
||||
defaults["testPath"] = ""
|
||||
|
||||
self.add_option("--browser-chrome",
|
||||
action = "store_true", dest = "browserChrome",
|
||||
help = "run browser chrome Mochitests")
|
||||
defaults["browserChrome"] = False
|
||||
|
||||
self.add_option("--a11y",
|
||||
action = "store_true", dest = "a11y",
|
||||
help = "run accessibility Mochitests");
|
||||
|
||||
self.add_option("--setenv",
|
||||
action = "append", type = "string",
|
||||
dest = "environment", metavar = "NAME=VALUE",
|
||||
help = "sets the given variable in the application's "
|
||||
"environment")
|
||||
defaults["environment"] = []
|
||||
|
||||
self.add_option("--browser-arg",
|
||||
action = "append", type = "string",
|
||||
dest = "browserArgs", metavar = "ARG",
|
||||
help = "provides an argument to the test application")
|
||||
defaults["browserArgs"] = []
|
||||
|
||||
self.add_option("--leak-threshold",
|
||||
action = "store", type = "int",
|
||||
dest = "leakThreshold", metavar = "THRESHOLD",
|
||||
help = "fail if the number of bytes leaked through "
|
||||
"refcounted objects (or bytes in classes with "
|
||||
"MOZ_COUNT_CTOR and MOZ_COUNT_DTOR) is greater "
|
||||
"than the given number")
|
||||
defaults["leakThreshold"] = 1.0e3000
|
||||
|
||||
self.add_option("--fatal-assertions",
|
||||
action = "store_true", dest = "fatalAssertions",
|
||||
help = "abort testing whenever an assertion is hit "
|
||||
"(requires a debug build to be effective)")
|
||||
defaults["fatalAssertions"] = False
|
||||
|
||||
# -h, --help are automatically handled by OptionParser
|
||||
|
||||
self.set_defaults(**defaults)
|
||||
|
||||
usage = """\
|
||||
Usage instructions for runtests.py.
|
||||
All arguments are optional.
|
||||
If --chrome is specified, chrome tests will be run instead of web content tests.
|
||||
If --browser-chrome is specified, browser-chrome tests will be run instead of web content tests.
|
||||
See <http://mochikit.com/doc/html/MochiKit/Logging.html> for details on the logging levels."""
|
||||
self.set_usage(usage)
|
||||
|
||||
|
||||
|
||||
#######################
|
||||
# HTTP SERVER SUPPORT #
|
||||
#######################
|
||||
|
||||
class MochitestServer:
|
||||
"Web server used to serve Mochitests, for closer fidelity to the real web."
|
||||
|
||||
def __init__(self, options):
|
||||
self._closeWhenDone = options.closeWhenDone
|
||||
|
||||
def start(self):
|
||||
"Run the Mochitest server, returning the process ID of the server."
|
||||
|
||||
env = dict(os.environ)
|
||||
if automation.UNIXISH:
|
||||
env["LD_LIBRARY_PATH"] = automation.DIST_BIN
|
||||
env["MOZILLA_FIVE_HOME"] = automation.DIST_BIN
|
||||
env["XPCOM_DEBUG_BREAK"] = "warn"
|
||||
|
||||
args = ["-v", "170",
|
||||
"-f", "./" + "httpd.js",
|
||||
"-f", "./" + "server.js"]
|
||||
|
||||
xpcshell = automation.DIST_BIN + "/" + "xpcshell";
|
||||
self._process = automation.Process(xpcshell, args, env = env)
|
||||
pid = self._process.pid
|
||||
if pid < 0:
|
||||
print "Error starting server."
|
||||
sys.exit(2)
|
||||
log.info("Server pid: %d", pid)
|
||||
|
||||
|
||||
def ensureReady(self, timeout):
|
||||
assert timeout >= 0
|
||||
|
||||
aliveFile = PROFILE_DIRECTORY + "/" + "server_alive.txt"
|
||||
i = 0
|
||||
while i < timeout:
|
||||
if os.path.exists(aliveFile):
|
||||
break
|
||||
time.sleep(1)
|
||||
i += 1
|
||||
else:
|
||||
print "Timed out while waiting for server startup."
|
||||
self.stop()
|
||||
sys.exit(1)
|
||||
|
||||
def stop(self):
|
||||
try:
|
||||
c = urllib2.urlopen(SERVER_SHUTDOWN_URL)
|
||||
c.read()
|
||||
c.close()
|
||||
self._process.wait()
|
||||
except:
|
||||
self._process.kill()
|
||||
|
||||
|
||||
#################
|
||||
# MAIN FUNCTION #
|
||||
#################
|
||||
|
||||
def main():
|
||||
parser = MochitestOptions()
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if not os.path.exists(options.app):
|
||||
msg = """\
|
||||
Error: Path %(app)s doesn't exist.
|
||||
Are you executing $objdir/_tests/testing/mochitest/runtests.py?"""
|
||||
print msg % {"app": options.app}
|
||||
sys.exit(1)
|
||||
|
||||
# browser environment
|
||||
browserEnv = dict(os.environ)
|
||||
|
||||
# These variables are necessary for correct application startup; change
|
||||
# via the commandline at your own risk.
|
||||
browserEnv["NO_EM_RESTART"] = "1"
|
||||
browserEnv["XPCOM_DEBUG_BREAK"] = "warn"
|
||||
if automation.UNIXISH:
|
||||
browserEnv["LD_LIBRARY_PATH"] = automation.DIST_BIN
|
||||
browserEnv["MOZILLA_FIVE_HOME"] = automation.DIST_BIN
|
||||
browserEnv["GNOME_DISABLE_CRASH_DIALOG"] = "1"
|
||||
|
||||
for v in options.environment:
|
||||
ix = v.find("=")
|
||||
if ix <= 0:
|
||||
print "Error: syntax error in --setenv=" + v
|
||||
sys.exit(1)
|
||||
browserEnv[v[:ix]] = v[ix + 1:]
|
||||
|
||||
automation.initializeProfile(PROFILE_DIRECTORY)
|
||||
manifest = addChromeToProfile(options)
|
||||
server = MochitestServer(options)
|
||||
server.start()
|
||||
|
||||
# If we're lucky, the server has fully started by now, and all paths are
|
||||
# ready, etc. However, xpcshell cold start times suck, at least for debug
|
||||
# builds. We'll try to connect to the server for awhile, and if we fail,
|
||||
# we'll try to kill the server and exit with an error.
|
||||
server.ensureReady(SERVER_STARTUP_TIMEOUT)
|
||||
|
||||
|
||||
# URL parameters to test URL:
|
||||
#
|
||||
# autorun -- kick off tests automatically
|
||||
# closeWhenDone -- runs quit.js after tests
|
||||
# logFile -- logs test run to an absolute path
|
||||
#
|
||||
|
||||
# consoleLevel, fileLevel: set the logging level of the console and
|
||||
# file logs, if activated.
|
||||
# <http://mochikit.com/doc/html/MochiKit/Logging.html>
|
||||
|
||||
testURL = TESTS_URL + options.testPath
|
||||
urlOpts = []
|
||||
if options.chrome:
|
||||
testURL = CHROMETESTS_URL
|
||||
if options.testPath:
|
||||
urlOpts.append("testPath=" + encodeURIComponent(options.testPath))
|
||||
elif options.a11y:
|
||||
testURL = A11YTESTS_URL
|
||||
if options.testPath:
|
||||
urlOpts.append("testPath=" + encodeURIComponent(options.testPath))
|
||||
elif options.browserChrome:
|
||||
testURL = "about:blank"
|
||||
|
||||
if options.browserChrome:
|
||||
makeTestConfig(options)
|
||||
else:
|
||||
if options.autorun:
|
||||
urlOpts.append("autorun=1")
|
||||
if options.closeWhenDone:
|
||||
urlOpts.append("closeWhenDone=1")
|
||||
if options.logFile:
|
||||
urlOpts.append("logFile=" + encodeURIComponent(options.logFile))
|
||||
urlOpts.append("fileLevel=" + encodeURIComponent(options.fileLevel))
|
||||
if options.consoleLevel:
|
||||
urlOpts.append("consoleLevel=" + encodeURIComponent(options.consoleLevel))
|
||||
if len(urlOpts) > 0:
|
||||
testURL += "?" + "&".join(urlOpts)
|
||||
|
||||
browserEnv["XPCOM_MEM_BLOAT_LOG"] = LEAK_REPORT_FILE
|
||||
|
||||
if options.fatalAssertions:
|
||||
browserEnv["XPCOM_DEBUG_BREAK"] = "stack-and-abort"
|
||||
|
||||
start = automation.runApp(testURL, browserEnv, options.app, PROFILE_DIRECTORY,
|
||||
options.browserArgs)
|
||||
|
||||
# Server's no longer needed, and perhaps more importantly, anything it might
|
||||
# spew to console shouldn't disrupt the leak information table we print next.
|
||||
server.stop()
|
||||
|
||||
if not os.path.exists(LEAK_REPORT_FILE):
|
||||
log.info("WARNING refcount logging is off, so leaks can't be detected!")
|
||||
else:
|
||||
leaks = open(LEAK_REPORT_FILE, "r")
|
||||
for line in leaks:
|
||||
log.info(line.rstrip())
|
||||
leaks.close()
|
||||
|
||||
threshold = options.leakThreshold
|
||||
leaks = open(LEAK_REPORT_FILE, "r")
|
||||
# Per-Inst Leaked Total Rem ...
|
||||
# 0 TOTAL 17 192 419115886 2 ...
|
||||
# 833 nsTimerImpl 60 120 24726 2 ...
|
||||
lineRe = re.compile(r"^\s*\d+\s+(?P<name>\S+)\s+"
|
||||
r"(?P<size>\d+)\s+(?P<bytesLeaked>\d+)\s+"
|
||||
r"\d+\s+(?P<numLeaked>\d+)")
|
||||
thresholdExceeded = False
|
||||
seenTotal = False
|
||||
prefix = "WARNING"
|
||||
for line in leaks:
|
||||
matches = lineRe.match(line)
|
||||
if not matches:
|
||||
continue
|
||||
name = matches.group("name")
|
||||
if "TOTAL" == name:
|
||||
seenTotal = True
|
||||
bytesLeaked = int(matches.group("bytesLeaked"))
|
||||
if bytesLeaked > threshold:
|
||||
thresholdExceeded = True
|
||||
prefix = "ERROR FAIL"
|
||||
log.info("ERROR FAIL leaked %d bytes during test execution (should "
|
||||
"have leaked no more than %d bytes)",
|
||||
bytesLeaked, threshold)
|
||||
elif bytesLeaked > 0:
|
||||
log.info("WARNING leaked %d bytes during test execution",
|
||||
bytesLeaked)
|
||||
else:
|
||||
numLeaked = int(matches.group("numLeaked"))
|
||||
if numLeaked != 0:
|
||||
if numLeaked > 1:
|
||||
instance = "instances"
|
||||
rest = " each (%s bytes total)" % matches.group("bytesLeaked")
|
||||
else:
|
||||
instance = "instance"
|
||||
rest = ""
|
||||
log.info("%(prefix)s leaked %(numLeaked)d %(instance)s of %(name)s "
|
||||
"with size %(size)s bytes%(rest)s" %
|
||||
{ "prefix": prefix,
|
||||
"numLeaked": numLeaked,
|
||||
"instance": instance,
|
||||
"name": name,
|
||||
"size": matches.group("size"),
|
||||
"rest": rest })
|
||||
if not seenTotal:
|
||||
log.info("ERROR FAIL missing output line for total leaks!")
|
||||
leaks.close()
|
||||
|
||||
|
||||
# print test run times
|
||||
finish = datetime.now()
|
||||
log.info(" started: %s", str(start))
|
||||
log.info("finished: %s", str(finish))
|
||||
|
||||
# delete the profile and manifest
|
||||
os.remove(manifest)
|
||||
|
||||
# hanging due to non-halting threads is no fun; assume we hit the errors we
|
||||
# were going to hit already and exit with a success code
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
|
||||
#######################
|
||||
# CONFIGURATION SETUP #
|
||||
#######################
|
||||
|
||||
def makeTestConfig(options):
|
||||
"Creates a test configuration file for customizing test execution."
|
||||
def boolString(b):
|
||||
if b:
|
||||
return "true"
|
||||
return "false"
|
||||
|
||||
logFile = options.logFile.replace("\\", "\\\\")
|
||||
testPath = options.testPath.replace("\\", "\\\\")
|
||||
content = """\
|
||||
({
|
||||
autoRun: %(autorun)s,
|
||||
closeWhenDone: %(closeWhenDone)s,
|
||||
logPath: "%(logPath)s",
|
||||
testPath: "%(testPath)s"
|
||||
})""" % {"autorun": boolString(options.autorun),
|
||||
"closeWhenDone": boolString(options.closeWhenDone),
|
||||
"logPath": logFile,
|
||||
"testPath": testPath}
|
||||
|
||||
config = open(PROFILE_DIRECTORY + "/" + "testConfig.js", "w")
|
||||
config.write(content)
|
||||
config.close()
|
||||
|
||||
|
||||
def addChromeToProfile(options):
|
||||
"Adds MochiKit chrome tests to the profile."
|
||||
|
||||
chromedir = PROFILE_DIRECTORY + "/" + "chrome"
|
||||
os.mkdir(chromedir)
|
||||
|
||||
chrome = []
|
||||
|
||||
part = """
|
||||
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); /* set default namespace to XUL */
|
||||
toolbar,
|
||||
toolbarpalette {
|
||||
background-color: rgb(235, 235, 235) !important;
|
||||
}
|
||||
toolbar#nav-bar {
|
||||
background-image: none !important;
|
||||
}
|
||||
"""
|
||||
chrome.append(part)
|
||||
|
||||
|
||||
|
||||
# write userChrome.css
|
||||
chromeFile = open(PROFILE_DIRECTORY + "/" + "userChrome.css", "a")
|
||||
chromeFile.write("".join(chrome))
|
||||
chromeFile.close()
|
||||
|
||||
|
||||
# register our chrome dir
|
||||
chrometestDir = os.path.abspath(".") + "/"
|
||||
if automation.IS_WIN32:
|
||||
chrometestDir = "file:///" + chrometestDir.replace("\\", "/")
|
||||
|
||||
|
||||
(path, leaf) = os.path.split(options.app)
|
||||
manifest = path + "/" + "chrome/mochikit.manifest"
|
||||
manifestFile = open(manifest, "w")
|
||||
manifestFile.write("content mochikit " + chrometestDir + " contentaccessible=yes\n")
|
||||
if options.browserChrome:
|
||||
overlayLine = "overlay " + BROWSER_CHROME_URL + " " \
|
||||
"chrome://mochikit/content/browser-test-overlay.xul\n"
|
||||
manifestFile.write(overlayLine)
|
||||
manifestFile.close()
|
||||
|
||||
return manifest
|
||||
|
||||
#########
|
||||
# DO IT #
|
||||
#########
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,480 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is MozJSHTTP code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Jeff Walden <jwalden+code@mit.edu>.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2006
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Robert Sayre <sayrer@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// Note that the server script itself already defines Cc, Ci, and Cr for us,
|
||||
// and because they're constants it's not safe to redefine them. Scope leakage
|
||||
// sucks.
|
||||
|
||||
const SERVER_PORT = 8888;
|
||||
var server; // for use in the shutdown handler, if necessary
|
||||
|
||||
//
|
||||
// HTML GENERATION
|
||||
//
|
||||
var tags = ['A', 'ABBR', 'ACRONYM', 'ADDRESS', 'APPLET', 'AREA', 'B', 'BASE',
|
||||
'BASEFONT', 'BDO', 'BIG', 'BLOCKQUOTE', 'BODY', 'BR', 'BUTTON',
|
||||
'CAPTION', 'CENTER', 'CITE', 'CODE', 'COL', 'COLGROUP', 'DD',
|
||||
'DEL', 'DFN', 'DIR', 'DIV', 'DL', 'DT', 'EM', 'FIELDSET', 'FONT',
|
||||
'FORM', 'FRAME', 'FRAMESET', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6',
|
||||
'HEAD', 'HR', 'HTML', 'I', 'IFRAME', 'IMG', 'INPUT', 'INS',
|
||||
'ISINDEX', 'KBD', 'LABEL', 'LEGEND', 'LI', 'LINK', 'MAP', 'MENU',
|
||||
'META', 'NOFRAMES', 'NOSCRIPT', 'OBJECT', 'OL', 'OPTGROUP',
|
||||
'OPTION', 'P', 'PARAM', 'PRE', 'Q', 'S', 'SAMP', 'SCRIPT',
|
||||
'SELECT', 'SMALL', 'SPAN', 'STRIKE', 'STRONG', 'STYLE', 'SUB',
|
||||
'SUP', 'TABLE', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD',
|
||||
'TITLE', 'TR', 'TT', 'U', 'UL', 'VAR'];
|
||||
|
||||
/**
|
||||
* Below, we'll use makeTagFunc to create a function for each of the
|
||||
* strings in 'tags'. This will allow us to use s-expression like syntax
|
||||
* to create HTML.
|
||||
*/
|
||||
function makeTagFunc(tagName)
|
||||
{
|
||||
return function (attrs /* rest... */)
|
||||
{
|
||||
var startChildren = 0;
|
||||
var response = "";
|
||||
|
||||
// write the start tag and attributes
|
||||
response += "<" + tagName;
|
||||
// if attr is an object, write attributes
|
||||
if (attrs && typeof attrs == 'object') {
|
||||
startChildren = 1;
|
||||
for (var [key,value] in attrs) {
|
||||
var val = "" + value;
|
||||
response += " " + key + '="' + val.replace('"','"') + '"';
|
||||
}
|
||||
}
|
||||
response += ">";
|
||||
|
||||
// iterate through the rest of the args
|
||||
for (var i = startChildren; i < arguments.length; i++) {
|
||||
if (typeof arguments[i] == 'function') {
|
||||
response += arguments[i]();
|
||||
} else {
|
||||
response += arguments[i];
|
||||
}
|
||||
}
|
||||
|
||||
// write the close tag
|
||||
response += "</" + tagName + ">\n";
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
function makeTags() {
|
||||
// map our global HTML generation functions
|
||||
for each (var tag in tags) {
|
||||
this[tag] = makeTagFunc(tag.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
// only run the "main" section if httpd.js was loaded ahead of us
|
||||
if (this["nsHttpServer"]) {
|
||||
//
|
||||
// SCRIPT CODE
|
||||
//
|
||||
runServer();
|
||||
|
||||
// We can only have gotten here if the /server/shutdown path was requested,
|
||||
// and we can shut down the xpcshell now that all testing requests have been
|
||||
// served.
|
||||
quit(0);
|
||||
}
|
||||
|
||||
var serverBasePath;
|
||||
|
||||
//
|
||||
// SERVER SETUP
|
||||
//
|
||||
function runServer()
|
||||
{
|
||||
serverBasePath = Cc["@mozilla.org/file/local;1"]
|
||||
.createInstance(Ci.nsILocalFile);
|
||||
var procDir = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIProperties).get("CurProcD", Ci.nsIFile);
|
||||
serverBasePath.initWithPath(procDir.parent.parent.path);
|
||||
serverBasePath.append("_tests");
|
||||
serverBasePath.append("testing");
|
||||
serverBasePath.append("mochitest");
|
||||
server = new nsHttpServer();
|
||||
server.registerDirectory("/", serverBasePath);
|
||||
|
||||
server.registerPathHandler("/server/shutdown", serverShutdown);
|
||||
|
||||
server.registerContentType("sjs", "sjs"); // .sjs == CGI-like functionality
|
||||
|
||||
server.setIndexHandler(defaultDirHandler);
|
||||
server.start(SERVER_PORT);
|
||||
|
||||
// touch a file in the profile directory to indicate we're alive
|
||||
var foStream = Cc["@mozilla.org/network/file-output-stream;1"]
|
||||
.createInstance(Ci.nsIFileOutputStream);
|
||||
var serverAlive = Cc["@mozilla.org/file/local;1"]
|
||||
.createInstance(Ci.nsILocalFile);
|
||||
serverAlive.initWithFile(serverBasePath);
|
||||
serverAlive.append("mochitesttestingprofile");
|
||||
|
||||
// If we're running outside of the test harness, there might
|
||||
// not be a test profile directory present
|
||||
if (serverAlive.exists()) {
|
||||
serverAlive.append("server_alive.txt");
|
||||
foStream.init(serverAlive,
|
||||
0x02 | 0x08 | 0x20, 0664, 0); // write, create, truncate
|
||||
data = "It's alive!";
|
||||
foStream.write(data, data.length);
|
||||
foStream.close();
|
||||
}
|
||||
|
||||
makeTags();
|
||||
|
||||
//
|
||||
// The following is threading magic to spin an event loop -- this has to
|
||||
// happen manually in xpcshell for the server to actually work.
|
||||
//
|
||||
var thread = Cc["@mozilla.org/thread-manager;1"]
|
||||
.getService()
|
||||
.currentThread;
|
||||
while (!server.isStopped())
|
||||
thread.processNextEvent(true);
|
||||
|
||||
// Server stopped by /server/shutdown handler -- go through pending events
|
||||
// and return.
|
||||
|
||||
// get rid of any pending requests
|
||||
while (thread.hasPendingEvents())
|
||||
thread.processNextEvent(true);
|
||||
}
|
||||
|
||||
// PATH HANDLERS
|
||||
|
||||
// /server/shutdown
|
||||
function serverShutdown(metadata, response)
|
||||
{
|
||||
response.setStatusLine("1.1", 200, "OK");
|
||||
response.setHeader("Content-type", "text/plain", false);
|
||||
|
||||
var body = "Server shut down.";
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
|
||||
// Note: this doesn't disrupt the current request.
|
||||
server.stop();
|
||||
}
|
||||
|
||||
//
|
||||
// DIRECTORY LISTINGS
|
||||
//
|
||||
|
||||
/**
|
||||
* Creates a generator that iterates over the contents of
|
||||
* an nsIFile directory.
|
||||
*/
|
||||
function dirIter(dir)
|
||||
{
|
||||
var en = dir.directoryEntries;
|
||||
while (en.hasMoreElements()) {
|
||||
var file = en.getNext();
|
||||
yield file.QueryInterface(Ci.nsILocalFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an optionally nested object containing links to the
|
||||
* files and directories within dir.
|
||||
*/
|
||||
function list(requestPath, directory, recurse)
|
||||
{
|
||||
var count = 0;
|
||||
var path = requestPath;
|
||||
if (path.charAt(path.length - 1) != "/") {
|
||||
path += "/";
|
||||
}
|
||||
|
||||
var dir = directory.QueryInterface(Ci.nsIFile);
|
||||
var links = {};
|
||||
|
||||
// The SimpleTest directory is hidden
|
||||
var files = [file for (file in dirIter(dir))
|
||||
if (file.exists() && file.path.indexOf("SimpleTest") == -1)];
|
||||
|
||||
// Sort files by name, so that tests can be run in a pre-defined order inside
|
||||
// a given directory (see bug 384823)
|
||||
function leafNameComparator(first, second) {
|
||||
if (first.leafName < second.leafName)
|
||||
return -1;
|
||||
if (first.leafName > second.leafName)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
files.sort(leafNameComparator);
|
||||
|
||||
count = files.length;
|
||||
for each (var file in files) {
|
||||
var key = path + file.leafName;
|
||||
var childCount = 0;
|
||||
if (file.isDirectory()) {
|
||||
key += "/";
|
||||
}
|
||||
if (recurse && file.isDirectory()) {
|
||||
[links[key], childCount] = list(key, file, recurse);
|
||||
count += childCount;
|
||||
} else {
|
||||
if (file.leafName.charAt(0) != '.') {
|
||||
links[key] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [links, count];
|
||||
}
|
||||
|
||||
/**
|
||||
* Heuristic function that determines whether a given path
|
||||
* is a test case to be executed in the harness, or just
|
||||
* a supporting file.
|
||||
*/
|
||||
function isTest(filename, pattern)
|
||||
{
|
||||
if (pattern)
|
||||
return pattern.test(filename);
|
||||
|
||||
return filename.indexOf("test_") > -1 &&
|
||||
filename.indexOf(".js") == -1 &&
|
||||
filename.indexOf(".css") == -1 &&
|
||||
!/\^headers\^$/.test(filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform nested hashtables of paths to nested HTML lists.
|
||||
*/
|
||||
function linksToListItems(links)
|
||||
{
|
||||
var response = "";
|
||||
var children = "";
|
||||
for (var [link, value] in links) {
|
||||
var classVal = (!isTest(link) && !(value instanceof Object))
|
||||
? "non-test invisible"
|
||||
: "test";
|
||||
if (value instanceof Object) {
|
||||
children = UL({class: "testdir"}, linksToListItems(value));
|
||||
} else {
|
||||
children = "";
|
||||
}
|
||||
|
||||
var bug_title = link.match(/test_bug\S+/);
|
||||
var bug_num = null;
|
||||
if (bug_title != null) {
|
||||
bug_num = bug_title[0].match(/\d+/);
|
||||
}
|
||||
|
||||
if ((bug_title == null) || (bug_num == null)) {
|
||||
response += LI({class: classVal}, A({href: link}, link), children);
|
||||
} else {
|
||||
var bug_url = "https://bugzilla.mozilla.org/show_bug.cgi?id="+bug_num;
|
||||
response += LI({class: classVal}, A({href: link}, link), " - ", A({href: bug_url}, "Bug "+bug_num), children);
|
||||
}
|
||||
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform nested hashtables of paths to a flat table rows.
|
||||
*/
|
||||
function linksToTableRows(links)
|
||||
{
|
||||
var response = "";
|
||||
for (var [link, value] in links) {
|
||||
var classVal = (!isTest(link) && !(value instanceof Object))
|
||||
? "non-test invisible"
|
||||
: "";
|
||||
if (value instanceof Object) {
|
||||
response += TR({class: "dir", id: "tr-" + link },
|
||||
TD({colspan: "3"}," "));
|
||||
response += linksToTableRows(value);
|
||||
} else {
|
||||
response += TR({class: classVal, id: "tr-" + link},
|
||||
TD("0"), TD("0"), TD("0"));
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
function arrayOfTestFiles(linkArray, fileArray, testPattern) {
|
||||
for (var [link, value] in linkArray) {
|
||||
if (value instanceof Object) {
|
||||
arrayOfTestFiles(value, fileArray, testPattern);
|
||||
} else if (isTest(link, testPattern)) {
|
||||
fileArray.push(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Produce a flat array of test file paths to be executed in the harness.
|
||||
*/
|
||||
function jsonArrayOfTestFiles(links)
|
||||
{
|
||||
var testFiles = [];
|
||||
arrayOfTestFiles(links, testFiles);
|
||||
testFiles = ['"' + file + '"' for each(file in testFiles)];
|
||||
return "[" + testFiles.join(",\n") + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a normal directory listing.
|
||||
*/
|
||||
function regularListing(metadata, response)
|
||||
{
|
||||
var [links, count] = list(metadata.path,
|
||||
metadata.getProperty("directory"),
|
||||
false);
|
||||
response.write(
|
||||
HTML(
|
||||
HEAD(
|
||||
TITLE("mochitest index ", metadata.path)
|
||||
),
|
||||
BODY(
|
||||
BR(),
|
||||
A({href: ".."}, "Up a level"),
|
||||
UL(linksToListItems(links))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a test harness page containing all the test cases
|
||||
* below it, recursively.
|
||||
*/
|
||||
function testListing(metadata, response)
|
||||
{
|
||||
var [links, count] = list(metadata.path,
|
||||
metadata.getProperty("directory"),
|
||||
true);
|
||||
dumpn("count: " + count);
|
||||
var tests = jsonArrayOfTestFiles(links);
|
||||
response.write(
|
||||
HTML(
|
||||
HEAD(
|
||||
TITLE("MochiTest | ", metadata.path),
|
||||
LINK({rel: "stylesheet",
|
||||
type: "text/css", href: "/static/harness.css"}
|
||||
),
|
||||
SCRIPT({type: "text/javascript", src: "/MochiKit/packed.js"}),
|
||||
SCRIPT({type: "text/javascript",
|
||||
src: "/tests/SimpleTest/TestRunner.js"}),
|
||||
SCRIPT({type: "text/javascript",
|
||||
src: "/tests/SimpleTest/MozillaFileLogger.js"}),
|
||||
SCRIPT({type: "text/javascript",
|
||||
src: "/tests/SimpleTest/quit.js"}),
|
||||
SCRIPT({type: "text/javascript",
|
||||
src: "/tests/SimpleTest/setup.js"}),
|
||||
SCRIPT({type: "text/javascript"},
|
||||
"connect(window, 'onload', hookup); gTestList=" + tests + ";"
|
||||
)
|
||||
),
|
||||
BODY(
|
||||
DIV({class: "container"},
|
||||
H2("--> ", A({href: "#", id: "runtests"}, "Run Tests"), " <--"),
|
||||
P({style: "float: right;"},
|
||||
SMALL(
|
||||
"Based on the ",
|
||||
A({href:"http://www.mochikit.com/"}, "MochiKit"),
|
||||
" unit tests."
|
||||
)
|
||||
),
|
||||
DIV({class: "status"},
|
||||
H1({id: "indicator"}, "Status"),
|
||||
H2({id: "pass"}, "Passed: ", SPAN({id: "pass-count"},"0")),
|
||||
H2({id: "fail"}, "Failed: ", SPAN({id: "fail-count"},"0")),
|
||||
H2({id: "fail"}, "Todo: ", SPAN({id: "todo-count"},"0"))
|
||||
),
|
||||
DIV({class: "clear"}),
|
||||
DIV({id: "current-test"},
|
||||
B("Currently Executing: ",
|
||||
SPAN({id: "current-test-path"}, "_")
|
||||
)
|
||||
),
|
||||
DIV({class: "clear"}),
|
||||
DIV({class: "frameholder"},
|
||||
IFRAME({scrolling: "no", id: "testframe", width: "500", height: "300"})
|
||||
),
|
||||
DIV({class: "clear"}),
|
||||
DIV({class: "toggle"},
|
||||
A({href: "#", id: "toggleNonTests"}, "Show Non-Tests"),
|
||||
BR()
|
||||
),
|
||||
|
||||
TABLE({cellpadding: 0, cellspacing: 0, id: "test-table"},
|
||||
TR(TD("Passed"), TD("Failed"), TD("Todo"),
|
||||
TD({rowspan: count+1},
|
||||
UL({class: "top"},
|
||||
LI(B("Test Files")),
|
||||
linksToListItems(links)
|
||||
)
|
||||
)
|
||||
),
|
||||
linksToTableRows(links)
|
||||
),
|
||||
DIV({class: "clear"})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond to requests that match a file system directory.
|
||||
* Under the tests/ directory, return a test harness page.
|
||||
*/
|
||||
function defaultDirHandler(metadata, response)
|
||||
{
|
||||
response.setStatusLine("1.1", 200, "OK");
|
||||
response.setHeader("Content-type", "text/html", false);
|
||||
try {
|
||||
if (metadata.path.indexOf("/tests") != 0) {
|
||||
regularListing(metadata, response);
|
||||
} else {
|
||||
testListing(metadata, response);
|
||||
}
|
||||
} catch (ex) {
|
||||
response.write(ex);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Mozilla test code
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Mozilla Foundation
|
||||
# Portions created by the Initial Developer are Copyright (C) 2008
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Ted Mielczarek <ted.mielczarek@gmail.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
PROGRAM = ssltunnel$(BIN_SUFFIX)
|
||||
|
||||
CPPSRCS = ssltunnel.cpp
|
||||
|
||||
# Despite not catching exceptions, we're using STL
|
||||
ENABLE_CXX_EXCEPTIONS=1
|
||||
|
||||
REQUIRES = \
|
||||
nspr \
|
||||
nss \
|
||||
$(NULL)
|
||||
|
||||
LIBS = \
|
||||
$(NSPR_LIBS) \
|
||||
$(NSS_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
|
@ -0,0 +1,403 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla test code
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Ted Mielczarek <ted.mielczarek@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include "prinit.h"
|
||||
#include "prerror.h"
|
||||
#include "prio.h"
|
||||
#include "prnetdb.h"
|
||||
#include "prtpool.h"
|
||||
#include "nss.h"
|
||||
#include "pk11func.h"
|
||||
#include "key.h"
|
||||
#include "keyt.h"
|
||||
#include "ssl.h"
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
// Structs for passing data into jobs on the thread pool
|
||||
typedef struct {
|
||||
PRInt32 listen_port;
|
||||
PRNetAddr remote_addr;
|
||||
string cert_nickname;
|
||||
} server_info_t;
|
||||
|
||||
typedef struct {
|
||||
PRFileDesc* client_sock;
|
||||
PRNetAddr client_addr;
|
||||
server_info_t* server_info;
|
||||
} connection_info_t;
|
||||
|
||||
// A couple of stack classes for managing NSS/NSPR resources
|
||||
class AutoCert {
|
||||
public:
|
||||
AutoCert(CERTCertificate* cert) { cert_ = cert; }
|
||||
~AutoCert() { if (cert_) CERT_DestroyCertificate(cert_); }
|
||||
operator CERTCertificate*() { return cert_; }
|
||||
private:
|
||||
CERTCertificate* cert_;
|
||||
};
|
||||
|
||||
class AutoKey {
|
||||
public:
|
||||
AutoKey(SECKEYPrivateKey* key) { key_ = key; }
|
||||
~AutoKey() { if (key_) SECKEY_DestroyPrivateKey(key_); }
|
||||
operator SECKEYPrivateKey*() { return key_; }
|
||||
private:
|
||||
SECKEYPrivateKey* key_;
|
||||
};
|
||||
|
||||
class AutoFD {
|
||||
public:
|
||||
AutoFD(PRFileDesc* fd) { fd_ = fd; }
|
||||
~AutoFD() {
|
||||
if (fd_) {
|
||||
PR_Shutdown(fd_, PR_SHUTDOWN_BOTH);
|
||||
PR_Close(fd_);
|
||||
}
|
||||
}
|
||||
operator PRFileDesc*() { return fd_; }
|
||||
PRFileDesc* reset(PRFileDesc* newfd) {
|
||||
PRFileDesc* oldfd = fd_;
|
||||
fd_ = newfd;
|
||||
return oldfd;
|
||||
}
|
||||
private:
|
||||
PRFileDesc* fd_;
|
||||
};
|
||||
|
||||
// These are suggestions. If the number of ports to proxy on * 2
|
||||
// is greater than either of these, then we'll use that value instead.
|
||||
const PRInt32 INITIAL_THREADS = 1;
|
||||
const PRInt32 MAX_THREADS = 5;
|
||||
const PRInt32 DEFAULT_STACKSIZE = (512 * 1024);
|
||||
const PRInt32 BUF_SIZE = 4096;
|
||||
|
||||
// global data
|
||||
PRThreadPool* threads = NULL;
|
||||
PRLock* shutdown_lock = NULL;
|
||||
PRCondVar* shutdown_condvar = NULL;
|
||||
// Not really used, unless something fails to start
|
||||
bool shutdown_server = false;
|
||||
|
||||
/*
|
||||
* Signal the main thread that the application should shut down.
|
||||
*/
|
||||
void SignalShutdown()
|
||||
{
|
||||
PR_Lock(shutdown_lock);
|
||||
PR_NotifyCondVar(shutdown_condvar);
|
||||
PR_Unlock(shutdown_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle an incoming client connection. The server thread has already
|
||||
* accepted the connection, so we just need to connect to the remote
|
||||
* port and then proxy data back and forth.
|
||||
* The data parameter is a connection_info_t*, and must be deleted
|
||||
* by this function.
|
||||
*/
|
||||
void PR_CALLBACK HandleConnection(void* data)
|
||||
{
|
||||
connection_info_t* ci = static_cast<connection_info_t*>(data);
|
||||
PRIntervalTime connect_timeout = PR_SecondsToInterval(2);
|
||||
PRIntervalTime short_timeout = PR_MillisecondsToInterval(250);
|
||||
|
||||
AutoFD other_sock(PR_NewTCPSocket());
|
||||
bool client_done = false;
|
||||
bool client_error = false;
|
||||
PRUint8 buf[BUF_SIZE];
|
||||
|
||||
if (other_sock &&
|
||||
PR_Connect(other_sock, &ci->server_info->remote_addr, connect_timeout)
|
||||
== PR_SUCCESS) {
|
||||
PRInt32 bytes = PR_Recv(ci->client_sock, buf, BUF_SIZE, 0, short_timeout);
|
||||
if (bytes > 0 &&
|
||||
PR_Send(other_sock, buf, bytes, 0, short_timeout) > 0) {
|
||||
bytes = PR_Recv(other_sock, buf, BUF_SIZE, 0, short_timeout);
|
||||
while (bytes > 0) {
|
||||
if (PR_Send(ci->client_sock, buf, bytes, 0, short_timeout) == -1) {
|
||||
client_error = true;
|
||||
break;
|
||||
}
|
||||
if (!client_done) {
|
||||
bytes = PR_Recv(ci->client_sock, buf, BUF_SIZE, 0, short_timeout);
|
||||
if (bytes > 0) {
|
||||
if (PR_Send(other_sock, buf, bytes, 0, short_timeout) == -1)
|
||||
break;
|
||||
}
|
||||
else if (bytes == 0) {
|
||||
client_done = true;
|
||||
}
|
||||
else {// error
|
||||
client_error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bytes = PR_Recv(other_sock, buf, BUF_SIZE, 0, short_timeout);
|
||||
}
|
||||
}
|
||||
else if (bytes == -1) {
|
||||
client_error = true;
|
||||
}
|
||||
}
|
||||
if (!client_error)
|
||||
PR_Shutdown(ci->client_sock, PR_SHUTDOWN_BOTH);
|
||||
PR_Close(ci->client_sock);
|
||||
|
||||
delete ci;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start listening for SSL connections on a specified port, handing
|
||||
* them off to client threads after accepting the connection.
|
||||
* The data parameter is a server_info_t*, owned by the calling
|
||||
* function.
|
||||
*/
|
||||
void PR_CALLBACK StartServer(void* data)
|
||||
{
|
||||
server_info_t* si = static_cast<server_info_t*>(data);
|
||||
|
||||
//TODO: select ciphers?
|
||||
AutoCert cert(PK11_FindCertFromNickname(si->cert_nickname.c_str(),
|
||||
NULL));
|
||||
if (!cert) {
|
||||
fprintf(stderr, "Failed to find cert %s\n", si->cert_nickname.c_str());
|
||||
SignalShutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
AutoKey privKey(PK11_FindKeyByAnyCert(cert, NULL));
|
||||
if (!privKey) {
|
||||
fprintf(stderr, "Failed to find private key\n");
|
||||
SignalShutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
AutoFD listen_socket(PR_NewTCPSocket());
|
||||
if (!listen_socket) {
|
||||
fprintf(stderr, "failed to create socket\n");
|
||||
SignalShutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
PRNetAddr server_addr;
|
||||
PR_InitializeNetAddr(PR_IpAddrAny, si->listen_port, &server_addr);
|
||||
if (PR_Bind(listen_socket, &server_addr) != PR_SUCCESS) {
|
||||
fprintf(stderr, "failed to bind socket\n");
|
||||
SignalShutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
if (PR_Listen(listen_socket, 1) != PR_SUCCESS) {
|
||||
fprintf(stderr, "failed to listen on socket\n");
|
||||
SignalShutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
PRFileDesc* ssl_socket = SSL_ImportFD(NULL, listen_socket);
|
||||
if (!ssl_socket) {
|
||||
fprintf(stderr, "Error importing SSL socket\n");
|
||||
SignalShutdown();
|
||||
return;
|
||||
}
|
||||
listen_socket.reset(ssl_socket);
|
||||
|
||||
if (SSL_ConfigSecureServer(listen_socket, cert, privKey, kt_rsa)
|
||||
!= SECSuccess) {
|
||||
fprintf(stderr, "Error configuring SSL listen socket\n");
|
||||
SignalShutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Server listening on port %d with cert %s\n", si->listen_port,
|
||||
si->cert_nickname.c_str());
|
||||
|
||||
while (!shutdown_server) {
|
||||
connection_info_t* ci = new connection_info_t();
|
||||
ci->server_info = si;
|
||||
// block waiting for connections
|
||||
ci->client_sock = PR_Accept(listen_socket, &ci->client_addr,
|
||||
PR_INTERVAL_NO_TIMEOUT);
|
||||
if (ci->client_sock)
|
||||
// Not actually using this PRJob*...
|
||||
//PRJob* job =
|
||||
PR_QueueJob(threads, HandleConnection, ci, PR_TRUE);
|
||||
else
|
||||
delete ci;
|
||||
}
|
||||
}
|
||||
|
||||
// bogus password func, just don't use passwords. :-P
|
||||
char* password_func(PK11SlotInfo* slot, PRBool retry, void* arg)
|
||||
{
|
||||
if (retry)
|
||||
return NULL;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc < 6) {
|
||||
fprintf(stderr, "Error: not enough arguments\n"
|
||||
"Usage: ssltunnel <NSS db path> <remote ip> <remote port> (<certname> <port>)+\n"
|
||||
" Provide SSL encrypted tunnels to <remote ip>:<remote port>\n"
|
||||
" from each port specified in a <certname>,<port> pair.\n"
|
||||
" <certname> must be the nickname of a server certificate\n"
|
||||
" installed in the NSS db pointed to by the <NSS db path>.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
PRNetAddr remote_addr;
|
||||
if (PR_StringToNetAddr(argv[2], &remote_addr) != PR_SUCCESS) {
|
||||
fprintf(stderr, "Invalid remote IP address: %s\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int port = atoi(argv[3]);
|
||||
if (port <= 0) {
|
||||
fprintf(stderr, "Invalid remote port: %s\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
remote_addr.inet.port = PR_htons(port);
|
||||
|
||||
// get our list of cert:port from the remaining args
|
||||
vector<server_info_t> servers;
|
||||
for (int i=4; i<argc; i++) {
|
||||
server_info_t server;
|
||||
memcpy(&server.remote_addr, &remote_addr, sizeof(PRNetAddr));
|
||||
server.cert_nickname = argv[i++];
|
||||
port = atoi(argv[i]);
|
||||
if (port <= 0) {
|
||||
fprintf(stderr, "Invalid port specified: %s\n", argv[i]);
|
||||
return 1;
|
||||
}
|
||||
server.listen_port = port;
|
||||
servers.push_back(server);
|
||||
}
|
||||
|
||||
// create a thread pool to handle connections
|
||||
threads = PR_CreateThreadPool(std::max<PRInt32>(INITIAL_THREADS,
|
||||
servers.size()*2),
|
||||
std::max<PRInt32>(MAX_THREADS,
|
||||
servers.size()*2),
|
||||
DEFAULT_STACKSIZE);
|
||||
if (!threads) {
|
||||
fprintf(stderr, "Failed to create thread pool\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
shutdown_lock = PR_NewLock();
|
||||
if (!shutdown_lock) {
|
||||
fprintf(stderr, "Failed to create lock\n");
|
||||
PR_ShutdownThreadPool(threads);
|
||||
return 1;
|
||||
}
|
||||
shutdown_condvar = PR_NewCondVar(shutdown_lock);
|
||||
if (!shutdown_condvar) {
|
||||
fprintf(stderr, "Failed to create condvar\n");
|
||||
PR_ShutdownThreadPool(threads);
|
||||
PR_DestroyLock(shutdown_lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
PK11_SetPasswordFunc(password_func);
|
||||
|
||||
// Initialize NSS
|
||||
char* configdir = argv[1];
|
||||
|
||||
if (NSS_Init(configdir) != SECSuccess) {
|
||||
PRInt32 errorlen = PR_GetErrorTextLength();
|
||||
char* err = new char[errorlen+1];
|
||||
PR_GetErrorText(err);
|
||||
fprintf(stderr, "Failed to init NSS: %s", err);
|
||||
delete[] err;
|
||||
PR_ShutdownThreadPool(threads);
|
||||
PR_DestroyCondVar(shutdown_condvar);
|
||||
PR_DestroyLock(shutdown_lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (NSS_SetDomesticPolicy() != SECSuccess) {
|
||||
fprintf(stderr, "NSS_SetDomesticPolicy failed\n");
|
||||
PR_ShutdownThreadPool(threads);
|
||||
PR_DestroyCondVar(shutdown_condvar);
|
||||
PR_DestroyLock(shutdown_lock);
|
||||
NSS_Shutdown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// these values should make NSS use the defaults
|
||||
if (SSL_ConfigServerSessionIDCache(0, 0, 0, NULL) != SECSuccess) {
|
||||
fprintf(stderr, "SSL_ConfigServerSessionIDCache failed\n");
|
||||
PR_ShutdownThreadPool(threads);
|
||||
PR_DestroyCondVar(shutdown_condvar);
|
||||
PR_DestroyLock(shutdown_lock);
|
||||
NSS_Shutdown();
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (vector<server_info_t>::iterator it = servers.begin();
|
||||
it != servers.end(); it++) {
|
||||
// Not actually using this PRJob*...
|
||||
// PRJob* server_job =
|
||||
PR_QueueJob(threads, StartServer, &(*it), PR_TRUE);
|
||||
}
|
||||
// now wait for someone to tell us to quit
|
||||
PR_Lock(shutdown_lock);
|
||||
PR_WaitCondVar(shutdown_condvar, PR_INTERVAL_NO_TIMEOUT);
|
||||
PR_Unlock(shutdown_lock);
|
||||
shutdown_server = true;
|
||||
printf("Shutting down...\n");
|
||||
// cleanup
|
||||
PR_ShutdownThreadPool(threads);
|
||||
PR_JoinThreadPool(threads);
|
||||
PR_DestroyCondVar(shutdown_condvar);
|
||||
PR_DestroyLock(shutdown_lock);
|
||||
if (NSS_Shutdown() == SECFailure) {
|
||||
fprintf(stderr, "Leaked NSS objects!\n");
|
||||
}
|
||||
PR_Cleanup();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Netscape Communications Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 1998
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = testing/mochitest/static
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_STATIC_FILES = test.template.txt \
|
||||
xhtml.template.txt \
|
||||
xul.template.txt \
|
||||
harness.css \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_STATIC_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/$(relativesrcdir)
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
<form method='get' action='/static/bug100533_load.html' id='b'><input type="submit"/></form>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,14 @@
|
|||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
</head>
|
||||
|
||||
|
||||
<body onload="parent.submitted();">
|
||||
|
||||
<span id="foo"></span>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!-- Use an unload handler to prevent bfcache from messing with us -->
|
||||
<body onunload="parent.childUnloaded = true;">
|
||||
<select id="select">
|
||||
<option>aaa</option>
|
||||
<option>bbbb</option>
|
||||
</select>
|
||||
|
||||
<textarea id="textarea">
|
||||
</textarea>
|
||||
|
||||
<input type="text" id="text">
|
||||
<input type="password" id="password">
|
||||
<input type="checkbox" id="checkbox">
|
||||
<input type="radio" id="radio">
|
||||
<input type="image" id="image">
|
||||
<input type="submit" id="submit">
|
||||
<input type="reset" id="reset">
|
||||
<input type="button" id="button input">
|
||||
<input type="hidden" id="hidden">
|
||||
<input type="file" id="file">
|
||||
|
||||
<button type="submit" id="submit button"></button>
|
||||
<button type="reset" id="reset button"></button>
|
||||
<button type="button" id="button"></button>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,27 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<!-- Use an unload handler to prevent bfcache from messing with us -->
|
||||
<body onunload="parent.childUnloaded = true;">
|
||||
<select id="select">
|
||||
<option>aaa</option>
|
||||
<option>bbbb</option>
|
||||
</select>
|
||||
|
||||
<textarea id="textarea">
|
||||
</textarea>
|
||||
|
||||
<input type="text" id="text" />
|
||||
<input type="password" id="password" />
|
||||
<input type="checkbox" id="checkbox" />
|
||||
<input type="radio" id="radio" />
|
||||
<input type="image" id="image" />
|
||||
<input type="submit" id="submit" />
|
||||
<input type="reset" id="reset" />
|
||||
<input type="button" id="button input" />
|
||||
<input type="hidden" id="hidden" />
|
||||
<input type="file" id="file" />
|
||||
|
||||
<button type="submit" id="submit button"></button>
|
||||
<button type="reset" id="reset button"></button>
|
||||
<button type="button" id="button"></button>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,4 @@
|
|||
Line 1.
|
||||
Line 2.
|
||||
Line 3.
|
||||
Line 4.
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 200 200">
|
||||
<g id="g1" transform="translate(100, 100)">
|
||||
<circle cx="0" cy="0" r="50" fill="green" />
|
||||
<text x="0" y="10" font-size="24" text-anchor="middle" fill="yellow">Kibology</text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 301 B |
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id={BUGNUMBER}
|
||||
-->
|
||||
<window title="Mozilla Bug {BUGNUMBER}"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"/>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id={BUGNUMBER}"
|
||||
target="_blank">Mozilla Bug {BUGNUMBER}</a>
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
/** Test for Bug {BUGNUMBER} **/
|
||||
|
||||
|
||||
|
||||
]]>
|
||||
</script>
|
||||
</window>
|
|
@ -0,0 +1,97 @@
|
|||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: Verdana, Helvetica, Arial, sans-serif;
|
||||
font-size: 11px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
#xulharness {
|
||||
position: fixed;
|
||||
top: 30px;
|
||||
bottom: 0;
|
||||
right: 0px;
|
||||
left: 0px;
|
||||
overflow:auto;
|
||||
}
|
||||
|
||||
th, td {
|
||||
font-family: Verdana, Helvetica, Arial, sans-serif;
|
||||
font-size: 11px;
|
||||
padding-left: .2em;
|
||||
padding-right: .2em;
|
||||
text-align: left;
|
||||
height: 15px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
li, li.test, li.dir {
|
||||
padding: 0;
|
||||
line-height: 15px;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
margin-left: 1em;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
ul.top {
|
||||
padding: 0;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
table#test-table {
|
||||
background: #f6f6f6;
|
||||
margin-left: 1em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.container {
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
a#runtests, a {
|
||||
color: #3333cc;
|
||||
}
|
||||
|
||||
li.non-test a {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
small a {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.clear { clear: both;}
|
||||
.invisible { display: none;}
|
||||
|
||||
div.status {
|
||||
min-height: 170px;
|
||||
width: 100%;
|
||||
border: 1px solid #666;
|
||||
}
|
||||
div.frameholder {
|
||||
min-height: 170px;
|
||||
min-width: 500px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
div#current-test {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
#indicator {
|
||||
color: white;
|
||||
background-color: green;
|
||||
padding: .5em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#pass, #fail {
|
||||
margin: 0;
|
||||
padding: .5em;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 732 B |
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id={BUGNUMBER}
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug {BUGNUMBER}</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id={BUGNUMBER}">Mozilla Bug {BUGNUMBER}</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Bug {BUGNUMBER} **/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id={BUGNUMBER}
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug {BUGNUMBER}</title>
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id={BUGNUMBER}">Mozilla Bug {BUGNUMBER}</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
<![CDATA[
|
||||
|
||||
/** Test for Bug {BUGNUMBER} **/
|
||||
|
||||
|
||||
|
||||
|
||||
]]>
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id={BUGNUMBER}
|
||||
-->
|
||||
<window title="Mozilla Bug {BUGNUMBER}"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="/MochiKit/packed.js" />
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"/>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id={BUGNUMBER}"
|
||||
target="_blank">Mozilla Bug {BUGNUMBER}</a>
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript"><![CDATA[
|
||||
|
||||
/** Test for Bug {BUGNUMBER} **/
|
||||
|
||||
|
||||
|
||||
]]></script>
|
||||
</window>
|
|
@ -0,0 +1,40 @@
|
|||
var JSAN = {
|
||||
global: this,
|
||||
use: function (module, symbols) {
|
||||
var components = module.split(/\./);
|
||||
var fn = components.join('/') + '.js';
|
||||
var o = JSAN.global;
|
||||
var i, c;
|
||||
for (i = 0; i < components.length; i++) {
|
||||
o = o[components[i]];
|
||||
if (typeof(o) == 'undefined') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (typeof(o) != 'undefined') {
|
||||
return o;
|
||||
}
|
||||
|
||||
load(fn);
|
||||
o = JSAN.global;
|
||||
for (i = 0; i < components.length; i++) {
|
||||
o = o[components[i]];
|
||||
if (typeof(o) == 'undefined') {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
if (!symbols) {
|
||||
var tags = o.EXPORT_TAGS;
|
||||
if (tags) {
|
||||
symbols = tags[':common'] || tags[':all'];
|
||||
}
|
||||
}
|
||||
if (symbols) {
|
||||
for (i = 0; i < symbols.length; i++) {
|
||||
c = symbols[i];
|
||||
JSAN.global[c] = o[c];
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,81 @@
|
|||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Netscape Communications Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 1998
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = testing/mochitest/tests
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
DIRS = SimpleTest \
|
||||
browser \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_TEST_FILES = test_MochiKit-Async.html \
|
||||
test_MochiKit-Base.html \
|
||||
test_MochiKit-DateTime.html \
|
||||
test_MochiKit-DOM.html \
|
||||
test_MochiKit-Style.html \
|
||||
test_MochiKit-Format.html \
|
||||
test_MochiKit-Iter.html \
|
||||
test_MochiKit-Logging.html \
|
||||
test_MochiKit-MochiKit.html \
|
||||
test_MochiKit-Color.html \
|
||||
test_MochiKit-Signal.html \
|
||||
test_Base.js \
|
||||
test_Color.js \
|
||||
test_DateTime.js \
|
||||
test_DragAndDrop.js \
|
||||
test_Format.js \
|
||||
test_Iter.js \
|
||||
test_Logging.js \
|
||||
test_Signal.js \
|
||||
cli.js \
|
||||
FakeJSAN.js \
|
||||
standalone.js \
|
||||
test_MochiKit-Async.json \
|
||||
$(NULL)
|
||||
|
||||
# Copy the MochiKit tests into subdirectory, so the top level is all dirs
|
||||
# in the test screen. We leave them in tests/ in the src tree to mirror
|
||||
# the upstream MochiKit repository
|
||||
libs:: $(_TEST_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/$(relativesrcdir)/MochiKit_Unit_Tests
|
|
@ -0,0 +1,356 @@
|
|||
/**
|
||||
* EventUtils provides some utility methods for creating and sending DOM events.
|
||||
* Current methods:
|
||||
* sendMouseEvent
|
||||
* sendChar
|
||||
* sendString
|
||||
* sendKey
|
||||
*/
|
||||
|
||||
/**
|
||||
* Send a mouse event to the node with id aTarget. The "event" passed in to
|
||||
* aEvent is just a JavaScript object with the properties set that the real
|
||||
* mouse event object should have. This includes the type of the mouse event.
|
||||
* E.g. to send an click event to the node with id 'node' you might do this:
|
||||
*
|
||||
* sendMouseEvent({type:'click'}, 'node');
|
||||
*/
|
||||
function sendMouseEvent(aEvent, aTarget, aWindow) {
|
||||
if (['click', 'mousedown', 'mouseup', 'mouseover', 'mouseout'].indexOf(aEvent.type) == -1) {
|
||||
throw new Error("sendMouseEvent doesn't know about event type '"+aEvent.type+"'");
|
||||
}
|
||||
|
||||
if (!aWindow) {
|
||||
aWindow = window;
|
||||
}
|
||||
|
||||
// For events to trigger the UA's default actions they need to be "trusted"
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserWrite');
|
||||
|
||||
var event = aWindow.document.createEvent('MouseEvent');
|
||||
|
||||
var typeArg = aEvent.type;
|
||||
var canBubbleArg = true;
|
||||
var cancelableArg = true;
|
||||
var viewArg = aWindow;
|
||||
var detailArg = aEvent.detail || (aEvent.type == 'click' ||
|
||||
aEvent.type == 'mousedown' ||
|
||||
aEvent.type == 'mouseup' ? 1 : 0);
|
||||
var screenXArg = aEvent.screenX || 0;
|
||||
var screenYArg = aEvent.screenY || 0;
|
||||
var clientXArg = aEvent.clientX || 0;
|
||||
var clientYArg = aEvent.clientY || 0;
|
||||
var ctrlKeyArg = aEvent.ctrlKey || false;
|
||||
var altKeyArg = aEvent.altKey || false;
|
||||
var shiftKeyArg = aEvent.shiftKey || false;
|
||||
var metaKeyArg = aEvent.metaKey || false;
|
||||
var buttonArg = aEvent.button || 0;
|
||||
var relatedTargetArg = aEvent.relatedTarget || null;
|
||||
|
||||
event.initMouseEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg,
|
||||
screenXArg, screenYArg, clientXArg, clientYArg,
|
||||
ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
|
||||
buttonArg, relatedTargetArg);
|
||||
|
||||
aWindow.document.getElementById(aTarget).dispatchEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the char aChar to the node with id aTarget. If aTarget is not
|
||||
* provided, use "target". This method handles casing of chars (sends the
|
||||
* right charcode, and sends a shift key for uppercase chars). No other
|
||||
* modifiers are handled at this point.
|
||||
*
|
||||
* For now this method only works for English letters (lower and upper case)
|
||||
* and the digits 0-9.
|
||||
*
|
||||
* Returns true if the keypress event was accepted (no calls to preventDefault
|
||||
* or anything like that), false otherwise.
|
||||
*/
|
||||
function sendChar(aChar, aTarget) {
|
||||
// DOM event charcodes match ASCII (JS charcodes) for a-zA-Z0-9.
|
||||
var hasShift = (aChar == aChar.toUpperCase());
|
||||
var charCode = aChar.charCodeAt(0);
|
||||
var keyCode = charCode;
|
||||
if (!hasShift) {
|
||||
// For lowercase letters, the keyCode is actually 32 less than the charCode
|
||||
keyCode -= 0x20;
|
||||
}
|
||||
|
||||
return __doEventDispatch(aTarget, charCode, keyCode, hasShift);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the string aStr to the node with id aTarget. If aTarget is not
|
||||
* provided, use "target".
|
||||
*
|
||||
* For now this method only works for English letters (lower and upper case)
|
||||
* and the digits 0-9.
|
||||
*/
|
||||
function sendString(aStr, aTarget) {
|
||||
for (var i = 0; i < aStr.length; ++i) {
|
||||
sendChar(aStr.charAt(i), aTarget);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the non-character key aKey to the node with id aTarget. If aTarget is
|
||||
* not provided, use "target". The name of the key should be a lowercase
|
||||
* version of the part that comes after "DOM_VK_" in the KeyEvent constant
|
||||
* name for this key. No modifiers are handled at this point.
|
||||
*
|
||||
* Returns true if the keypress event was accepted (no calls to preventDefault
|
||||
* or anything like that), false otherwise.
|
||||
*/
|
||||
function sendKey(aKey, aTarget) {
|
||||
keyName = "DOM_VK_" + aKey.toUpperCase();
|
||||
|
||||
if (!KeyEvent[keyName]) {
|
||||
throw "Unknown key: " + keyName;
|
||||
}
|
||||
|
||||
return __doEventDispatch(aTarget, 0, KeyEvent[keyName], false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually perform event dispatch given a charCode, keyCode, and boolean for
|
||||
* whether "shift" was pressed. Send the event to the node with id aTarget. If
|
||||
* aTarget is not provided, use "target".
|
||||
*
|
||||
* Returns true if the keypress event was accepted (no calls to preventDefault
|
||||
* or anything like that), false otherwise.
|
||||
*/
|
||||
function __doEventDispatch(aTarget, aCharCode, aKeyCode, aHasShift) {
|
||||
if (aTarget === undefined) {
|
||||
aTarget = "target";
|
||||
}
|
||||
|
||||
// Make our events trusted
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
var event = document.createEvent("KeyEvents");
|
||||
event.initKeyEvent("keydown", true, true, document.defaultView,
|
||||
false, false, aHasShift, false,
|
||||
aKeyCode, 0);
|
||||
var accepted = $(aTarget).dispatchEvent(event);
|
||||
|
||||
// Cancelling keydown cancels keypress too
|
||||
if (accepted) {
|
||||
event = document.createEvent("KeyEvents");
|
||||
if (aCharCode) {
|
||||
event.initKeyEvent("keypress", true, true, document.defaultView,
|
||||
false, false, aHasShift, false,
|
||||
0, aCharCode);
|
||||
} else {
|
||||
event.initKeyEvent("keypress", true, true, document.defaultView,
|
||||
false, false, aHasShift, false,
|
||||
aKeyCode, 0);
|
||||
}
|
||||
accepted = $(aTarget).dispatchEvent(event);
|
||||
}
|
||||
|
||||
// Always send keyup
|
||||
var event = document.createEvent("KeyEvents");
|
||||
event.initKeyEvent("keyup", true, true, document.defaultView,
|
||||
false, false, aHasShift, false,
|
||||
aKeyCode, 0);
|
||||
$(aTarget).dispatchEvent(event);
|
||||
return accepted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the key modifier flags from aEvent. Used to share code between
|
||||
* synthesizeMouse and synthesizeKey.
|
||||
*/
|
||||
function _parseModifiers(aEvent)
|
||||
{
|
||||
const masks = Components.interfaces.nsIDOMNSEvent;
|
||||
var mval = 0;
|
||||
if (aEvent.shiftKey)
|
||||
mval |= masks.SHIFT_MASK;
|
||||
if (aEvent.ctrlKey)
|
||||
mval |= masks.CONTROL_MASK;
|
||||
if (aEvent.altKey)
|
||||
mval |= masks.ALT_MASK;
|
||||
if (aEvent.metaKey)
|
||||
mval |= masks.META_MASK;
|
||||
if (aEvent.accelKey)
|
||||
mval |= (navigator.platform.indexOf("Mac") >= 0) ? masks.META_MASK :
|
||||
masks.CONTROL_MASK;
|
||||
|
||||
return mval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize a mouse event on a target. The actual client point is determined
|
||||
* by taking the aTarget's client box and offseting it by aOffsetX and
|
||||
* aOffsetY. This allows mouse clicks to be simulated by calling this method.
|
||||
*
|
||||
* aEvent is an object which may contain the properties:
|
||||
* shiftKey, ctrlKey, altKey, metaKey, accessKey, type
|
||||
*
|
||||
* If the type is specified, an mouse event of that type is fired. Otherwise,
|
||||
* a mousedown followed by a mouse up is performed.
|
||||
*
|
||||
* aWindow is optional, and defaults to the current window object.
|
||||
*/
|
||||
function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
if (!aWindow)
|
||||
aWindow = window;
|
||||
|
||||
var utils = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
if (utils) {
|
||||
var button = aEvent.button || 0;
|
||||
var clickCount = aEvent.clickCount || 1;
|
||||
var modifiers = _parseModifiers(aEvent);
|
||||
|
||||
var left = aTarget.boxObject.x;
|
||||
var top = aTarget.boxObject.y;
|
||||
|
||||
if (aEvent.type) {
|
||||
utils.sendMouseEvent(aEvent.type, left + aOffsetX, top + aOffsetY, button, clickCount, modifiers);
|
||||
}
|
||||
else {
|
||||
utils.sendMouseEvent("mousedown", left + aOffsetX, top + aOffsetY, button, clickCount, modifiers);
|
||||
utils.sendMouseEvent("mouseup", left + aOffsetX, top + aOffsetY, button, clickCount, modifiers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synthesize a key event. It is targeted at whatever would be targeted by an
|
||||
* actual keypress by the user, typically the focused element.
|
||||
*
|
||||
* aKey should be either a character or a keycode starting with VK_ such as
|
||||
* VK_ENTER.
|
||||
*
|
||||
* aEvent is an object which may contain the properties:
|
||||
* shiftKey, ctrlKey, altKey, metaKey, accessKey, type
|
||||
*
|
||||
* If the type is specified, a key event of that type is fired. Otherwise,
|
||||
* a keydown, a keypress and then a keyup event are fired in sequence.
|
||||
*
|
||||
* aWindow is optional, and defaults to the current window object.
|
||||
*/
|
||||
function synthesizeKey(aKey, aEvent, aWindow)
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
if (!aWindow)
|
||||
aWindow = window;
|
||||
|
||||
var utils = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
if (utils) {
|
||||
var keyCode = 0, charCode = 0;
|
||||
if (aKey.indexOf("VK_") == 0)
|
||||
keyCode = KeyEvent["DOM_" + aKey];
|
||||
else
|
||||
charCode = aKey.charCodeAt(0);
|
||||
|
||||
var modifiers = _parseModifiers(aEvent);
|
||||
|
||||
if (aEvent.type) {
|
||||
utils.sendKeyEvent(aEvent.type, keyCode, charCode, modifiers);
|
||||
}
|
||||
else {
|
||||
utils.sendKeyEvent("keydown", keyCode, charCode, modifiers);
|
||||
utils.sendKeyEvent("keypress", keyCode, charCode, modifiers);
|
||||
utils.sendKeyEvent("keyup", keyCode, charCode, modifiers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var _gSeenEvent = false;
|
||||
|
||||
/**
|
||||
* Indicate that an event with an original target of aExpectedTarget and
|
||||
* a type of aExpectedEvent is expected to be fired, or not expected to
|
||||
* be fired.
|
||||
*/
|
||||
function _expectEvent(aExpectedTarget, aExpectedEvent, aTestName)
|
||||
{
|
||||
if (!aExpectedTarget || !aExpectedEvent)
|
||||
return null;
|
||||
|
||||
_gSeenEvent = false;
|
||||
|
||||
var type = (aExpectedEvent.charAt(0) == "!") ?
|
||||
aExpectedEvent.substring(1) : aExpectedEvent;
|
||||
var eventHandler = function(event) {
|
||||
var epassed = (!_gSeenEvent && event.originalTarget == aExpectedTarget &&
|
||||
event.type == type);
|
||||
is(epassed, true, aTestName + " " + type + " event target " + (_gSeenEvent ? "twice" : ""));
|
||||
_gSeenEvent = true;
|
||||
};
|
||||
|
||||
aExpectedTarget.addEventListener(type, eventHandler, false);
|
||||
return eventHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the event was fired or not. The event handler aEventHandler
|
||||
* will be removed.
|
||||
*/
|
||||
function _checkExpectedEvent(aExpectedTarget, aExpectedEvent, aEventHandler, aTestName)
|
||||
{
|
||||
if (aEventHandler) {
|
||||
var expectEvent = (aExpectedEvent.charAt(0) != "!");
|
||||
var type = expectEvent ? aExpectedEvent : aExpectedEvent.substring(1);
|
||||
aExpectedTarget.removeEventListener(type, aEventHandler, false);
|
||||
var desc = type + " event";
|
||||
if (!expectEvent)
|
||||
desc += " not";
|
||||
is(_gSeenEvent, expectEvent, aTestName + " " + desc + " fired");
|
||||
}
|
||||
|
||||
_gSeenEvent = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to synthesizeMouse except that a test is performed to see if an
|
||||
* event is fired at the right target as a result.
|
||||
*
|
||||
* aExpectedTarget - the expected originalTarget of the event.
|
||||
* aExpectedEvent - the expected type of the event, such as 'select'.
|
||||
* aTestName - the test name when outputing results
|
||||
*
|
||||
* To test that an event is not fired, use an expected type preceded by an
|
||||
* exclamation mark, such as '!select'. This might be used to test that a
|
||||
* click on a disabled element doesn't fire certain events for instance.
|
||||
*
|
||||
* aWindow is optional, and defaults to the current window object.
|
||||
*/
|
||||
function synthesizeMouseExpectEvent(aTarget, aOffsetX, aOffsetY, aEvent,
|
||||
aExpectedTarget, aExpectedEvent, aTestName,
|
||||
aWindow)
|
||||
{
|
||||
var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName);
|
||||
synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow);
|
||||
_checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to synthesizeKey except that a test is performed to see if an
|
||||
* event is fired at the right target as a result.
|
||||
*
|
||||
* aExpectedTarget - the expected originalTarget of the event.
|
||||
* aExpectedEvent - the expected type of the event, such as 'select'.
|
||||
* aTestName - the test name when outputing results
|
||||
*
|
||||
* To test that an event is not fired, use an expected type preceded by an
|
||||
* exclamation mark, such as '!select'.
|
||||
*
|
||||
* aWindow is optional, and defaults to the current window object.
|
||||
*/
|
||||
function synthesizeKeyExpectEvent(key, aEvent, aExpectedTarget, aExpectedEvent,
|
||||
aTestName, aWindow)
|
||||
{
|
||||
var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName);
|
||||
synthesizeKey(key, aEvent, aWindow);
|
||||
_checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName);
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Netscape Communications Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 1998
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = testing/mochitest/tests/SimpleTest
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_SIMPLETEST_FILES = MozillaFileLogger.js \
|
||||
quit.js \
|
||||
SimpleTest.js \
|
||||
test.css \
|
||||
TestRunner.js \
|
||||
setup.js \
|
||||
EventUtils.js \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_SIMPLETEST_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/$(relativesrcdir)
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* MozillaFileLogger, a log listener that can write to a local file.
|
||||
*/
|
||||
try {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const FOSTREAM_CID = "@mozilla.org/network/file-output-stream;1";
|
||||
const LF_CID = "@mozilla.org/file/local;1";
|
||||
|
||||
// File status flags. It is a bitwise OR of the following bit flags.
|
||||
// Only one of the first three flags below may be used.
|
||||
const PR_READ_ONLY = 0x01; // Open for reading only.
|
||||
const PR_WRITE_ONLY = 0x02; // Open for writing only.
|
||||
const PR_READ_WRITE = 0x04; // Open for reading and writing.
|
||||
|
||||
// If the file does not exist, the file is created.
|
||||
// If the file exists, this flag has no effect.
|
||||
const PR_CREATE_FILE = 0x08;
|
||||
|
||||
// The file pointer is set to the end of the file prior to each write.
|
||||
const PR_APPEND = 0x10;
|
||||
|
||||
// If the file exists, its length is truncated to 0.
|
||||
const PR_TRUNCATE = 0x20;
|
||||
|
||||
// If set, each write will wait for both the file data
|
||||
// and file status to be physically updated.
|
||||
const PR_SYNC = 0x40;
|
||||
|
||||
// If the file does not exist, the file is created. If the file already
|
||||
// exists, no action and NULL is returned.
|
||||
const PR_EXCL = 0x80;
|
||||
} catch (ex) {
|
||||
// probably not running in the test harness
|
||||
}
|
||||
|
||||
/** Init the file logger with the absolute path to the file.
|
||||
It will create and append if the file already exists **/
|
||||
var MozillaFileLogger = {}
|
||||
|
||||
MozillaFileLogger.init = function(path) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
MozillaFileLogger._file = Cc[LF_CID].createInstance(Ci.nsILocalFile);
|
||||
MozillaFileLogger._file.initWithPath(path);
|
||||
MozillaFileLogger._foStream = Cc[FOSTREAM_CID].createInstance(Ci.nsIFileOutputStream);
|
||||
MozillaFileLogger._foStream.init(this._file, PR_WRITE_ONLY | PR_CREATE_FILE | PR_APPEND,
|
||||
0664, 0);
|
||||
}
|
||||
|
||||
MozillaFileLogger.getLogCallback = function() {
|
||||
return function (msg) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var data = msg.num + " " + msg.level + " " + msg.info.join(' ') + "\n";
|
||||
MozillaFileLogger._foStream.write(data, data.length);
|
||||
if (data.indexOf("SimpleTest FINISH") >= 0) {
|
||||
MozillaFileLogger.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MozillaFileLogger.close = function() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
MozillaFileLogger._foStream.close();
|
||||
MozillaFileLogger._foStream = null;
|
||||
MozillaFileLogger._file = null;
|
||||
}
|
|
@ -0,0 +1,461 @@
|
|||
/**
|
||||
* SimpleTest, a partial Test.Simple/Test.More API compatible test library.
|
||||
*
|
||||
* Why?
|
||||
*
|
||||
* Test.Simple doesn't work on IE < 6.
|
||||
* TODO:
|
||||
* * Support the Test.Simple API used by MochiKit, to be able to test MochiKit
|
||||
* itself against IE 5.5
|
||||
*
|
||||
**/
|
||||
|
||||
if (typeof(SimpleTest) == "undefined") {
|
||||
var SimpleTest = {};
|
||||
}
|
||||
|
||||
var parentRunner = null;
|
||||
if (typeof(parent) != "undefined" && parent.TestRunner) {
|
||||
parentRunner = parent.TestRunner;
|
||||
} else if (parent && parent.wrappedJSObject &&
|
||||
parent.wrappedJSObject.TestRunner) {
|
||||
parentRunner = parent.wrappedJSObject.TestRunner;
|
||||
}
|
||||
|
||||
// Check to see if the TestRunner is present and has logging
|
||||
if (parentRunner) {
|
||||
SimpleTest._logEnabled = parentRunner.logEnabled;
|
||||
}
|
||||
|
||||
SimpleTest._tests = [];
|
||||
SimpleTest._stopOnLoad = true;
|
||||
|
||||
/**
|
||||
* Something like assert.
|
||||
**/
|
||||
SimpleTest.ok = function (condition, name, diag) {
|
||||
var test = {'result': !!condition, 'name': name, 'diag': diag || ""};
|
||||
if (SimpleTest._logEnabled)
|
||||
SimpleTest._logResult(test, "PASS", "FAIL");
|
||||
SimpleTest._tests.push(test);
|
||||
};
|
||||
|
||||
/**
|
||||
* Roughly equivalent to ok(a==b, name)
|
||||
**/
|
||||
SimpleTest.is = function (a, b, name) {
|
||||
var repr = MochiKit.Base.repr;
|
||||
SimpleTest.ok(a == b, name, "got " + repr(a) + ", expected " + repr(b));
|
||||
};
|
||||
|
||||
SimpleTest.isnot = function (a, b, name) {
|
||||
var repr = MochiKit.Base.repr;
|
||||
SimpleTest.ok(a != b, name, "Didn't expect " + repr(a) + ", but got it.");
|
||||
};
|
||||
|
||||
// --------------- Test.Builder/Test.More todo() -----------------
|
||||
|
||||
SimpleTest.todo = function(condition, name, diag) {
|
||||
var test = {'result': !!condition, 'name': name, 'diag': diag || "", todo: true};
|
||||
if (SimpleTest._logEnabled)
|
||||
SimpleTest._logResult(test, "TODO WORKED?", "TODO");
|
||||
SimpleTest._tests.push(test);
|
||||
}
|
||||
|
||||
SimpleTest._logResult = function(test, passString, failString) {
|
||||
var msg = test.result ? passString : failString;
|
||||
msg += " | " + test.name;
|
||||
var url = "";
|
||||
if (parentRunner.currentTestURL)
|
||||
url = " | " + parentRunner.currentTestURL;
|
||||
|
||||
if (test.result) {
|
||||
if (test.todo)
|
||||
parentRunner.logger.error(msg + url)
|
||||
else
|
||||
parentRunner.logger.log(msg);
|
||||
} else {
|
||||
msg += " | " + test.diag;
|
||||
if (test.todo)
|
||||
parentRunner.logger.log(msg)
|
||||
else
|
||||
parentRunner.logger.error(msg + url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies of is and isnot with the call to ok replaced by a call to todo.
|
||||
**/
|
||||
|
||||
SimpleTest.todo_is = function (a, b, name) {
|
||||
var repr = MochiKit.Base.repr;
|
||||
SimpleTest.todo(a == b, name, "got " + repr(a) + ", expected " + repr(b));
|
||||
};
|
||||
|
||||
SimpleTest.todo_isnot = function (a, b, name) {
|
||||
var repr = MochiKit.Base.repr;
|
||||
SimpleTest.todo(a != b, name, "Didn't expect " + repr(a) + ", but got it.");
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Makes a test report, returns it as a DIV element.
|
||||
**/
|
||||
SimpleTest.report = function () {
|
||||
var DIV = MochiKit.DOM.DIV;
|
||||
var passed = 0;
|
||||
var failed = 0;
|
||||
var todo = 0;
|
||||
var results = MochiKit.Base.map(
|
||||
function (test) {
|
||||
var cls, msg;
|
||||
if (test.todo && !test.result) {
|
||||
todo++;
|
||||
cls = "test_todo"
|
||||
msg = "todo - " + test.name + " " + test.diag;
|
||||
} else if (test.result &&!test.todo) {
|
||||
passed++;
|
||||
cls = "test_ok";
|
||||
msg = "ok - " + test.name;
|
||||
} else {
|
||||
failed++;
|
||||
cls = "test_not_ok";
|
||||
msg = "not ok - " + test.name + " " + test.diag;
|
||||
}
|
||||
return DIV({"class": cls}, msg);
|
||||
},
|
||||
SimpleTest._tests
|
||||
);
|
||||
var summary_class = ((failed == 0) ? 'all_pass' : 'some_fail');
|
||||
return DIV({'class': 'tests_report'},
|
||||
DIV({'class': 'tests_summary ' + summary_class},
|
||||
DIV({'class': 'tests_passed'}, "Passed: " + passed),
|
||||
DIV({'class': 'tests_failed'}, "Failed: " + failed),
|
||||
DIV({'class': 'tests_todo'}, "Todo: " + todo)),
|
||||
results
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle element visibility
|
||||
**/
|
||||
SimpleTest.toggle = function(el) {
|
||||
if (MochiKit.Style.computedStyle(el, 'display') == 'block') {
|
||||
el.style.display = 'none';
|
||||
} else {
|
||||
el.style.display = 'block';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle visibility for divs with a specific class.
|
||||
**/
|
||||
SimpleTest.toggleByClass = function (cls, evt) {
|
||||
var elems = getElementsByTagAndClassName('div', cls);
|
||||
MochiKit.Base.map(SimpleTest.toggle, elems);
|
||||
if (evt)
|
||||
evt.preventDefault();
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows the report in the browser
|
||||
**/
|
||||
|
||||
SimpleTest.showReport = function() {
|
||||
var togglePassed = A({'href': '#'}, "Toggle passed tests");
|
||||
var toggleFailed = A({'href': '#'}, "Toggle failed tests");
|
||||
togglePassed.onclick = partial(SimpleTest.toggleByClass, 'test_ok');
|
||||
toggleFailed.onclick = partial(SimpleTest.toggleByClass, 'test_not_ok');
|
||||
var body = document.body; // Handles HTML documents
|
||||
if (!body) {
|
||||
// Do the XML thing
|
||||
body = document.getElementsByTagNameNS("http://www.w3.org/1999/xhtml",
|
||||
"body")[0]
|
||||
}
|
||||
var firstChild = body.childNodes[0];
|
||||
var addNode;
|
||||
if (firstChild) {
|
||||
addNode = function (el) {
|
||||
body.insertBefore(el, firstChild);
|
||||
};
|
||||
} else {
|
||||
addNode = function (el) {
|
||||
body.appendChild(el)
|
||||
};
|
||||
}
|
||||
addNode(togglePassed);
|
||||
addNode(SPAN(null, " "));
|
||||
addNode(toggleFailed);
|
||||
addNode(SimpleTest.report());
|
||||
};
|
||||
|
||||
/**
|
||||
* Tells SimpleTest to don't finish the test when the document is loaded,
|
||||
* useful for asynchronous tests.
|
||||
*
|
||||
* When SimpleTest.waitForExplicitFinish is called,
|
||||
* explicit SimpleTest.finish() is required.
|
||||
**/
|
||||
SimpleTest.waitForExplicitFinish = function () {
|
||||
SimpleTest._stopOnLoad = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Executes a function shortly after the call, but lets the caller continue
|
||||
* working (or finish).
|
||||
*/
|
||||
SimpleTest.executeSoon = function(aFunc) {
|
||||
var tm = Components.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService(Components.interfaces.nsIThreadManager);
|
||||
|
||||
tm.mainThread.dispatch({
|
||||
run: function() {
|
||||
aFunc();
|
||||
}
|
||||
}, Components.interfaces.nsIThread.DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Talks to the TestRunner if being ran on a iframe and the parent has a
|
||||
* TestRunner object.
|
||||
**/
|
||||
SimpleTest.talkToRunner = function () {
|
||||
if (parentRunner) {
|
||||
parentRunner.testFinished(document);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Finishes the tests. This is automatically called, except when
|
||||
* SimpleTest.waitForExplicitFinish() has been invoked.
|
||||
**/
|
||||
SimpleTest.finish = function () {
|
||||
SimpleTest.showReport();
|
||||
SimpleTest.talkToRunner();
|
||||
};
|
||||
|
||||
|
||||
addLoadEvent(function() {
|
||||
if (SimpleTest._stopOnLoad) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
});
|
||||
|
||||
// --------------- Test.Builder/Test.More isDeeply() -----------------
|
||||
|
||||
|
||||
SimpleTest.DNE = {dne: 'Does not exist'};
|
||||
SimpleTest.LF = "\r\n";
|
||||
SimpleTest._isRef = function (object) {
|
||||
var type = typeof(object);
|
||||
return type == 'object' || type == 'function';
|
||||
};
|
||||
|
||||
|
||||
SimpleTest._deepCheck = function (e1, e2, stack, seen) {
|
||||
var ok = false;
|
||||
// Either they're both references or both not.
|
||||
var sameRef = !(!SimpleTest._isRef(e1) ^ !SimpleTest._isRef(e2));
|
||||
if (e1 == null && e2 == null) {
|
||||
ok = true;
|
||||
} else if (e1 != null ^ e2 != null) {
|
||||
ok = false;
|
||||
} else if (e1 == SimpleTest.DNE ^ e2 == SimpleTest.DNE) {
|
||||
ok = false;
|
||||
} else if (sameRef && e1 == e2) {
|
||||
// Handles primitives and any variables that reference the same
|
||||
// object, including functions.
|
||||
ok = true;
|
||||
} else if (SimpleTest.isa(e1, 'Array') && SimpleTest.isa(e2, 'Array')) {
|
||||
ok = SimpleTest._eqArray(e1, e2, stack, seen);
|
||||
} else if (typeof e1 == "object" && typeof e2 == "object") {
|
||||
ok = SimpleTest._eqAssoc(e1, e2, stack, seen);
|
||||
} else {
|
||||
// If we get here, they're not the same (function references must
|
||||
// always simply rererence the same function).
|
||||
stack.push({ vals: [e1, e2] });
|
||||
ok = false;
|
||||
}
|
||||
return ok;
|
||||
};
|
||||
|
||||
SimpleTest._eqArray = function (a1, a2, stack, seen) {
|
||||
// Return if they're the same object.
|
||||
if (a1 == a2) return true;
|
||||
|
||||
// JavaScript objects have no unique identifiers, so we have to store
|
||||
// references to them all in an array, and then compare the references
|
||||
// directly. It's slow, but probably won't be much of an issue in
|
||||
// practice. Start by making a local copy of the array to as to avoid
|
||||
// confusing a reference seen more than once (such as [a, a]) for a
|
||||
// circular reference.
|
||||
for (var j = 0; j < seen.length; j++) {
|
||||
if (seen[j][0] == a1) {
|
||||
return seen[j][1] == a2;
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, we haven't seen a1 before, so store it with reference
|
||||
// to a2.
|
||||
seen.push([ a1, a2 ]);
|
||||
|
||||
var ok = true;
|
||||
// Only examines enumerable attributes. Only works for numeric arrays!
|
||||
// Associative arrays return 0. So call _eqAssoc() for them, instead.
|
||||
var max = a1.length > a2.length ? a1.length : a2.length;
|
||||
if (max == 0) return SimpleTest._eqAssoc(a1, a2, stack, seen);
|
||||
for (var i = 0; i < max; i++) {
|
||||
var e1 = i > a1.length - 1 ? SimpleTest.DNE : a1[i];
|
||||
var e2 = i > a2.length - 1 ? SimpleTest.DNE : a2[i];
|
||||
stack.push({ type: 'Array', idx: i, vals: [e1, e2] });
|
||||
if (ok = SimpleTest._deepCheck(e1, e2, stack, seen)) {
|
||||
stack.pop();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
};
|
||||
|
||||
SimpleTest._eqAssoc = function (o1, o2, stack, seen) {
|
||||
// Return if they're the same object.
|
||||
if (o1 == o2) return true;
|
||||
|
||||
// JavaScript objects have no unique identifiers, so we have to store
|
||||
// references to them all in an array, and then compare the references
|
||||
// directly. It's slow, but probably won't be much of an issue in
|
||||
// practice. Start by making a local copy of the array to as to avoid
|
||||
// confusing a reference seen more than once (such as [a, a]) for a
|
||||
// circular reference.
|
||||
seen = seen.slice(0);
|
||||
for (var j = 0; j < seen.length; j++) {
|
||||
if (seen[j][0] == o1) {
|
||||
return seen[j][1] == o2;
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, we haven't seen o1 before, so store it with reference
|
||||
// to o2.
|
||||
seen.push([ o1, o2 ]);
|
||||
|
||||
// They should be of the same class.
|
||||
|
||||
var ok = true;
|
||||
// Only examines enumerable attributes.
|
||||
var o1Size = 0; for (var i in o1) o1Size++;
|
||||
var o2Size = 0; for (var i in o2) o2Size++;
|
||||
var bigger = o1Size > o2Size ? o1 : o2;
|
||||
for (var i in bigger) {
|
||||
var e1 = o1[i] == undefined ? SimpleTest.DNE : o1[i];
|
||||
var e2 = o2[i] == undefined ? SimpleTest.DNE : o2[i];
|
||||
stack.push({ type: 'Object', idx: i, vals: [e1, e2] });
|
||||
if (ok = SimpleTest._deepCheck(e1, e2, stack, seen)) {
|
||||
stack.pop();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
};
|
||||
|
||||
SimpleTest._formatStack = function (stack) {
|
||||
var variable = '$Foo';
|
||||
for (var i = 0; i < stack.length; i++) {
|
||||
var entry = stack[i];
|
||||
var type = entry['type'];
|
||||
var idx = entry['idx'];
|
||||
if (idx != null) {
|
||||
if (/^\d+$/.test(idx)) {
|
||||
// Numeric array index.
|
||||
variable += '[' + idx + ']';
|
||||
} else {
|
||||
// Associative array index.
|
||||
idx = idx.replace("'", "\\'");
|
||||
variable += "['" + idx + "']";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var vals = stack[stack.length-1]['vals'].slice(0, 2);
|
||||
var vars = [
|
||||
variable.replace('$Foo', 'got'),
|
||||
variable.replace('$Foo', 'expected')
|
||||
];
|
||||
|
||||
var out = "Structures begin differing at:" + SimpleTest.LF;
|
||||
for (var i = 0; i < vals.length; i++) {
|
||||
var val = vals[i];
|
||||
if (val == null) {
|
||||
val = 'undefined';
|
||||
} else {
|
||||
val == SimpleTest.DNE ? "Does not exist" : "'" + val + "'";
|
||||
}
|
||||
}
|
||||
|
||||
out += vars[0] + ' = ' + vals[0] + SimpleTest.LF;
|
||||
out += vars[1] + ' = ' + vals[1] + SimpleTest.LF;
|
||||
|
||||
return ' ' + out;
|
||||
};
|
||||
|
||||
|
||||
SimpleTest.isDeeply = function (it, as, name) {
|
||||
var ok;
|
||||
// ^ is the XOR operator.
|
||||
if (SimpleTest._isRef(it) ^ SimpleTest._isRef(as)) {
|
||||
// One's a reference, one isn't.
|
||||
ok = false;
|
||||
} else if (!SimpleTest._isRef(it) && !SimpleTest._isRef(as)) {
|
||||
// Neither is an object.
|
||||
ok = SimpleTest.is(it, as, name);
|
||||
} else {
|
||||
// We have two objects. Do a deep comparison.
|
||||
var stack = [], seen = [];
|
||||
if ( SimpleTest._deepCheck(it, as, stack, seen)) {
|
||||
ok = SimpleTest.ok(true, name);
|
||||
} else {
|
||||
ok = SimpleTest.ok(false, name, SimpleTest._formatStack(stack));
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
};
|
||||
|
||||
SimpleTest.typeOf = function (object) {
|
||||
var c = Object.prototype.toString.apply(object);
|
||||
var name = c.substring(8, c.length - 1);
|
||||
if (name != 'Object') return name;
|
||||
// It may be a non-core class. Try to extract the class name from
|
||||
// the constructor function. This may not work in all implementations.
|
||||
if (/function ([^(\s]+)/.test(Function.toString.call(object.constructor))) {
|
||||
return RegExp.$1;
|
||||
}
|
||||
// No idea. :-(
|
||||
return name;
|
||||
};
|
||||
|
||||
SimpleTest.isa = function (object, clas) {
|
||||
return SimpleTest.typeOf(object) == clas;
|
||||
};
|
||||
|
||||
// Global symbols:
|
||||
var ok = SimpleTest.ok;
|
||||
var is = SimpleTest.is;
|
||||
var isnot = SimpleTest.isnot;
|
||||
var todo = SimpleTest.todo;
|
||||
var todo_is = SimpleTest.todo_is;
|
||||
var todo_isnot = SimpleTest.todo_isnot;
|
||||
var isDeeply = SimpleTest.isDeeply;
|
||||
var oldOnError = window.onerror;
|
||||
window.onerror = function (ev) {
|
||||
is(0, 1, "Error thrown during test: " + ev);
|
||||
if (oldOnError) {
|
||||
try {
|
||||
oldOnError(ev);
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
if (SimpleTest._stopOnLoad == false) {
|
||||
// Need to finish() manually here
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
/**
|
||||
* TestRunner: A test runner for SimpleTest
|
||||
* TODO:
|
||||
*
|
||||
* * Avoid moving iframes: That causes reloads on mozilla and opera.
|
||||
*
|
||||
*
|
||||
**/
|
||||
var TestRunner = {};
|
||||
TestRunner.logEnabled = false;
|
||||
TestRunner._currentTest = 0;
|
||||
TestRunner.currentTestURL = "";
|
||||
TestRunner._urls = [];
|
||||
|
||||
TestRunner.timeout = 300; // seconds
|
||||
TestRunner.maxTimeouts = 4; // halt testing after too many timeouts
|
||||
|
||||
/**
|
||||
* Make sure the tests don't hang indefinitely.
|
||||
**/
|
||||
TestRunner._numTimeouts = 0;
|
||||
TestRunner._currentTestStartTime = new Date().valueOf();
|
||||
|
||||
TestRunner._checkForHangs = function() {
|
||||
if (TestRunner._currentTest < TestRunner._urls.length) {
|
||||
var runtime = (new Date().valueOf() - TestRunner._currentTestStartTime) / 1000;
|
||||
if (runtime >= TestRunner.timeout) {
|
||||
var frameWindow = $('testframe').contentWindow.wrappedJSObject ||
|
||||
$('testframe').contentWindow;
|
||||
frameWindow.SimpleTest.ok(false, "Test timed out.");
|
||||
|
||||
// If we have too many timeouts, give up. We don't want to wait hours
|
||||
// for results if some bug causes lots of tests to time out.
|
||||
if (++TestRunner._numTimeouts >= TestRunner.maxTimeouts) {
|
||||
TestRunner._haltTests = true;
|
||||
frameWindow.SimpleTest.ok(false, "Too many test timeouts, giving up.");
|
||||
}
|
||||
|
||||
frameWindow.SimpleTest.finish();
|
||||
}
|
||||
TestRunner.deferred = callLater(30, TestRunner._checkForHangs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called after generating the summary.
|
||||
**/
|
||||
TestRunner.onComplete = null;
|
||||
|
||||
/**
|
||||
* If logEnabled is true, this is the logger that will be used.
|
||||
**/
|
||||
TestRunner.logger = MochiKit.Logging.logger;
|
||||
|
||||
/**
|
||||
* Toggle element visibility
|
||||
**/
|
||||
TestRunner._toggle = function(el) {
|
||||
if (el.className == "noshow") {
|
||||
el.className = "";
|
||||
el.style.cssText = "";
|
||||
} else {
|
||||
el.className = "noshow";
|
||||
el.style.cssText = "width:0px; height:0px; border:0px;";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates the iframe that contains a test
|
||||
**/
|
||||
TestRunner._makeIframe = function (url, retry) {
|
||||
var iframe = $('testframe');
|
||||
if (url != "about:blank" && (!document.hasFocus() ||
|
||||
document.activeElement != iframe)) {
|
||||
// typically calling ourselves from setTimeout is sufficient
|
||||
// but we'll try focus() just in case that's needed
|
||||
window.focus();
|
||||
iframe.focus();
|
||||
if (retry < 3) {
|
||||
window.setTimeout('TestRunner._makeIframe("'+url+'", '+(retry+1)+')', 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TestRunner.logEnabled) {
|
||||
var frameWindow = $('testframe').contentWindow.wrappedJSObject ||
|
||||
$('testframe').contentWindow;
|
||||
TestRunner.logger.log("Error: Unable to restore focus, expect failures and timeouts.");
|
||||
}
|
||||
}
|
||||
window.scrollTo(0, $('indicator').offsetTop);
|
||||
iframe.src = url;
|
||||
iframe.name = url;
|
||||
iframe.width = "500";
|
||||
return iframe;
|
||||
};
|
||||
|
||||
/**
|
||||
* TestRunner entry point.
|
||||
*
|
||||
* The arguments are the URLs of the test to be ran.
|
||||
*
|
||||
**/
|
||||
TestRunner.runTests = function (/*url...*/) {
|
||||
if (TestRunner.logEnabled)
|
||||
TestRunner.logger.log("SimpleTest START");
|
||||
|
||||
TestRunner._urls = flattenArguments(arguments);
|
||||
$('testframe').src="";
|
||||
TestRunner._checkForHangs();
|
||||
window.focus();
|
||||
$('testframe').focus();
|
||||
TestRunner.runNextTest();
|
||||
};
|
||||
|
||||
/**
|
||||
* Run the next test. If no test remains, calls makeSummary
|
||||
**/
|
||||
TestRunner._haltTests = false;
|
||||
TestRunner.runNextTest = function() {
|
||||
if (TestRunner._currentTest < TestRunner._urls.length &&
|
||||
!TestRunner._haltTests) {
|
||||
var url = TestRunner._urls[TestRunner._currentTest];
|
||||
TestRunner.currentTestURL = url;
|
||||
|
||||
$("current-test-path").innerHTML = url;
|
||||
|
||||
TestRunner._currentTestStartTime = new Date().valueOf();
|
||||
|
||||
if (TestRunner.logEnabled)
|
||||
TestRunner.logger.log("Running " + url + "...");
|
||||
|
||||
TestRunner._makeIframe(url, 0);
|
||||
} else {
|
||||
$("current-test").innerHTML = "<b>Finished</b>";
|
||||
TestRunner._makeIframe("about:blank", 0);
|
||||
if (TestRunner.logEnabled) {
|
||||
TestRunner.logger.log("Passed: " + $("pass-count").innerHTML);
|
||||
TestRunner.logger.log("Failed: " + $("fail-count").innerHTML);
|
||||
TestRunner.logger.log("Todo: " + $("todo-count").innerHTML);
|
||||
TestRunner.logger.log("SimpleTest FINISHED");
|
||||
}
|
||||
if (TestRunner.onComplete)
|
||||
TestRunner.onComplete();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This stub is called by SimpleTest when a test is finished.
|
||||
**/
|
||||
TestRunner.testFinished = function(doc) {
|
||||
var finishedURL = TestRunner._urls[TestRunner._currentTest];
|
||||
|
||||
if (TestRunner.logEnabled)
|
||||
TestRunner.logger.debug("SimpleTest finished " + finishedURL);
|
||||
|
||||
TestRunner.updateUI();
|
||||
TestRunner._currentTest++;
|
||||
TestRunner.runNextTest();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the results.
|
||||
*/
|
||||
TestRunner.countResults = function(doc) {
|
||||
var nOK = withDocument(doc,
|
||||
partial(getElementsByTagAndClassName, 'div', 'test_ok')
|
||||
).length;
|
||||
var nNotOK = withDocument(doc,
|
||||
partial(getElementsByTagAndClassName, 'div', 'test_not_ok')
|
||||
).length;
|
||||
var nTodo = withDocument(doc,
|
||||
partial(getElementsByTagAndClassName, 'div', 'test_todo')
|
||||
).length;
|
||||
return {"OK": nOK, "notOK": nNotOK, "todo": nTodo};
|
||||
}
|
||||
|
||||
TestRunner.updateUI = function() {
|
||||
var results = TestRunner.countResults($('testframe').contentDocument);
|
||||
var passCount = parseInt($("pass-count").innerHTML) + results.OK;
|
||||
var failCount = parseInt($("fail-count").innerHTML) + results.notOK;
|
||||
var todoCount = parseInt($("todo-count").innerHTML) + results.todo;
|
||||
$("pass-count").innerHTML = passCount;
|
||||
$("fail-count").innerHTML = failCount;
|
||||
$("todo-count").innerHTML = todoCount;
|
||||
|
||||
// Set the top Green/Red bar
|
||||
var indicator = $("indicator");
|
||||
if (failCount > 0) {
|
||||
indicator.innerHTML = "Status: Fail";
|
||||
indicator.style.backgroundColor = "red";
|
||||
} else if (passCount > 0) {
|
||||
indicator.innerHTML = "Status: Pass";
|
||||
indicator.style.backgroundColor = "green";
|
||||
}
|
||||
|
||||
// Set the table values
|
||||
var trID = "tr-" + $('current-test-path').innerHTML;
|
||||
var row = $(trID);
|
||||
replaceChildNodes(row,
|
||||
TD({'style':
|
||||
{'backgroundColor': results.notOK > 0 ? "#f00":"#0d0"}}, results.OK),
|
||||
TD({'style':
|
||||
{'backgroundColor': results.notOK > 0 ? "#f00":"#0d0"}}, results.notOK),
|
||||
TD({'style': {'backgroundColor':
|
||||
results.todo > 0 ? "orange":"#0d0"}}, results.todo)
|
||||
);
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Automated Testing Code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2005
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Bob Clary <bob@bclary.com>
|
||||
* Jeff Walden <jwalden+code@mit.edu>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/*
|
||||
From mozilla/toolkit/content
|
||||
These files did not have a license
|
||||
*/
|
||||
|
||||
function quitHook()
|
||||
{
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "http://" + location.host + "/server/shutdown", true);
|
||||
xhr.onreadystatechange = function (event)
|
||||
{
|
||||
if (xhr.readyState == 4)
|
||||
goQuitApplication();
|
||||
};
|
||||
xhr.send(null);
|
||||
}
|
||||
|
||||
function canQuitApplication()
|
||||
{
|
||||
var os = Components.classes["@mozilla.org/observer-service;1"]
|
||||
.getService(Components.interfaces.nsIObserverService);
|
||||
if (!os)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]
|
||||
.createInstance(Components.interfaces.nsISupportsPRBool);
|
||||
os.notifyObservers(cancelQuit, "quit-application-requested", null);
|
||||
|
||||
// Something aborted the quit process.
|
||||
if (cancelQuit.data)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (ex)
|
||||
{
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function goQuitApplication()
|
||||
{
|
||||
const privs = 'UniversalXPConnect';
|
||||
|
||||
try
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege(privs);
|
||||
}
|
||||
catch(ex)
|
||||
{
|
||||
throw('goQuitApplication: privilege failure ' + ex);
|
||||
}
|
||||
|
||||
if (!canQuitApplication())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const kAppStartup = '@mozilla.org/toolkit/app-startup;1';
|
||||
const kAppShell = '@mozilla.org/appshell/appShellService;1';
|
||||
var appService;
|
||||
var forceQuit;
|
||||
|
||||
if (kAppStartup in Components.classes)
|
||||
{
|
||||
appService = Components.classes[kAppStartup].
|
||||
getService(Components.interfaces.nsIAppStartup);
|
||||
forceQuit = Components.interfaces.nsIAppStartup.eForceQuit;
|
||||
|
||||
}
|
||||
else if (kAppShell in Components.classes)
|
||||
{
|
||||
appService = Components.classes[kAppShell].
|
||||
getService(Components.interfaces.nsIAppShellService);
|
||||
forceQuit = Components.interfaces.nsIAppShellService.eForceQuit;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw 'goQuitApplication: no AppStartup/appShell';
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
appService.quit(forceQuit);
|
||||
}
|
||||
catch(ex)
|
||||
{
|
||||
throw('goQuitApplication: ' + ex);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2006
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Robert Sayre <sayrer@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
TestRunner.logEnabled = true;
|
||||
TestRunner.logger = new Logger();
|
||||
|
||||
// Check the query string for arguments
|
||||
var params = parseQueryString(location.search.substring(1), true);
|
||||
|
||||
// log levels for console and logfile
|
||||
var fileLevel = params.fileLevel || null;
|
||||
var consoleLevel = params.consoleLevel || null;
|
||||
|
||||
// closeWhenDone tells us to call quit.js when complete
|
||||
if (params.closeWhenDone) {
|
||||
TestRunner.onComplete = goQuitApplication;
|
||||
}
|
||||
|
||||
// logFile to write our results
|
||||
if (params.logFile) {
|
||||
MozillaFileLogger.init(params.logFile);
|
||||
TestRunner.logger.addListener("mozLogger", fileLevel + "", MozillaFileLogger.getLogCallback());
|
||||
}
|
||||
|
||||
// if we get a quiet param, don't log to the console
|
||||
if (!params.quiet) {
|
||||
function dumpListener(msg) {
|
||||
dump("*** " + msg.num + " " + msg.level + " " + msg.info.join(' ') + "\n");
|
||||
}
|
||||
TestRunner.logger.addListener("dumpListener", consoleLevel + "", dumpListener);
|
||||
}
|
||||
|
||||
var gTestList = [];
|
||||
var RunSet = {}
|
||||
RunSet.runall = function(e) {
|
||||
TestRunner.runTests(gTestList);
|
||||
}
|
||||
RunSet.reloadAndRunAll = function(e) {
|
||||
e.preventDefault();
|
||||
//window.location.hash = "";
|
||||
var addParam = "";
|
||||
if (params.autorun) {
|
||||
window.location.search += "";
|
||||
window.location.href = window.location.href;
|
||||
} else if (window.location.search) {
|
||||
window.location.href += "&autorun=1";
|
||||
} else {
|
||||
window.location.href += "?autorun=1";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// UI Stuff
|
||||
function toggleVisible(elem) {
|
||||
toggleElementClass("invisible", elem);
|
||||
}
|
||||
|
||||
function makeVisible(elem) {
|
||||
removeElementClass(elem, "invisible");
|
||||
}
|
||||
|
||||
function makeInvisible(elem) {
|
||||
addElementClass(elem, "invisible");
|
||||
}
|
||||
|
||||
function isVisible(elem) {
|
||||
// you may also want to check for
|
||||
// getElement(elem).style.display == "none"
|
||||
return !hasElementClass(elem, "invisible");
|
||||
};
|
||||
|
||||
function toggleNonTests (e) {
|
||||
e.preventDefault();
|
||||
var elems = getElementsByTagAndClassName("*", "non-test");
|
||||
for (var i="0"; i<elems.length; i++) {
|
||||
toggleVisible(elems[i]);
|
||||
}
|
||||
if (isVisible(elems[0])) {
|
||||
$("toggleNonTests").innerHTML = "Hide Non-Tests";
|
||||
} else {
|
||||
$("toggleNonTests").innerHTML = "Show Non-Tests";
|
||||
}
|
||||
}
|
||||
|
||||
// hook up our buttons
|
||||
function hookup() {
|
||||
connect("runtests", "onclick", RunSet, "reloadAndRunAll");
|
||||
connect("toggleNonTests", "onclick", toggleNonTests);
|
||||
// run automatically if
|
||||
if (params.autorun) {
|
||||
RunSet.runall();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
.test_ok {
|
||||
color: green;
|
||||
display: none;
|
||||
}
|
||||
.test_not_ok {
|
||||
color: red;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.test_ok, .test_not_ok {
|
||||
border-bottom-width: 2px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: black;
|
||||
}
|
||||
|
||||
.all_pass {
|
||||
background-color: lime;
|
||||
}
|
||||
|
||||
.some_fail {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
.tests_report {
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
width: 20em;
|
||||
display: table;
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2007
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Gavin Sharp <gavin@gavinsharp.com> (Original author)
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = testing/mochitest/tests/browser
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_BROWSER_TEST_FILES = browser_pass.js \
|
||||
browser_async.js \
|
||||
browser_scope.js \
|
||||
# Disabled, these are only good for testing the harness' failure reporting
|
||||
# browser_fail.js \
|
||||
# browser_fail_async_throw.js \
|
||||
# browser_fail_fp.js \
|
||||
# browser_fail_pf.js \
|
||||
# browser_fail_throw.js \
|
||||
# browser_fail_timeout.js \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_BROWSER_TEST_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
|
|
@ -0,0 +1,8 @@
|
|||
function test() {
|
||||
waitForExplicitFinish();
|
||||
function done() {
|
||||
ok(true, "timeout ran");
|
||||
finish();
|
||||
}
|
||||
setTimeout(done, 10000);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
function test() {
|
||||
ok(false, "fail ok");
|
||||
is(true, false, "fail is");
|
||||
isnot(true, true, "fail isnot");
|
||||
todo(true, "fail todo");
|
||||
todo_is(true, true, "fail todo_is");
|
||||
todo_isnot(true, false, "fail todo_isnot");
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
function test() {
|
||||
function end() {
|
||||
throw "thrown exception";
|
||||
}
|
||||
waitForExplicitFinish();
|
||||
setTimeout(end, 1000);
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
function test() {
|
||||
ok(false, "first fail ok");
|
||||
ok(true, "then pass ok");
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
function test() {
|
||||
ok(true, "first pass ok");
|
||||
ok(false, "then fail ok");
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
function test() {
|
||||
throw "thrown exception";
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
function test() {
|
||||
function end() {
|
||||
ok(true, "didn't time out?");
|
||||
finish();
|
||||
}
|
||||
waitForExplicitFinish();
|
||||
setTimeout(end, 20000);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
function test() {
|
||||
ok(true, "pass ok");
|
||||
is(true, true, "pass is");
|
||||
isnot(false, true, "pass isnot");
|
||||
todo(false, "pass todo");
|
||||
todo_is(false, true, "pass todo_is");
|
||||
todo_isnot(true, true, "pass todo_isnot");
|
||||
|
||||
var func = is;
|
||||
func(true, true, "pass indirect is");
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
function test() {
|
||||
ok(!!gBrowser, "gBrowser exists");
|
||||
is(gBrowser, getBrowser(), "both ways of getting tabbrowser work");
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
MochiKit = {__export__: true};
|
||||
load('tests/FakeJSAN.js')
|
||||
JSAN.use('MochiKit.MockDOM');
|
||||
var window = this;
|
||||
var document = MochiKit.MockDOM.document;
|
||||
JSAN.use('MochiKit.MochiKit');
|
|
@ -0,0 +1,72 @@
|
|||
<html>
|
||||
<head>
|
||||
<!-- This harness does not work locally in Safari -->
|
||||
<script type="text/javascript" src="../MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="SimpleTest/TestRunner.js"></script>
|
||||
<script type="text/javascript" src="SimpleTest/MozillaFileLogger.js"></script>
|
||||
<script type="text/javascript" src="SimpleTest/quit.js"></script>
|
||||
<!--<link rel="stylesheet" type="text/css" href="/static/main.css" />-->
|
||||
</head>
|
||||
<body>
|
||||
<p><a href="#" id="runtests">Click To Run Tests</a></p>
|
||||
<!--<div id="mt_maketemplate">Make Template for Bug <input type="text" /></div>-->
|
||||
<script type="text/javascript">
|
||||
|
||||
TestRunner.logEnabled = true;
|
||||
TestRunner.logger = new Logger();
|
||||
|
||||
// Check the query string for arguments
|
||||
var params = parseQueryString(location.search.substring(1), true);
|
||||
|
||||
// log levels for console and logfile
|
||||
var fileLevel = params.fileLevel || null;
|
||||
var consoleLevel = params.consoleLevel || null;
|
||||
|
||||
// closeWhenDone tells us to call quit.js when complete
|
||||
if (params.closeWhenDone) {
|
||||
TestRunner.onComplete = goQuitApplication;
|
||||
}
|
||||
|
||||
// logFile to write our results
|
||||
if (params.logFile) {
|
||||
MozillaFileLogger.init(params.logFile);
|
||||
TestRunner.logger.addListener("mozLogger", fileLevel + "", MozillaFileLogger.getLogCallback());
|
||||
}
|
||||
|
||||
// if we get a quiet param, don't log to the console
|
||||
if (!params.quiet) {
|
||||
function dumpListener(msg) {
|
||||
dump("*** " + msg.num + " " + msg.level + " " + msg.info.join(' ') + "\n");
|
||||
}
|
||||
TestRunner.logger.addListener("dumpListener", consoleLevel + "", dumpListener);
|
||||
}
|
||||
|
||||
var RunSet = {}
|
||||
RunSet.runall = function() {
|
||||
TestRunner.runTests(
|
||||
'test_bug362788.xhtml',
|
||||
'test_bug366645.xhtml'
|
||||
);
|
||||
};
|
||||
RunSet.reloadAndRunAll = function() {
|
||||
if (params.autorun) {
|
||||
window.location.href = window.location.href;
|
||||
} else if (location.search) {
|
||||
window.location.href = window.location.href + "&autorun=1";
|
||||
} else {
|
||||
window.location.href = window.location.href + "?autorun=1";
|
||||
}
|
||||
};
|
||||
|
||||
// run automatically if
|
||||
if (params.autorun) {
|
||||
RunSet.runall();
|
||||
}
|
||||
|
||||
// hook up our buttons
|
||||
connect("runtests", "onclick", RunSet, "reloadAndRunAll");
|
||||
|
||||
</script>
|
||||
<small>Based on the <a href="http://www.mochikit.com/">MochiKit</a> unit tests.</small>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,16 @@
|
|||
load('tests/cli.js');
|
||||
|
||||
JSAN.use('MochiKit.Test');
|
||||
|
||||
print("[[ MochiKit.Base ]]");
|
||||
runTests('tests.test_Base');
|
||||
print("[[ MochiKit.Color ]]");
|
||||
runTests('tests.test_Color');
|
||||
print("[[ MochiKit.DateTime ]]");
|
||||
runTests('tests.test_DateTime');
|
||||
print("[[ MochiKit.Format ]]");
|
||||
runTests('tests.test_Format');
|
||||
print("[[ MochiKit.Iter ]]");
|
||||
runTests('tests.test_Iter');
|
||||
print("[[ MochiKit.Logging ]]");
|
||||
runTests('tests.test_Logging');
|
|
@ -0,0 +1,500 @@
|
|||
if (typeof(dojo) != 'undefined') { dojo.require('MochiKit.Base'); }
|
||||
if (typeof(JSAN) != 'undefined') { JSAN.use('MochiKit.Base'); }
|
||||
if (typeof(tests) == 'undefined') { tests = {}; }
|
||||
|
||||
tests.test_Base = function (t) {
|
||||
// test bind
|
||||
var not_self = {"toString": function () { return "not self"; } };
|
||||
var self = {"toString": function () { return "self"; } };
|
||||
var func = function (arg) { return this.toString() + " " + arg; };
|
||||
var boundFunc = bind(func, self);
|
||||
not_self.boundFunc = boundFunc;
|
||||
|
||||
t.is( isEmpty([], [], ""), true, "isEmpty true" )
|
||||
t.is( isEmpty([], [1], ""), true, "isEmpty true" )
|
||||
t.is( isNotEmpty([], [], ""), false, "isNotEmpty false" )
|
||||
t.is( isNotEmpty([], [1], ""), false, "isNotEmpty false" )
|
||||
|
||||
t.is( isEmpty([1], [1], "1"), false, "isEmpty false" )
|
||||
t.is( isEmpty([1], [1], "1"), false, "isEmpty false" )
|
||||
t.is( isNotEmpty([1], [1], "1"), true, "isNotEmpty true" )
|
||||
t.is( isNotEmpty([1], [1], "1"), true, "isNotEmpty true" )
|
||||
|
||||
t.is( boundFunc("foo"), "self foo", "boundFunc bound to self properly" );
|
||||
t.is( not_self.boundFunc("foo"), "self foo", "boundFunc bound to self on another obj" );
|
||||
t.is( bind(boundFunc, not_self)("foo"), "not self foo", "boundFunc successfully rebound!" );
|
||||
t.is( bind(boundFunc, undefined, "foo")(), "self foo", "boundFunc partial no self change" );
|
||||
t.is( bind(boundFunc, not_self, "foo")(), "not self foo", "boundFunc partial self change" );
|
||||
|
||||
// test method
|
||||
not_self = {"toString": function () { return "not self"; } };
|
||||
self = {"toString": function () { return "self"; } };
|
||||
func = function (arg) { return this.toString() + " " + arg; };
|
||||
var boundMethod = method(self, func);
|
||||
not_self.boundMethod = boundMethod;
|
||||
|
||||
t.is( boundMethod("foo"), "self foo", "boundMethod bound to self properly" );
|
||||
t.is( not_self.boundMethod("foo"), "self foo", "boundMethod bound to self on another obj" );
|
||||
t.is( method(not_self, boundMethod)("foo"), "not self foo", "boundMethod successfully rebound!" );
|
||||
t.is( method(undefined, boundMethod, "foo")(), "self foo", "boundMethod partial no self change" );
|
||||
t.is( method(not_self, boundMethod, "foo")(), "not self foo", "boundMethod partial self change" );
|
||||
|
||||
|
||||
|
||||
|
||||
// test bindMethods
|
||||
|
||||
var O = function (value) {
|
||||
bindMethods(this);
|
||||
this.value = value;
|
||||
};
|
||||
O.prototype.func = function () {
|
||||
return this.value;
|
||||
};
|
||||
|
||||
var o = new O("boring");
|
||||
var p = {};
|
||||
p.func = o.func;
|
||||
var func = o.func;
|
||||
t.is( o.func(), "boring", "bindMethods doesn't break shit" );
|
||||
t.is( p.func(), "boring", "bindMethods works on other objects" );
|
||||
t.is( func(), "boring", "bindMethods works on functions" );
|
||||
|
||||
var p = clone(o);
|
||||
t.ok( p instanceof O, "cloned correct inheritance" );
|
||||
var q = clone(p);
|
||||
t.ok( q instanceof O, "clone-cloned correct inheritance" );
|
||||
q.foo = "bar";
|
||||
t.is( p.foo, undefined, "clone-clone is copy-on-write" );
|
||||
p.bar = "foo";
|
||||
t.is( o.bar, undefined, "clone is copy-on-write" );
|
||||
t.is( q.bar, "foo", "clone-clone has proper delegation" );
|
||||
// unbind
|
||||
p.func = bind(p.func, null);
|
||||
t.is( p.func(), "boring", "clone function calls correct" );
|
||||
q.value = "awesome";
|
||||
t.is( q.func(), "awesome", "clone really does work" );
|
||||
|
||||
// test boring boolean funcs
|
||||
|
||||
t.is( isCallable(isCallable), true, "isCallable returns true on itself" );
|
||||
t.is( isCallable(1), false, "isCallable returns false on numbers" );
|
||||
|
||||
t.is( isUndefined(null), false, "null is not undefined" );
|
||||
t.is( isUndefined(""), false, "empty string is not undefined" );
|
||||
t.is( isUndefined(undefined), true, "undefined is undefined" );
|
||||
t.is( isUndefined({}.foo), true, "missing property is undefined" );
|
||||
|
||||
t.is( isUndefinedOrNull(null), true, "null is undefined or null" );
|
||||
t.is( isUndefinedOrNull(""), false, "empty string is not undefined or null" );
|
||||
t.is( isUndefinedOrNull(undefined), true, "undefined is undefined or null" );
|
||||
t.is( isUndefinedOrNull({}.foo), true, "missing property is undefined or null" );
|
||||
|
||||
// test extension of arrays
|
||||
var a = [];
|
||||
var b = [];
|
||||
var three = [1, 2, 3];
|
||||
|
||||
extend(a, three, 1);
|
||||
t.ok( objEqual(a, [2, 3]), "extend to an empty array" );
|
||||
extend(a, three, 1)
|
||||
t.ok( objEqual(a, [2, 3, 2, 3]), "extend to a non-empty array" );
|
||||
|
||||
extend(b, three);
|
||||
t.ok( objEqual(b, three), "extend of an empty array" );
|
||||
|
||||
t.is( compare(1, 2), -1, "numbers compare lt" );
|
||||
t.is( compare(2, 1), 1, "numbers compare gt" );
|
||||
t.is( compare(1, 1), 0, "numbers compare eq" );
|
||||
t.is( compare([1], [1]), 0, "arrays compare eq" );
|
||||
t.is( compare([1], [1, 2]), -1, "arrays compare lt (length)" );
|
||||
t.is( compare([1, 2], [2, 1]), -1, "arrays compare lt (contents)" );
|
||||
t.is( compare([1, 2], [1]), 1, "arrays compare gt (length)" );
|
||||
t.is( compare([2, 1], [1, 1]), 1, "arrays compare gt (contents)" );
|
||||
|
||||
// test partial application
|
||||
var a = [];
|
||||
var func = function (a, b) {
|
||||
if (arguments.length != 2) {
|
||||
return "bad args";
|
||||
} else {
|
||||
return this.value + a + b;
|
||||
}
|
||||
};
|
||||
var self = {"value": 1, "func": func};
|
||||
var self2 = {"value": 2};
|
||||
t.is( self.func(2, 3), 6, "setup for test is correct" );
|
||||
self.funcTwo = partial(self.func, 2);
|
||||
t.is( self.funcTwo(3), 6, "partial application works" );
|
||||
t.is( self.funcTwo(3), 6, "partial application works still" );
|
||||
t.is( bind(self.funcTwo, self2)(3), 7, "rebinding partial works" );
|
||||
self.funcTwo = bind(bind(self.funcTwo, self2), null);
|
||||
t.is( self.funcTwo(3), 6, "re-unbinding partial application works" );
|
||||
|
||||
|
||||
// nodeWalk test
|
||||
// ... looks a lot like a DOM tree on purpose
|
||||
var tree = {
|
||||
"id": "nodeWalkTestTree",
|
||||
"test:int": "1",
|
||||
"childNodes": [
|
||||
{
|
||||
"test:int": "2",
|
||||
"childNodes": [
|
||||
{"test:int": "5"},
|
||||
"ignored string",
|
||||
{"ignored": "object"},
|
||||
["ignored", "list"],
|
||||
{
|
||||
"test:skipchildren": "1",
|
||||
"childNodes": [{"test:int": 6}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{"test:int": "3"},
|
||||
{"test:int": "4"}
|
||||
]
|
||||
}
|
||||
|
||||
var visitedNodes = [];
|
||||
nodeWalk(tree, function (node) {
|
||||
var attr = node["test:int"];
|
||||
if (attr) {
|
||||
visitedNodes.push(attr);
|
||||
}
|
||||
if (node["test:skipchildren"]) {
|
||||
return;
|
||||
}
|
||||
return node.childNodes;
|
||||
});
|
||||
|
||||
t.ok( objEqual(visitedNodes, ["1", "2", "3", "4", "5"]), "nodeWalk looks like it works");
|
||||
|
||||
// test map
|
||||
var minusOne = function (x) { return x - 1; };
|
||||
var res = map(minusOne, [1, 2, 3]);
|
||||
t.ok( objEqual(res, [0, 1, 2]), "map works" );
|
||||
|
||||
var res2 = xmap(minusOne, 1, 2, 3);
|
||||
t.ok( objEqual(res2, res), "xmap works" );
|
||||
|
||||
res = map(operator.add, [1, 2, 3], [2, 4, 6]);
|
||||
t.ok( objEqual(res, [3, 6, 9]), "map(fn, p, q) works" );
|
||||
|
||||
res = map(operator.add, [1, 2, 3], [2, 4, 6, 8]);
|
||||
t.ok( objEqual(res, [3, 6, 9]), "map(fn, p, q) works (q long)" );
|
||||
|
||||
res = map(operator.add, [1, 2, 3, 4], [2, 4, 6]);
|
||||
t.ok( objEqual(res, [3, 6, 9]), "map(fn, p, q) works (p long)" );
|
||||
|
||||
res = map(null, [1, 2, 3], [2, 4, 6]);
|
||||
t.ok( objEqual(res, [[1, 2], [2, 4], [3, 6]]), "map(null, p, q) works" );
|
||||
|
||||
res = zip([1, 2, 3], [2, 4, 6]);
|
||||
t.ok( objEqual(res, [[1, 2], [2, 4], [3, 6]]), "zip(p, q) works" );
|
||||
|
||||
res = map(null, [1, 2, 3]);
|
||||
t.ok( objEqual(res, [1, 2, 3]), "map(null, lst) works" );
|
||||
|
||||
|
||||
|
||||
|
||||
t.is( isNotEmpty("foo"), true, "3 char string is not empty" );
|
||||
t.is( isNotEmpty(""), false, "0 char string is empty" );
|
||||
t.is( isNotEmpty([1, 2, 3]), true, "3 element list is not empty" );
|
||||
t.is( isNotEmpty([]), false, "0 element list is empty" );
|
||||
|
||||
// test filter
|
||||
var greaterThanThis = function (x) { return x > this; };
|
||||
var greaterThanOne = function (x) { return x > 1; };
|
||||
var res = filter(greaterThanOne, [-1, 0, 1, 2, 3]);
|
||||
t.ok( objEqual(res, [2, 3]), "filter works" );
|
||||
var res = filter(greaterThanThis, [-1, 0, 1, 2, 3], 1);
|
||||
t.ok( objEqual(res, [2, 3]), "filter self works" );
|
||||
var res2 = xfilter(greaterThanOne, -1, 0, 1, 2, 3);
|
||||
t.ok( objEqual(res2, res), "xfilter works" );
|
||||
|
||||
t.is(objMax(1, 2, 9, 12, 42, -16, 16), 42, "objMax works (with numbers)");
|
||||
t.is(objMin(1, 2, 9, 12, 42, -16, 16), -16, "objMin works (with numbers)");
|
||||
|
||||
// test adapter registry
|
||||
|
||||
var R = new AdapterRegistry();
|
||||
R.register("callable", isCallable, function () { return "callable"; });
|
||||
R.register("arrayLike", isArrayLike, function () { return "arrayLike"; });
|
||||
t.is( R.match(function () {}), "callable", "registry found callable" );
|
||||
t.is( R.match([]), "arrayLike", "registry found ArrayLike" );
|
||||
try {
|
||||
R.match(null);
|
||||
t.ok( false, "non-matching didn't raise!" );
|
||||
} catch (e) {
|
||||
t.is( e, NotFound, "non-matching raised correctly" );
|
||||
}
|
||||
R.register("undefinedOrNull", isUndefinedOrNull, function () { return "undefinedOrNull" });
|
||||
R.register("undefined", isUndefined, function () { return "undefined" });
|
||||
t.is( R.match(undefined), "undefinedOrNull", "priorities are as documented" );
|
||||
t.ok( R.unregister("undefinedOrNull"), "removed adapter" );
|
||||
t.is( R.match(undefined), "undefined", "adapter was removed" );
|
||||
R.register("undefinedOrNull", isUndefinedOrNull, function () { return "undefinedOrNull" }, true);
|
||||
t.is( R.match(undefined), "undefinedOrNull", "override works" );
|
||||
|
||||
var a1 = {"a": 1, "b": 2, "c": 2};
|
||||
var a2 = {"a": 2, "b": 1, "c": 2};
|
||||
t.is( keyComparator("a")(a1, a2), -1, "keyComparator 1 lt" );
|
||||
t.is( keyComparator("c")(a1, a2), 0, "keyComparator 1 eq" );
|
||||
t.is( keyComparator("c", "b")(a1, a2), 1, "keyComparator 2 eq gt" );
|
||||
t.is( keyComparator("c", "a")(a1, a2), -1, "keyComparator 2 eq lt" );
|
||||
t.is( reverseKeyComparator("a")(a1, a2), 1, "reverseKeyComparator" );
|
||||
t.is( compare(concat([1], [2], [3]), [1, 2, 3]), 0, "concat" );
|
||||
t.is( repr("foo"), '"foo"', "string repr" );
|
||||
t.is( repr(1), '1', "number repr" );
|
||||
t.is( listMin([1, 3, 5, 3, -1]), -1, "listMin" );
|
||||
t.is( objMin(1, 3, 5, 3, -1), -1, "objMin" );
|
||||
t.is( listMax([1, 3, 5, 3, -1]), 5, "listMax" );
|
||||
t.is( objMax(1, 3, 5, 3, -1), 5, "objMax" );
|
||||
|
||||
var v = keys(a1);
|
||||
v.sort();
|
||||
t.is( compare(v, ["a", "b", "c"]), 0, "keys" );
|
||||
v = items(a1);
|
||||
v.sort();
|
||||
t.is( compare(v, [["a", 1], ["b", 2], ["c", 2]]), 0, "items" );
|
||||
|
||||
var StringMap = function() {};
|
||||
a = new StringMap();
|
||||
a.foo = "bar";
|
||||
b = new StringMap();
|
||||
b.foo = "bar";
|
||||
try {
|
||||
compare(a, b);
|
||||
t.ok( false, "bad comparison registered!?" );
|
||||
} catch (e) {
|
||||
t.ok( e instanceof TypeError, "bad comparison raised TypeError" );
|
||||
}
|
||||
|
||||
t.is( repr(a), "[object Object]", "default repr for StringMap" );
|
||||
var isStringMap = function () {
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
if (!(arguments[i] instanceof StringMap)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
registerRepr("stringMap",
|
||||
isStringMap,
|
||||
function (obj) {
|
||||
return "StringMap(" + repr(items(obj)) + ")";
|
||||
}
|
||||
);
|
||||
|
||||
t.is( repr(a), 'StringMap([["foo", "bar"]])', "repr worked" );
|
||||
|
||||
// not public API
|
||||
MochiKit.Base.reprRegistry.unregister("stringMap");
|
||||
|
||||
t.is( repr(a), "[object Object]", "default repr for StringMap" );
|
||||
|
||||
registerComparator("stringMap",
|
||||
isStringMap,
|
||||
function (a, b) {
|
||||
// no sorted(...) in base
|
||||
a = items(a);
|
||||
b = items(b);
|
||||
a.sort(compare);
|
||||
b.sort(compare);
|
||||
return compare(a, b);
|
||||
}
|
||||
);
|
||||
|
||||
t.is( compare(a, b), 0, "registerComparator" );
|
||||
|
||||
update(a, {"foo": "bar"}, {"wibble": "baz"}, undefined, null, {"grr": 1});
|
||||
t.is( a.foo, "bar", "update worked (first obj)" );
|
||||
t.is( a.wibble, "baz", "update worked (second obj)" );
|
||||
t.is( a.grr, 1, "update worked (skipped undefined and null)" );
|
||||
t.is( compare(a, b), 1, "update worked (comparison)" );
|
||||
|
||||
|
||||
setdefault(a, {"foo": "unf"}, {"bar": "web taco"} );
|
||||
t.is( a.foo, "bar", "setdefault worked (skipped existing)" );
|
||||
t.is( a.bar, "web taco", "setdefault worked (set non-existing)" );
|
||||
|
||||
var c = items(merge({"foo": "bar"}, {"wibble": "baz"}));
|
||||
c.sort(compare);
|
||||
t.is( compare(c, [["foo", "bar"], ["wibble", "baz"]]), 0, "merge worked" );
|
||||
|
||||
// not public API
|
||||
MochiKit.Base.comparatorRegistry.unregister("stringMap");
|
||||
|
||||
try {
|
||||
compare(a, b);
|
||||
t.ok( false, "bad comparison registered!?" );
|
||||
} catch (e) {
|
||||
t.ok( e instanceof TypeError, "bad comparison raised TypeError" );
|
||||
}
|
||||
|
||||
var o = {"__repr__": function () { return "__repr__"; }};
|
||||
t.is( repr(o), "__repr__", "__repr__ protocol" );
|
||||
t.is( repr(MochiKit.Base), MochiKit.Base.__repr__(), "__repr__ protocol when repr is defined" );
|
||||
var o = {"NAME": "NAME"};
|
||||
t.is( repr(o), "NAME", "NAME protocol (obj)" );
|
||||
o = function () { return "TACO" };
|
||||
o.NAME = "NAME";
|
||||
t.is( repr(o), "NAME", "NAME protocol (func)" );
|
||||
|
||||
t.is( repr(MochiKit.Base.nameFunctions), "MochiKit.Base.nameFunctions", "test nameFunctions" );
|
||||
// Done!
|
||||
|
||||
t.is( urlEncode("1+2=2").toUpperCase(), "1%2B2%3D2", "urlEncode" );
|
||||
t.is( queryString(["a", "b"], [1, "two"]), "a=1&b=two", "queryString");
|
||||
t.is( queryString({"a": 1}), "a=1", "one item alternate form queryString" );
|
||||
var o = {"a": 1, "b": 2, "c": function() {}};
|
||||
var res = queryString(o).split("&");
|
||||
res.sort();
|
||||
t.is( res.join("&"), "a=1&b=2", "two item alternate form queryString, function skip" );
|
||||
var res = parseQueryString("1+1=2&b=3%3D2");
|
||||
t.is( res["1 1"], "2", "parseQueryString pathological name" );
|
||||
t.is( res.b, "3=2", "parseQueryString second name:value pair" );
|
||||
var res = parseQueryString("foo=one&foo=two", true);
|
||||
t.is( res["foo"].join(" "), "one two", "parseQueryString useArrays" );
|
||||
var res = parseQueryString("?foo=2&bar=1");
|
||||
t.is( res["foo"], "2", "parseQueryString strip leading question mark");
|
||||
|
||||
t.is( serializeJSON("foo\n\r\b\f\t"), "\"foo\\n\\r\\b\\f\\t\"", "string JSON" );
|
||||
t.is( serializeJSON(null), "null", "null JSON");
|
||||
try {
|
||||
serializeJSON(undefined);
|
||||
t.ok(false, "undefined should not be serializable");
|
||||
} catch (e) {
|
||||
t.ok(e instanceof TypeError, "undefined not serializable");
|
||||
}
|
||||
t.is( serializeJSON(1), "1", "1 JSON");
|
||||
t.is( serializeJSON(1.23), "1.23", "1.23 JSON");
|
||||
t.is( serializeJSON(serializeJSON), null, "function JSON (null, not string)" );
|
||||
t.is( serializeJSON([1, "2", 3.3]), "[1, \"2\", 3.3]", "array JSON" );
|
||||
var res = evalJSON(serializeJSON({"a":1, "b":2}));
|
||||
t.is( res.a, 1, "evalJSON on an object (1)" );
|
||||
t.is( res.b, 2, "evalJSON on an object (2)" );
|
||||
var res = {"a": 1, "b": 2, "json": function () { return this; }};
|
||||
var res = evalJSON(serializeJSON(res));
|
||||
t.is( res.a, 1, "evalJSON on an object that jsons self (1)" );
|
||||
t.is( res.b, 2, "evalJSON on an object that jsons self (2)" );
|
||||
var strJSON = {"a": 1, "b": 2, "json": function () { return "json"; }};
|
||||
t.is( serializeJSON(strJSON), "\"json\"", "json serialization calling" );
|
||||
t.is( serializeJSON([strJSON]), "[\"json\"]", "json serialization calling in a structure" );
|
||||
registerJSON("isDateLike",
|
||||
isDateLike,
|
||||
function (d) {
|
||||
return "this was a date";
|
||||
}
|
||||
);
|
||||
t.is( serializeJSON(new Date()), "\"this was a date\"", "json registry" );
|
||||
MochiKit.Base.jsonRegistry.unregister("isDateLike");
|
||||
|
||||
var a = {"foo": {"bar": 12, "wibble": 13}};
|
||||
var b = {"foo": {"baz": 4, "bar": 16}, "bar": 4};
|
||||
updatetree(a, b);
|
||||
var expect = [["bar", 16], ["baz", 4], ["wibble", 13]];
|
||||
var got = items(a.foo);
|
||||
got.sort(compare);
|
||||
t.is( repr(got), repr(expect), "updatetree merge" );
|
||||
t.is( a.bar, 4, "updatetree insert" );
|
||||
|
||||
var c = counter();
|
||||
t.is( c(), 1, "counter starts at 1" );
|
||||
t.is( c(), 2, "counter increases" );
|
||||
c = counter(2);
|
||||
t.is( c(), 2, "counter starts at 2" );
|
||||
t.is( c(), 3, "counter increases" );
|
||||
|
||||
t.is( findValue([1, 2, 3], 4), -1, "findValue returns -1 on not found");
|
||||
t.is( findValue([1, 2, 3], 1), 0, "findValue returns correct index");
|
||||
t.is( findValue([1, 2, 3], 1, 1), -1, "findValue honors start");
|
||||
t.is( findValue([1, 2, 3], 2, 0, 1), -1, "findValue honors end");
|
||||
t.is( findIdentical([1, 2, 3], 4), -1, "findIdentical returns -1");
|
||||
t.is( findIdentical([1, 2, 3], 1), 0, "findIdentical returns correct index");
|
||||
t.is( findIdentical([1, 2, 3], 1, 1), -1, "findIdentical honors start");
|
||||
t.is( findIdentical([1, 2, 3], 2, 0, 1), -1, "findIdentical honors end");
|
||||
t.is( isNull(undefined), false, "isNull doesn't match undefined" );
|
||||
|
||||
var flat = flattenArguments(1, "2", 3, [4, [5, [6, 7], 8, [], 9]]);
|
||||
var expect = [1, "2", 3, 4, 5, 6, 7, 8, 9];
|
||||
t.is( repr(flat), repr(expect), "flattenArguments" );
|
||||
|
||||
var fn = function () {
|
||||
return [this, concat(arguments)];
|
||||
}
|
||||
t.is( methodcaller("toLowerCase")("FOO"), "foo", "methodcaller with a method name" );
|
||||
t.is( repr(methodcaller(fn, 2, 3)(1)), "[1, [2, 3]]", "methodcaller with a function" );
|
||||
|
||||
var f1 = function (x) { return [1, x]; };
|
||||
var f2 = function (x) { return [2, x]; };
|
||||
var f3 = function (x) { return [3, x]; };
|
||||
t.is( repr(f1(f2(f3(4)))), "[1, [2, [3, 4]]]", "test the compose test" );
|
||||
t.is( repr(compose(f1,f2,f3)(4)), "[1, [2, [3, 4]]]", "three fn composition works" );
|
||||
t.is( repr(compose(compose(f1,f2),f3)(4)), "[1, [2, [3, 4]]]", "associative left" );
|
||||
t.is( repr(compose(f1,compose(f2,f3))(4)), "[1, [2, [3, 4]]]", "associative right" );
|
||||
|
||||
try {
|
||||
compose(f1, "foo");
|
||||
t.ok( false, "wrong compose argument not raised!" );
|
||||
} catch (e) {
|
||||
t.is( e.name, 'TypeError', "wrong compose argument raised correctly" );
|
||||
}
|
||||
|
||||
t.is(camelize('one'), 'one', 'one word');
|
||||
t.is(camelize('one-two'), 'oneTwo', 'two words');
|
||||
t.is(camelize('one-two-three'), 'oneTwoThree', 'three words');
|
||||
t.is(camelize('1-one'), '1One', 'letter and word');
|
||||
t.is(camelize('one-'), 'one', 'trailing hyphen');
|
||||
t.is(camelize('-one'), 'One', 'starting hyphen');
|
||||
t.is(camelize('o-two'), 'oTwo', 'one character and word');
|
||||
|
||||
var flat = flattenArray([1, "2", 3, [4, [5, [6, 7], 8, [], 9]]]);
|
||||
var expect = [1, "2", 3, 4, 5, 6, 7, 8, 9];
|
||||
t.is( repr(flat), repr(expect), "flattenArray" );
|
||||
|
||||
/* mean */
|
||||
try {
|
||||
mean();
|
||||
t.ok( false, "no arguments didn't raise!" );
|
||||
} catch (e) {
|
||||
t.is( e.name, 'TypeError', "no arguments raised correctly" );
|
||||
}
|
||||
t.is( mean(1), 1, 'single argument (arg list)');
|
||||
t.is( mean([1]), 1, 'single argument (array)');
|
||||
t.is( mean(1,2,3), 2, 'three arguments (arg list)');
|
||||
t.is( mean([1,2,3]), 2, 'three arguments (array)');
|
||||
t.is( average(1), 1, 'test the average alias');
|
||||
|
||||
/* median */
|
||||
try {
|
||||
median();
|
||||
t.ok( false, "no arguments didn't raise!" );
|
||||
} catch (e) {
|
||||
t.is( e.name, 'TypeError', "no arguments raised correctly" );
|
||||
}
|
||||
t.is( median(1), 1, 'single argument (arg list)');
|
||||
t.is( median([1]), 1, 'single argument (array)');
|
||||
t.is( median(3,1,2), 2, 'three arguments (arg list)');
|
||||
t.is( median([3,1,2]), 2, 'three arguments (array)');
|
||||
t.is( median(3,1,2,4), 2.5, 'four arguments (arg list)');
|
||||
t.is( median([3,1,2,4]), 2.5, 'four arguments (array)');
|
||||
|
||||
/* #185 */
|
||||
t.is( serializeJSON(parseQueryString("")), "{}", "parseQueryString('')" );
|
||||
t.is( serializeJSON(parseQueryString("", true)), "{}", "parseQueryString('', true)" );
|
||||
|
||||
/* #109 */
|
||||
t.is( queryString({ids: [1,2,3]}), "ids=1&ids=2&ids=3", "queryString array value" );
|
||||
t.is( queryString({ids: "123"}), "ids=123", "queryString string value" );
|
||||
|
||||
/* test values */
|
||||
var o = {a: 1, b: 2, c: 4, d: -1};
|
||||
var got = values(o);
|
||||
got.sort();
|
||||
t.is( repr(got), repr([-1, 1, 2, 4]), "values()" );
|
||||
|
||||
};
|
|
@ -0,0 +1,128 @@
|
|||
if (typeof(dojo) != 'undefined') { dojo.require('MochiKit.Color'); }
|
||||
if (typeof(JSAN) != 'undefined') { JSAN.use('MochiKit.Color'); }
|
||||
if (typeof(tests) == 'undefined') { tests = {}; }
|
||||
|
||||
tests.test_Color = function (t) {
|
||||
var approx = function (a, b, msg) {
|
||||
return t.is(a.toPrecision(4), b.toPrecision(4), msg);
|
||||
};
|
||||
|
||||
t.is( Color.whiteColor().toHexString(), "#ffffff", "whiteColor has right hex" );
|
||||
t.is( Color.blackColor().toHexString(), "#000000", "blackColor has right hex" );
|
||||
t.is( Color.blueColor().toHexString(), "#0000ff", "blueColor has right hex" );
|
||||
t.is( Color.redColor().toHexString(), "#ff0000", "redColor has right hex" );
|
||||
t.is( Color.greenColor().toHexString(), "#00ff00", "greenColor has right hex" );
|
||||
t.is( compare(Color.whiteColor(), Color.whiteColor()), 0, "default colors compare right" );
|
||||
t.ok( Color.whiteColor() == Color.whiteColor(), "default colors are interned" );
|
||||
t.ok( Color.whiteColor().toRGBString(), "rgb(255,255,255)", "toRGBString white" );
|
||||
t.ok( Color.blueColor().toRGBString(), "rgb(0,0,255)", "toRGBString blue" );
|
||||
t.is( Color.fromRGB(190/255, 222/255, 173/255).toHexString(), "#bedead", "fromRGB works" );
|
||||
t.is( Color.fromRGB(226/255, 15.9/255, 182/255).toHexString(), "#e210b6", "fromRGB < 16 works" );
|
||||
t.is( Color.fromRGB({r:190/255,g:222/255,b:173/255}).toHexString(), "#bedead", "alt fromRGB works" );
|
||||
t.is( Color.fromHexString("#bedead").toHexString(), "#bedead", "round-trip hex" );
|
||||
t.is( Color.fromString("#bedead").toHexString(), "#bedead", "round-trip string(hex)" );
|
||||
t.is( Color.fromRGBString("rgb(190,222,173)").toHexString(), "#bedead", "round-trip rgb" );
|
||||
t.is( Color.fromString("rgb(190,222,173)").toHexString(), "#bedead", "round-trip rgb" );
|
||||
|
||||
var hsl = Color.redColor().asHSL();
|
||||
approx( hsl.h, 0.0, "red hsl.h" );
|
||||
approx( hsl.s, 1.0, "red hsl.s" );
|
||||
approx( hsl.l, 0.5, "red hsl.l" );
|
||||
hsl = Color.fromRGB(0, 0, 0.5).asHSL();
|
||||
approx( hsl.h, 2/3, "darkblue hsl.h" );
|
||||
approx( hsl.s, 1.0, "darkblue hsl.s" );
|
||||
approx( hsl.l, 0.25, "darkblue hsl.l" );
|
||||
hsl = Color.fromString("#4169E1").asHSL();
|
||||
approx( hsl.h, (5/8), "4169e1 h");
|
||||
approx( hsl.s, (8/11), "4169e1 s");
|
||||
approx( hsl.l, (29/51), "4169e1 l");
|
||||
hsl = Color.fromString("#555544").asHSL();
|
||||
approx( hsl.h, (1/6), "555544 h" );
|
||||
approx( hsl.s, (1/9), "555544 s" );
|
||||
approx( hsl.l, (3/10), "555544 l" );
|
||||
hsl = Color.fromRGB(0.5, 1, 0.5).asHSL();
|
||||
approx( hsl.h, 1/3, "aqua hsl.h" );
|
||||
approx( hsl.s, 1.0, "aqua hsl.s" );
|
||||
approx( hsl.l, 0.75, "aqua hsl.l" );
|
||||
t.is(
|
||||
Color.fromHSL(hsl.h, hsl.s, hsl.l).toHexString(),
|
||||
Color.fromRGB(0.5, 1, 0.5).toHexString(),
|
||||
"fromHSL works with components"
|
||||
);
|
||||
t.is(
|
||||
Color.fromHSL(hsl).toHexString(),
|
||||
Color.fromRGB(0.5, 1, 0.5).toHexString(),
|
||||
"fromHSL alt form"
|
||||
);
|
||||
t.is(
|
||||
Color.fromString("hsl(120,100%,75%)").toHexString(),
|
||||
"#80ff80",
|
||||
"fromHSLString"
|
||||
);
|
||||
t.is(
|
||||
Color.fromRGB(0.5, 1, 0.5).toHSLString(),
|
||||
"hsl(120,100.0%,75.00%)",
|
||||
"toHSLString"
|
||||
);
|
||||
t.is( Color.fromHSL(0, 0, 0).toHexString(), "#000000", "fromHSL to black" );
|
||||
hsl = Color.blackColor().asHSL();
|
||||
approx( hsl.h, 0.0, "black hsl.h" );
|
||||
approx( hsl.s, 0.0, "black hsl.s" );
|
||||
approx( hsl.l, 0.0, "black hsl.l" );
|
||||
hsl.h = 1.0;
|
||||
hsl = Color.blackColor().asHSL();
|
||||
approx( hsl.h, 0.0, "asHSL returns copy" );
|
||||
var rgb = Color.brownColor().asRGB();
|
||||
approx( rgb.r, 153/255, "brown rgb.r" );
|
||||
approx( rgb.g, 102/255, "brown rgb.g" );
|
||||
approx( rgb.b, 51/255, "brown rgb.b" );
|
||||
rgb.r = 0;
|
||||
rgb = Color.brownColor().asRGB();
|
||||
approx( rgb.r, 153/255, "asRGB returns copy" );
|
||||
|
||||
t.is( Color.fromName("aqua").toHexString(), "#00ffff", "aqua fromName" );
|
||||
t.is( Color.fromString("aqua").toHexString(), "#00ffff", "aqua fromString" );
|
||||
t.is( Color.fromName("transparent"), Color.transparentColor(), "transparent fromName" );
|
||||
t.is( Color.fromString("transparent"), Color.transparentColor(), "transparent fromString" );
|
||||
t.is( Color.transparentColor().toRGBString(), "rgba(0,0,0,0)", "transparent toRGBString" );
|
||||
t.is( Color.fromRGBString("rgba( 0, 255, 255, 50%)").asRGB().a, 0.5, "rgba parsing alpha correctly" );
|
||||
t.is( Color.fromRGBString("rgba( 0, 255, 255, 50%)").toRGBString(), "rgba(0,255,255,0.5)", "rgba output correctly" );
|
||||
t.is( Color.fromRGBString("rgba( 0, 255, 255, 1)").toHexString(), "#00ffff", "fromRGBString with spaces and alpha" );
|
||||
t.is( Color.fromRGBString("rgb( 0, 255, 255)").toHexString(), "#00ffff", "fromRGBString with spaces" );
|
||||
t.is( Color.fromRGBString("rgb( 0, 100%, 255)").toHexString(), "#00ffff", "fromRGBString with percents" );
|
||||
|
||||
var hsv = Color.redColor().asHSV();
|
||||
approx( hsv.h, 0.0, "red hsv.h" );
|
||||
approx( hsv.s, 1.0, "red hsv.s" );
|
||||
approx( hsv.v, 1.0, "red hsv.v" );
|
||||
t.is( Color.fromHSV(hsv).toHexString(), Color.redColor().toHexString(), "red hexstring" );
|
||||
hsv = Color.fromRGB(0, 0, 0.5).asHSV();
|
||||
approx( hsv.h, 2/3, "darkblue hsv.h" );
|
||||
approx( hsv.s, 1.0, "darkblue hsv.s" );
|
||||
approx( hsv.v, 0.5, "darkblue hsv.v" );
|
||||
t.is( Color.fromHSV(hsv).toHexString(), Color.fromRGB(0, 0, 0.5).toHexString(), "darkblue hexstring" );
|
||||
hsv = Color.fromString("#4169E1").asHSV();
|
||||
approx( hsv.h, 5/8, "4169e1 h");
|
||||
approx( hsv.s, 32/45, "4169e1 s");
|
||||
approx( hsv.v, 15/17, "4169e1 l");
|
||||
t.is( Color.fromHSV(hsv).toHexString(), "#4169e1", "4169e1 hexstring" );
|
||||
hsv = Color.fromString("#555544").asHSV();
|
||||
approx( hsv.h, 1/6, "555544 h" );
|
||||
approx( hsv.s, 1/5, "555544 s" );
|
||||
approx( hsv.v, 1/3, "555544 l" );
|
||||
t.is( Color.fromHSV(hsv).toHexString(), "#555544", "555544 hexstring" );
|
||||
hsv = Color.fromRGB(0.5, 1, 0.5).asHSV();
|
||||
approx( hsv.h, 1/3, "aqua hsv.h" );
|
||||
approx( hsv.s, 0.5, "aqua hsv.s" );
|
||||
approx( hsv.v, 1, "aqua hsv.v" );
|
||||
t.is(
|
||||
Color.fromHSV(hsv.h, hsv.s, hsv.v).toHexString(),
|
||||
Color.fromRGB(0.5, 1, 0.5).toHexString(),
|
||||
"fromHSV works with components"
|
||||
);
|
||||
t.is(
|
||||
Color.fromHSV(hsv).toHexString(),
|
||||
Color.fromRGB(0.5, 1, 0.5).toHexString(),
|
||||
"fromHSV alt form"
|
||||
);
|
||||
};
|
|
@ -0,0 +1,45 @@
|
|||
if (typeof(dojo) != 'undefined') { dojo.require('MochiKit.DateTime'); }
|
||||
if (typeof(JSAN) != 'undefined') { JSAN.use('MochiKit.DateTime'); }
|
||||
if (typeof(tests) == 'undefined') { tests = {}; }
|
||||
|
||||
tests.test_DateTime = function (t) {
|
||||
var testDate = isoDate('2005-2-3');
|
||||
t.is(testDate.getFullYear(), 2005, "isoDate year ok");
|
||||
t.is(testDate.getDate(), 3, "isoDate day ok");
|
||||
t.is(testDate.getMonth(), 1, "isoDate month ok");
|
||||
t.ok(objEqual(testDate, new Date("February 3, 2005")), "matches string date");
|
||||
t.is(toISODate(testDate), '2005-02-03', 'toISODate ok');
|
||||
|
||||
var testDate = isoDate('2005-06-08');
|
||||
t.is(testDate.getFullYear(), 2005, "isoDate year ok");
|
||||
t.is(testDate.getDate(), 8, "isoDate day ok");
|
||||
t.is(testDate.getMonth(), 5, "isoDate month ok");
|
||||
t.ok(objEqual(testDate, new Date("June 8, 2005")), "matches string date");
|
||||
t.is(toISODate(testDate), '2005-06-08', 'toISODate ok');
|
||||
|
||||
t.is(compare(new Date("February 3, 2005"), new Date(2005, 1, 3)), 0, "dates compare eq");
|
||||
t.is(compare(new Date("February 3, 2005"), new Date(2005, 2, 3)), -1, "dates compare lt");
|
||||
t.is(compare(new Date("February 3, 2005"), new Date(2005, 0, 3)), 1, "dates compare gt");
|
||||
|
||||
var testDate = isoDate('2005-2-3');
|
||||
t.is(compare(americanDate('2/3/2005'), testDate), 0, "americanDate eq");
|
||||
t.is(compare('2/3/2005', toAmericanDate(testDate)), 0, "toAmericanDate eq");
|
||||
|
||||
var testTimestamp = isoTimestamp('2005-2-3 22:01:03');
|
||||
t.is(compare(testTimestamp, new Date(2005,1,3,22,1,3)), 0, "isoTimestamp eq");
|
||||
t.is(compare(testTimestamp, isoTimestamp('2005-2-3T22:01:03')), 0, "isoTimestamp (real ISO) eq");
|
||||
t.is(compare(toISOTimestamp(testTimestamp), '2005-02-03 22:01:03'), 0, "toISOTimestamp eq");
|
||||
testTimestamp = isoTimestamp('2005-2-3T22:01:03Z');
|
||||
t.is(toISOTimestamp(testTimestamp, true), '2005-02-03T22:01:03Z', "toISOTimestamp (real ISO) eq");
|
||||
|
||||
var localTZ = Math.round((new Date(2005,1,3,22,1,3)).getTimezoneOffset()/60)
|
||||
var direction = (localTZ < 0) ? "+" : "-";
|
||||
localTZ = Math.abs(localTZ);
|
||||
localTZ = direction + ((localTZ < 10) ? "0" : "") + localTZ;
|
||||
testTimestamp = isoTimestamp("2005-2-3T22:01:03" + localTZ);
|
||||
var testDateTimestamp = new Date(2005,1,3,22,1,3);
|
||||
t.is(compare(testTimestamp, testDateTimestamp), 0, "equal with local tz");
|
||||
testTimestamp = isoTimestamp("2005-2-3T17:01:03-05");
|
||||
var testDateTimestamp = new Date(Date.UTC(2005,1,3,22,1,3));
|
||||
t.is(compare(testTimestamp, testDateTimestamp), 0, "equal with specific tz");
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
if (typeof(dojo) != 'undefined') { dojo.require('MochiKit.Signal'); }
|
||||
if (typeof(JSAN) != 'undefined') { JSAN.use('MochiKit.Signal'); }
|
||||
if (typeof(tests) == 'undefined') { tests = {}; }
|
||||
|
||||
tests.test_DragAndDrop = function (t) {
|
||||
|
||||
var drag1 = new MochiKit.DragAndDrop.Draggable('drag1', {'revert': true, 'ghosting': true});
|
||||
|
||||
var drop1 = new MochiKit.DragAndDrop.Droppable('drop1', {'hoverclass': 'drop-hover'});
|
||||
drop1.activate();
|
||||
t.is(hasElementClass('drop1', 'drop-hover'), true, "hoverclass ok");
|
||||
drop1.deactivate();
|
||||
t.is(hasElementClass('drop1', 'drop-hover'), false, "remove hoverclass ok");
|
||||
drop1.destroy();
|
||||
|
||||
t.is( isEmpty(MochiKit.DragAndDrop.Droppables.drops), true, "Unregister droppable ok");
|
||||
|
||||
var onhover = function (element) {
|
||||
t.is(element, getElement('drag1'), 'onhover ok');
|
||||
};
|
||||
var drop2 = new MochiKit.DragAndDrop.Droppable('drop1', {'onhover': onhover});
|
||||
var pos = getElementPosition('drop1');
|
||||
pos = {"x": pos.x + 5, "y": pos.y + 5};
|
||||
MochiKit.DragAndDrop.Droppables.show({"page": pos}, getElement('drag1'));
|
||||
|
||||
drag1.destroy();
|
||||
t.is( isEmpty(MochiKit.DragAndDrop.Draggables.drops), true, "Unregister draggable ok");
|
||||
|
||||
};
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
if (typeof(dojo) != 'undefined') { dojo.require('MochiKit.Format'); }
|
||||
if (typeof(JSAN) != 'undefined') { JSAN.use('MochiKit.Format'); }
|
||||
if (typeof(tests) == 'undefined') { tests = {}; }
|
||||
|
||||
tests.test_Format = function (t) {
|
||||
t.is( truncToFixed(0.1234, 3), "0.123", "truncToFixed truncate" );
|
||||
t.is( truncToFixed(0.12, 3), "0.120", "truncToFixed trailing zeros" );
|
||||
t.is( truncToFixed(0.15, 1), "0.1", "truncToFixed no round" );
|
||||
t.is( truncToFixed(0.15, 0), "0", "truncToFixed zero (edge case)" );
|
||||
|
||||
t.is( roundToFixed(0.1234, 3), "0.123", "roundToFixed truncate" );
|
||||
t.is( roundToFixed(0.12, 3), "0.120", "roundToFixed trailing zeros" );
|
||||
t.is( roundToFixed(0.15, 1), "0.2", "roundToFixed round" );
|
||||
t.is( roundToFixed(0.15, 0), "0", "roundToFixed zero (edge case)" );
|
||||
|
||||
t.is( twoDigitFloat(-0.1234), "-0.12", "twoDigitFloat -0.1234 correct");
|
||||
t.is( twoDigitFloat(-0.1), "-0.1", "twoDigitFloat -0.1 correct");
|
||||
t.is( twoDigitFloat(-0), "0", "twoDigitFloat -0 correct");
|
||||
t.is( twoDigitFloat(0), "0", "twoDigitFloat 0 correct");
|
||||
t.is( twoDigitFloat(1), "1", "twoDigitFloat 1 correct");
|
||||
t.is( twoDigitFloat(1.0), "1", "twoDigitFloat 1.0 correct");
|
||||
t.is( twoDigitFloat(1.2), "1.2", "twoDigitFloat 1.2 correct");
|
||||
t.is( twoDigitFloat(1.234), "1.23", "twoDigitFloat 1.234 correct");
|
||||
t.is( percentFormat(123), "12300%", "percentFormat 123 correct");
|
||||
t.is( percentFormat(1.23), "123%", "percentFormat 123 correct");
|
||||
t.is( twoDigitAverage(1, 0), "0", "twoDigitAverage dbz correct");
|
||||
t.is( twoDigitAverage(1, 1), "1", "twoDigitAverage 1 correct");
|
||||
t.is( twoDigitAverage(1, 10), "0.1", "twoDigitAverage .1 correct");
|
||||
function reprIs(a, b) {
|
||||
arguments[0] = repr(a);
|
||||
arguments[1] = repr(b);
|
||||
t.is.apply(this, arguments);
|
||||
}
|
||||
reprIs( lstrip("\r\t\n foo \n\t\r"), "foo \n\t\r", "lstrip whitespace chars" );
|
||||
reprIs( rstrip("\r\t\n foo \n\t\r"), "\r\t\n foo", "rstrip whitespace chars" );
|
||||
reprIs( strip("\r\t\n foo \n\t\r"), "foo", "strip whitespace chars" );
|
||||
reprIs( lstrip("\r\n\t \r", "\r"), "\n\t \r", "lstrip custom chars" );
|
||||
reprIs( rstrip("\r\n\t \r", "\r"), "\r\n\t ", "rstrip custom chars" );
|
||||
reprIs( strip("\r\n\t \r", "\r"), "\n\t ", "strip custom chars" );
|
||||
|
||||
var nf = numberFormatter("$###,###.00 footer");
|
||||
t.is( nf(1000.1), "$1,000.10 footer", "trailing zeros" );
|
||||
t.is( nf(1000000.1), "$1,000,000.10 footer", "two seps" );
|
||||
t.is( nf(100), "$100.00 footer", "shorter than sep" );
|
||||
t.is( nf(100.555), "$100.56 footer", "rounding" );
|
||||
t.is( nf(-100.555), "$-100.56 footer", "default neg" );
|
||||
nf = numberFormatter("-$###,###.00");
|
||||
t.is( nf(-100.555), "-$100.56", "custom neg" );
|
||||
nf = numberFormatter("0000.0000");
|
||||
t.is( nf(0), "0000.0000", "leading and trailing" );
|
||||
t.is( nf(1.1), "0001.1000", "leading and trailing" );
|
||||
t.is( nf(12345.12345), "12345.1235", "no need for leading/trailing" );
|
||||
nf = numberFormatter("0000.0000");
|
||||
t.is( nf("taco"), "", "default placeholder" );
|
||||
nf = numberFormatter("###,###.00", "foo", "de_DE");
|
||||
t.is( nf("taco"), "foo", "custom placeholder" );
|
||||
t.is( nf(12345.12345), "12.345,12", "de_DE locale" );
|
||||
nf = numberFormatter("#%");
|
||||
t.is( nf(1), "100%", "trivial percent" );
|
||||
t.is( nf(0.55), "55%", "percent" );
|
||||
|
||||
var customLocale = {
|
||||
separator: " apples and ",
|
||||
decimal: " bagels at ",
|
||||
percent: "am for breakfast"};
|
||||
var customFormatter = numberFormatter("###,###.0%", "No breakfast", customLocale);
|
||||
t.is( customFormatter(23.458), "2 apples and 345 bagels at 8am for breakfast", "custom locale" );
|
||||
|
||||
nf = numberFormatter("###,###");
|
||||
t.is( nf(123), "123", "large number format" );
|
||||
t.is( nf(1234), "1,234", "large number format" );
|
||||
t.is( nf(12345), "12,345", "large number format" );
|
||||
t.is( nf(123456), "123,456", "large number format" );
|
||||
t.is( nf(1234567), "1,234,567", "large number format" );
|
||||
t.is( nf(12345678), "12,345,678", "large number format" );
|
||||
t.is( nf(123456789), "123,456,789", "large number format" );
|
||||
t.is( nf(1234567890), "1,234,567,890", "large number format" );
|
||||
t.is( nf(12345678901), "12,345,678,901", "large number format" );
|
||||
t.is( nf(123456789012), "123,456,789,012", "large number format" );
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue