Wao-commits
Threads by month
- ----- 2026 -----
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
April 2014
- 5 participants
- 162 discussions
r1887 - in trunk: . wao-services/src/main/java/fr/ifremer/wao/services/service wao-services/src/main/java/fr/ifremer/wao/services/service/administration wao-services/src/main/java/fr/ifremer/wao/services/service/mail wao-services/src/main/resources/email wao-services/src/test/java/fr/ifremer/wao/services wao-services/src/test/java/fr/ifremer/wao/services/service/mail
by tchemit@users.forge.codelutin.com 15 Apr '14
by tchemit@users.forge.codelutin.com 15 Apr '14
15 Apr '14
Author: tchemit
Date: 2014-04-15 11:08:27 +0200 (Tue, 15 Apr 2014)
New Revision: 1887
Url: http://forge.codelutin.com/projects/wao/repository/revisions/1887
Log:
refs #4560 : review email templates locale
Added:
trunk/wao-services/src/main/resources/email/UserCredentialsEmail_en.mustache
trunk/wao-services/src/main/resources/email/UserCredentialsEmail_fr.mustache
Removed:
trunk/wao-services/src/main/resources/email/UserCredentialsEmail_en_GB.mustache
trunk/wao-services/src/main/resources/email/UserCredentialsEmail_fr-FR.mustache
Modified:
trunk/pom.xml
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerContactsService.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/mail/EmailService.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/mail/MammalsObservationEmail.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/mail/UserCredentialsEmail.java
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/mail/WaoMail.java
trunk/wao-services/src/test/java/fr/ifremer/wao/services/FakeWaoServiceContext.java
trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/mail/EmailServiceTest.java
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2014-04-15 08:48:50 UTC (rev 1886)
+++ trunk/pom.xml 2014-04-15 09:08:27 UTC (rev 1887)
@@ -129,6 +129,11 @@
<!-- Using Java 7 -->
<javaVersion>1.7</javaVersion>
+ <!--TODO remove this when IDEA won't ask to change jdk level at each pom modification-->
+ <maven.compiler.source>${javaVersion}</maven.compiler.source>
+ <!--TODO remove this when IDEA won't ask to change jdk level at each pom modification-->
+ <maven.compiler.target>${javaVersion}</maven.compiler.target>
+
<signatureArtifactId>java17</signatureArtifactId>
<signatureVersion>1.0</signatureVersion>
<jsoupVersion>1.7.3</jsoupVersion>
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerContactsService.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerContactsService.java 2014-04-15 08:48:50 UTC (rev 1886)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerContactsService.java 2014-04-15 09:08:27 UTC (rev 1887)
@@ -712,7 +712,7 @@
if (updateContactCommand.isMammalsInfosChanged()) {
- MammalsObservationEmail email = new MammalsObservationEmail(getLocale());
+ MammalsObservationEmail email = getEmailService().newMammalsObservationEmail();
email.setContact(contact);
// now trying to find to what user we need to send the mail
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java 2014-04-15 08:48:50 UTC (rev 1886)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java 2014-04-15 09:08:27 UTC (rev 1887)
@@ -194,7 +194,7 @@
if (updateWaoUserCommand.getPasswordStrategy().isGeneratePassword()) {
UserCredentialsEmail userCredentialsEmail =
- new UserCredentialsEmail(getLocale());
+ getEmailService().newUserCredentialsEmail();
userCredentialsEmail.addTo(waoUser);
userCredentialsEmail.setWaoUser(waoUser);
userCredentialsEmail.setNewPassword(newPassword);
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/mail/EmailService.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/mail/EmailService.java 2014-04-15 08:48:50 UTC (rev 1886)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/mail/EmailService.java 2014-04-15 09:08:27 UTC (rev 1887)
@@ -41,17 +41,25 @@
private static final Log log = LogFactory.getLog(EmailService.class);
+ public UserCredentialsEmail newUserCredentialsEmail() {
+ return new UserCredentialsEmail(getLocale());
+ }
+
+ public MammalsObservationEmail newMammalsObservationEmail() {
+ return new MammalsObservationEmail(getLocale());
+ }
+
public void send(WaoMail mail) {
if (getApplicationConfig().isDevMode()) {
if (log.isInfoEnabled()) {
log.info("an email should have been sent if not in devMode: tos = " +
- mail.getTos() + ". subject = '" + mail.getSubject() + "'. body = \n" + getBody(mail));
+ mail.getTos() + ". subject = '" + mail.getSubject() + "'. body = \n" + getBody(mail));
}
if (log.isWarnEnabled()) {
- if ( ! mail.isRecipientProvided()) {
+ if (!mail.isRecipientProvided()) {
log.warn("email has no recipient, would not have been sent " + mail);
}
}
@@ -115,11 +123,11 @@
/** On utilise le modèle Mustache */
protected String getBody(WaoMail mail) {
- Locale locale = getLocale();
+ Locale locale = mail.getLocale();
MustacheFactory mustacheFactory = new DefaultMustacheFactory();
- String templateName = "email/" + mail.getClass().getSimpleName() + "_" + locale.toLanguageTag() + ".mustache";
+ String templateName = "email/" + mail.getClass().getSimpleName() + "_" + locale.getLanguage() + ".mustache";
Mustache mustache = mustacheFactory.compile(templateName);
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/mail/MammalsObservationEmail.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/mail/MammalsObservationEmail.java 2014-04-15 08:48:50 UTC (rev 1886)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/mail/MammalsObservationEmail.java 2014-04-15 09:08:27 UTC (rev 1887)
@@ -42,7 +42,7 @@
private Contact contact;
- public MammalsObservationEmail(Locale locale) {
+ protected MammalsObservationEmail(Locale locale) {
super(locale);
}
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/mail/UserCredentialsEmail.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/mail/UserCredentialsEmail.java 2014-04-15 08:48:50 UTC (rev 1886)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/mail/UserCredentialsEmail.java 2014-04-15 09:08:27 UTC (rev 1887)
@@ -32,7 +32,7 @@
protected String newPassword;
- public UserCredentialsEmail(Locale locale) {
+ protected UserCredentialsEmail(Locale locale) {
super(locale);
}
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/mail/WaoMail.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/mail/WaoMail.java 2014-04-15 08:48:50 UTC (rev 1886)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/mail/WaoMail.java 2014-04-15 09:08:27 UTC (rev 1887)
@@ -42,6 +42,10 @@
this.locale = locale;
}
+ public Locale getLocale() {
+ return locale;
+ }
+
public Set<String> getTos() {
if (tos == null) {
tos = Sets.newHashSet();
Copied: trunk/wao-services/src/main/resources/email/UserCredentialsEmail_en.mustache (from rev 1885, trunk/wao-services/src/main/resources/email/UserCredentialsEmail_en_GB.mustache)
===================================================================
--- trunk/wao-services/src/main/resources/email/UserCredentialsEmail_en.mustache (rev 0)
+++ trunk/wao-services/src/main/resources/email/UserCredentialsEmail_en.mustache 2014-04-15 09:08:27 UTC (rev 1887)
@@ -0,0 +1,14 @@
+Hi {{waoUser.fullName}},
+
+Your credentials have been updated.
+
+Your login: {{waoUser.login}}
+
+Your password: {{newPassword}}
+
+You can change your password online.
+
+Regards.
+
+--
+The WAO team.
Deleted: trunk/wao-services/src/main/resources/email/UserCredentialsEmail_en_GB.mustache
===================================================================
--- trunk/wao-services/src/main/resources/email/UserCredentialsEmail_en_GB.mustache 2014-04-15 08:48:50 UTC (rev 1886)
+++ trunk/wao-services/src/main/resources/email/UserCredentialsEmail_en_GB.mustache 2014-04-15 09:08:27 UTC (rev 1887)
@@ -1,14 +0,0 @@
-Hi {{waoUser.fullName}},
-
-Your credentials have been updated.
-
-Your login: {{waoUser.login}}
-
-Your password: {{newPassword}}
-
-You can change your password online.
-
-Regards.
-
---
-The WAO team.
Deleted: trunk/wao-services/src/main/resources/email/UserCredentialsEmail_fr-FR.mustache
===================================================================
--- trunk/wao-services/src/main/resources/email/UserCredentialsEmail_fr-FR.mustache 2014-04-15 08:48:50 UTC (rev 1886)
+++ trunk/wao-services/src/main/resources/email/UserCredentialsEmail_fr-FR.mustache 2014-04-15 09:08:27 UTC (rev 1887)
@@ -1,14 +0,0 @@
-Bonjour {{waoUser.firstName}},
-
-Vos identifiants de connexion ont changés.
-
-Votre identifiant : {{waoUser.login}}
-
-Votre mot de passe : {{newPassword}}
-
-Vous pouvez modifier ce mot de passe depuis le logiciel.
-
-Cordialement.
-
---
-L'équipe WAO.
Copied: trunk/wao-services/src/main/resources/email/UserCredentialsEmail_fr.mustache (from rev 1885, trunk/wao-services/src/main/resources/email/UserCredentialsEmail_fr-FR.mustache)
===================================================================
--- trunk/wao-services/src/main/resources/email/UserCredentialsEmail_fr.mustache (rev 0)
+++ trunk/wao-services/src/main/resources/email/UserCredentialsEmail_fr.mustache 2014-04-15 09:08:27 UTC (rev 1887)
@@ -0,0 +1,14 @@
+Bonjour {{waoUser.firstName}},
+
+Vos identifiants de connexion ont changés.
+
+Votre identifiant : {{waoUser.login}}
+
+Votre mot de passe : {{newPassword}}
+
+Vous pouvez modifier ce mot de passe depuis le logiciel.
+
+Cordialement.
+
+--
+L'équipe WAO.
Modified: trunk/wao-services/src/test/java/fr/ifremer/wao/services/FakeWaoServiceContext.java
===================================================================
--- trunk/wao-services/src/test/java/fr/ifremer/wao/services/FakeWaoServiceContext.java 2014-04-15 08:48:50 UTC (rev 1886)
+++ trunk/wao-services/src/test/java/fr/ifremer/wao/services/FakeWaoServiceContext.java 2014-04-15 09:08:27 UTC (rev 1887)
@@ -35,6 +35,11 @@
protected Date date;
+ public FakeWaoServiceContext() {
+ // default locale
+ setLocale(Locale.FRANCE);
+ }
+
@Override
public Random getRandom() {
if (random == null) {
@@ -56,9 +61,9 @@
this.date = date;
}
- @Override
- public Locale getLocale() {
- return Locale.FRANCE;
- }
+// @Override
+// public Locale getLocale() {
+// return Locale.FRANCE;
+// }
}
Modified: trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/mail/EmailServiceTest.java
===================================================================
--- trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/mail/EmailServiceTest.java 2014-04-15 08:48:50 UTC (rev 1886)
+++ trunk/wao-services/src/test/java/fr/ifremer/wao/services/service/mail/EmailServiceTest.java 2014-04-15 09:08:27 UTC (rev 1887)
@@ -43,15 +43,84 @@
@Test
public void testGetBody() {
- UserCredentialsEmail mail = new UserCredentialsEmail(Locale.FRANCE);
- mail.setWaoUser(new WaoUserImpl());
- mail.setNewPassword("taiste");
- String body = service.getBody(mail);
+ {
+ serviceContext.setLocale(Locale.FRANCE);
+ UserCredentialsEmail mail = service.newUserCredentialsEmail();
+ mail.setWaoUser(new WaoUserImpl());
+ mail.setNewPassword("taiste");
- Assert.assertTrue(body.contains("taiste"));
- Assert.assertTrue(body.contains("Bonjour"));
+ String body = service.getBody(mail);
+ Assert.assertTrue(body.contains("taiste"));
+ Assert.assertTrue(body.contains("Bonjour"));
+
+ }
+
+ {
+ serviceContext.setLocale(Locale.FRENCH);
+ UserCredentialsEmail mail = service.newUserCredentialsEmail();
+ mail.setWaoUser(new WaoUserImpl());
+ mail.setNewPassword("taiste");
+
+ String body = service.getBody(mail);
+
+ Assert.assertTrue(body.contains("taiste"));
+ Assert.assertTrue(body.contains("Bonjour"));
+
+ }
+
+ {
+ serviceContext.setLocale(Locale.CANADA_FRENCH);
+ UserCredentialsEmail mail = service.newUserCredentialsEmail();
+ mail.setWaoUser(new WaoUserImpl());
+ mail.setNewPassword("taiste");
+
+ String body = service.getBody(mail);
+
+ Assert.assertTrue(body.contains("taiste"));
+ Assert.assertTrue(body.contains("Bonjour"));
+
+ }
+
+ {
+ serviceContext.setLocale(Locale.ENGLISH);
+ UserCredentialsEmail mail = service.newUserCredentialsEmail();
+ mail.setWaoUser(new WaoUserImpl());
+ mail.setNewPassword("taiste");
+
+ String body = service.getBody(mail);
+
+ Assert.assertTrue(body.contains("taiste"));
+ Assert.assertTrue(body.contains("Hi"));
+
+ }
+
+ {
+ serviceContext.setLocale(Locale.US);
+ UserCredentialsEmail mail = service.newUserCredentialsEmail();
+ mail.setWaoUser(new WaoUserImpl());
+ mail.setNewPassword("taiste");
+
+ String body = service.getBody(mail);
+
+ Assert.assertTrue(body.contains("taiste"));
+ Assert.assertTrue(body.contains("Hi"));
+
+ }
+
+ {
+ serviceContext.setLocale(Locale.CANADA);
+ UserCredentialsEmail mail = service.newUserCredentialsEmail();
+ mail.setWaoUser(new WaoUserImpl());
+ mail.setNewPassword("taiste");
+
+ String body = service.getBody(mail);
+
+ Assert.assertTrue(body.contains("taiste"));
+ Assert.assertTrue(body.contains("Hi"));
+
+ }
}
}
1
0
r1886 - in trunk: . wao-web wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer
by sbavencoff@users.forge.codelutin.com 15 Apr '14
by sbavencoff@users.forge.codelutin.com 15 Apr '14
15 Apr '14
Author: sbavencoff
Date: 2014-04-15 10:48:50 +0200 (Tue, 15 Apr 2014)
New Revision: 1886
Url: http://forge.codelutin.com/projects/wao/repository/revisions/1886
Log:
refs #4553 : add clean html
Modified:
trunk/pom.xml
trunk/wao-web/pom.xml
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/EditNewsAction.java
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2014-04-15 08:01:22 UTC (rev 1885)
+++ trunk/pom.xml 2014-04-15 08:48:50 UTC (rev 1886)
@@ -131,6 +131,7 @@
<javaVersion>1.7</javaVersion>
<signatureArtifactId>java17</signatureArtifactId>
<signatureVersion>1.0</signatureVersion>
+ <jsoupVersion>1.7.3</jsoupVersion>
</properties>
<repositories>
@@ -347,6 +348,12 @@
</exclusions>
</dependency>
+ <dependency>
+ <groupId>org.jsoup</groupId>
+ <artifactId>jsoup</artifactId>
+ <version>${jsoupVersion}</version>
+ </dependency>
+
</dependencies>
</dependencyManagement>
Modified: trunk/wao-web/pom.xml
===================================================================
--- trunk/wao-web/pom.xml 2014-04-15 08:01:22 UTC (rev 1885)
+++ trunk/wao-web/pom.xml 2014-04-15 08:48:50 UTC (rev 1886)
@@ -158,6 +158,11 @@
<artifactId>postgresql</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.jsoup</groupId>
+ <artifactId>jsoup</artifactId>
+ </dependency>
+
</dependencies>
<build>
Modified: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/EditNewsAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/EditNewsAction.java 2014-04-15 08:01:22 UTC (rev 1885)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/EditNewsAction.java 2014-04-15 08:48:50 UTC (rev 1886)
@@ -28,6 +28,8 @@
import org.apache.commons.lang3.StringUtils;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
+import org.jsoup.Jsoup;
+import org.jsoup.safety.Whitelist;
@Results({
@Result(name="success", type="redirectAction", params = { "actionName", "news" })
@@ -36,6 +38,8 @@
private static final long serialVersionUID = 1L;
+ protected static final Whitelist WHITE_LIST = Whitelist.basicWithImages().addTags("h1", "h2", "h3");
+
protected transient NewsService service;
protected String newsId;
@@ -66,6 +70,10 @@
@Override
public String execute() {
+ //filtrage de news
+ String clean = Jsoup.clean(news.getContent(), WHITE_LIST);
+ news.setContent(clean);
+
service.save(news);
addActionMessage(t("wao.ui.action.createNews.success"));
1
0
r1885 - in trunk/wao-web/src/main: resources/i18n webapp webapp/WEB-INF/content/obsmer webapp/bootstrap-wysihtml5-0.0.2 webapp/wysihtml5-0.3.0
by sbavencoff@users.forge.codelutin.com 15 Apr '14
by sbavencoff@users.forge.codelutin.com 15 Apr '14
15 Apr '14
Author: sbavencoff
Date: 2014-04-15 10:01:22 +0200 (Tue, 15 Apr 2014)
New Revision: 1885
Url: http://forge.codelutin.com/projects/wao/repository/revisions/1885
Log:
refs #4553 : change editor wysiwyg
Added:
trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/
trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.css
trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.fr-FR.js
trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.js
trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.min.js
trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/wysiwyg-color.css
trunk/wao-web/src/main/webapp/wysihtml5-0.3.0/
trunk/wao-web/src/main/webapp/wysihtml5-0.3.0/wysihtml5.js
trunk/wao-web/src/main/webapp/wysihtml5-0.3.0/wysihtml5.min.js
Removed:
trunk/wao-web/src/main/webapp/js/
Modified:
trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties
trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties
trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/edit-news-input.jsp
Modified: trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties
===================================================================
--- trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties 2014-04-14 09:57:45 UTC (rev 1884)
+++ trunk/wao-web/src/main/resources/i18n/wao-web_en_GB.properties 2014-04-15 08:01:22 UTC (rev 1885)
@@ -538,3 +538,4 @@
wao.ui.userMustAcceptCgu=You must accept the terms of use
wao.ui.validLogin=Valid identifier
wao.ui.validation.validation.contacts.failure.not.valid=Could not validate contact\: %s
+wao.ui.wysihtml5.lang=en
Modified: trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties
===================================================================
--- trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties 2014-04-14 09:57:45 UTC (rev 1884)
+++ trunk/wao-web/src/main/resources/i18n/wao-web_fr_FR.properties 2014-04-15 08:01:22 UTC (rev 1885)
@@ -537,3 +537,4 @@
wao.ui.userList=Liste des utilisateurs
wao.ui.userMustAcceptCgu=Vous devez accepter les conditions d'utilisation pour utiliser Wao
wao.ui.validLogin=Identifiant valide
+wao.ui.wysihtml5.lang=fr-FR
Modified: trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/edit-news-input.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/edit-news-input.jsp 2014-04-14 09:57:45 UTC (rev 1884)
+++ trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/edit-news-input.jsp 2014-04-15 08:01:22 UTC (rev 1885)
@@ -23,43 +23,21 @@
<html>
<head>
- <link href="http://netdna.bootstrapcdn.com/font-awesome/3.0.2/css/font-awesome.css" rel="stylesheet">
- <script type="text/javascript" src="<s:url value='/js/jquery.hotkeys/jquery.hotkeys.js' />"></script>
- <script type="text/javascript" src="<s:url value='/js/bootstrap-wysiwyg/bootstrap-wysiwyg.js' />"></script>
+ <link rel="stylesheet" type="text/css" href="<s:url value='/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.css' />" />
+ <script src="<s:url value='/wysihtml5-0.3.0/wysihtml5.js'/>"></script>
+ <script src="<s:url value='/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.js' />"></script>
+ <s:if test="!getText('wao.ui.wysihtml5.lang').equals('en')">
+ <script src="<s:url value='/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.%{getText("wao.ui.wysihtml5.lang")}.js' />"></script>
+ </s:if>
<script>
-
-
- function initToolbarBootstrapBindings() {
- var fonts = ['Serif', 'Sans', 'Arial', 'Arial Black', 'Courier',
- 'Courier New', 'Comic Sans MS', 'Helvetica', 'Impact', 'Lucida Grande', 'Lucida Sans', 'Tahoma', 'Times',
- 'Times New Roman', 'Verdana'],
- fontTarget = $('#fontMenu').siblings('.dropdown-menu');
- $.each(fonts, function (idx, fontName) {
- fontTarget.append($('<li><a data-edit="fontName ' + fontName +'" style="font-family:\''+ fontName +'\'">'+fontName + '</a></li>'));
- });
-
- $('a[title]').tooltip({container:'body'});
- $('.dropdown-menu input').click(function() {return false;})
- .change(function () {$(this).parent('.dropdown-menu').siblings('.dropdown-toggle').dropdown('toggle');})
- .keydown('esc', function () {this.value='';$(this).change();});
-
- $('[data-role=magic-overlay]').each(function () {
- var overlay = $(this), target = $(overlay.data('target'));
- overlay.css('opacity', 0).css('position', 'absolute').offset(target.offset()).width(target.outerWidth()).height(target.outerHeight());
- });
- };
-
$(document).ready(function () {
- initToolbarBootstrapBindings();
- $("#editor").wysiwyg();
- });
-
- function save () {
- var content = $('#editor').cleanHtml()
- $('#edit-news_news_content').val(content);
- }
-
+ $("#editor").wysihtml5({
+ locale: "<s:property value="%{getText('wao.ui.wysihtml5.lang')}"/>",
+ stylesheets: ["<s:url value='/bootstrap-wysihtml5-0.0.2/wysiwyg-color.css' />"]
+ });
+ });
</script>
+
</head>
<s:form onsubmit="save();">
@@ -68,108 +46,11 @@
<s:textfield name="news.title" label="%{getText('wao.ui.news.title')}" cssClass="input-xxlarge" />
- <div class="control-group ">
- <label class="control-label" for="editor"><s:property value="%{getText('wao.ui.news.content')}" /></label>
+ <s:textarea label="%{getText('wao.ui.news.content')}"
+ name="news.content"
+ id="editor"
+ rows="8"/>
- <div class="btn-toolbar" data-role="editor-toolbar" data-target="#editor">
- <div class="btn-group">
- <a class="btn dropdown-toggle"
- data-toggle="dropdown"
- data-original-title="Font"
- id="fontMenu"
- title="Polices"><i class="icon-font"></i><b class="caret"></b></a>
- <ul class="dropdown-menu">
- </ul>
- </div>
-
- <div class="btn-group">
- <a class="btn dropdown-toggle"
- data-toggle="dropdown"
- data-original-title="Font Size"
- title="Tailles"><i class="icon-text-height"></i> <b class="caret"></b></a>
- <ul class="dropdown-menu">
- <li><a data-edit="fontSize 5"><font size="5">Grand</font></a></li>
- <li><a data-edit="fontSize 3"><font size="3">Normal</font></a></li>
- <li><a data-edit="fontSize 1"><font size="1">Petit</font></a></li>
- </ul>
- </div>
-
- <div class="btn-group">
- <a class="btn dropdown-toggle"
- data-toggle="dropdown"
- data-original-title="Color"
- title="Couleur"><i class="icon-tint"></i> <b class="caret"></b></a>
- <ul class="dropdown-menu">
- <li>
- <a data-edit="foreColor #000000"> <font color="#000000" >Noir</font></a>
- </li>
- <li>
- <a data-edit="foreColor #c0c0c0"> <font color="#c0c0c0" >Gris</font></a>
- </li>
- <li>
- <a data-edit="foreColor #ff0000"> <font color="#ff0000" >Rouge</font></a>
- </li>
- <li>
- <a data-edit="foreColor #00ff00"> <font color="#00ff00" >Vert</font></a>
- </li>
- <li>
- <a data-edit="foreColor #0000ff"> <font color="#0000ff" >Bleu</font></a>
- </li>
- <li>
- <a data-edit="foreColor #ffff00"> <font color="#ffff00" >Jaune</font></a>
- </li>
- <li>
- <a data-edit="foreColor #00ffff"> <font color="#00ffff" >Cyan </font></a>
- </li>
- <li>
- <a data-edit="foreColor #ff00ff"> <font color="#ff00ff" >Magenta</font></a>
- </li>
- <li>
- </ul>
- </div>
-
- <div class="btn-group">
- <a class="btn" data-edit="bold" title="Gras (Ctrl + B)"><i class="icon-bold"></i></a>
- <a class="btn" data-edit="italic" title="Italic (Ctrl + I)"><i class="icon-italic"></i></a>
- <a class="btn" data-edit="strikethrough" title="Barré"><i class="icon-strikethrough"></i></a>
- <a class="btn" data-edit="underline" title="sous-ligné (Ctrl + U)"><i class="icon-underline"></i></a>
- </div>
- <div class="btn-group">
- <a class="btn" data-edit="insertunorderedlist" title="Puces"><i class="icon-list-ul"></i></a>
- <a class="btn" data-edit="insertorderedlist" title="Numérotation"><i class="icon-list-ol"></i></a>
- <a class="btn" data-edit="outdent" title="Réduire de retrait (Maj + Tab)"><i class="icon-indent-left"></i></a>
- <a class="btn" data-edit="indent" title="Augmenter le retrait (Tab)"><i class="icon-indent-right"></i></a>
- </div>
- <div class="btn-group">
- <a class="btn" data-edit="justifyleft" title="Aligner à gauche (Ctrl + L)"><i class="icon-align-left"></i></a>
- <a class="btn" data-edit="justifycenter" title="Centré (Ctrl + E)"><i class="icon-align-center"></i></a>
- <a class="btn" data-edit="justifyright" title="Aligner à droite (Ctrl/Cmd+R)"><i class="icon-align-right"></i></a>
- <a class="btn" data-edit="justifyfull" title="Justifié (Ctrl + J)"><i class="icon-align-justify"></i></a>
- </div>
- <div class="btn-group">
- <a class="btn dropdown-toggle" data-toggle="dropdown" title="Hyperlien"><i class="icon-link"></i></a>
- <div class="dropdown-menu input-append">
- <input class="span2" placeholder="URL" type="text" data-edit="createLink"/>
- <button class="btn" type="button">Add</button>
- </div>
- <a class="btn" data-edit="unlink" title="Supprimer l'hyperlien"><i class="icon-cut"></i></a>
- </div>
-
- <div class="btn-group">
- <a class="btn" data-edit="undo" title="Annuler (Ctrl + Z)"><i class="icon-undo"></i></a>
- <a class="btn" data-edit="redo" title="Rétablir (Ctrl + Y)"><i class="icon-repeat"></i></a>
- </div>
- </div>
-
- <div class="controls">
- <div id="editor">
- <s:property value="news.content" escapeHtml="false"/>
- </div>
- </div>
- </div>
-
- <s:hidden name="news.content"/>
-
<div class="form-actions">
<s:url action="news" id="newsUrl" />
<s:a href="%{newsUrl}" cssClass="btn">
Added: trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.css
===================================================================
--- trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.css (rev 0)
+++ trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.css 2014-04-15 08:01:22 UTC (rev 1885)
@@ -0,0 +1,44 @@
+ul.wysihtml5-toolbar {
+ margin: 0;
+ padding: 0;
+ display: block;
+}
+
+ul.wysihtml5-toolbar::after {
+ clear: both;
+ display: table;
+ content: "";
+}
+
+ul.wysihtml5-toolbar > li {
+ float: left;
+ display: list-item;
+ list-style: none;
+ margin: 0 5px 10px 0;
+}
+
+ul.wysihtml5-toolbar a[data-wysihtml5-command=bold] {
+ font-weight: bold;
+}
+
+ul.wysihtml5-toolbar a[data-wysihtml5-command=italic] {
+ font-style: italic;
+}
+
+ul.wysihtml5-toolbar a[data-wysihtml5-command=underline] {
+ text-decoration: underline;
+}
+
+ul.wysihtml5-toolbar a.btn.wysihtml5-command-active {
+ background-image: none;
+ -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);
+ background-color: #E6E6E6;
+ background-color: #D9D9D9 9;
+ outline: 0;
+}
+
+ul.wysihtml5-commands-disabled .dropdown-menu {
+ display: none !important;
+}
Added: trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.fr-FR.js
===================================================================
--- trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.fr-FR.js (rev 0)
+++ trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.fr-FR.js 2014-04-15 08:01:22 UTC (rev 1885)
@@ -0,0 +1,49 @@
+/**
+ * French translation for bootstrap-wysihtml5
+ */
+(function($){
+ $.fn.wysihtml5.locale["fr-FR"] = {
+ font_styles: {
+ normal: "Texte normal",
+ h1: "Titre 1",
+ h2: "Titre 2",
+ h3: "Titre 3"
+ },
+ emphasis: {
+ bold: "Gras",
+ italic: "Italique",
+ underline: "Souligné"
+ },
+ lists: {
+ unordered: "Liste à puces",
+ ordered: "Liste numérotée",
+ outdent: "Diminuer le retrait",
+ indent: "Augmenter le retrait",
+ indered: "Augmenter le retrait"
+ },
+ link: {
+ insert: "Insérer un lien",
+ cancel: "Annuler"
+ },
+ image: {
+ insert: "Insérer une image",
+ cancel: "Annuler"
+ },
+ html: {
+ edit: "Editer en HTML"
+ },
+ colours: {
+ black: "Noir",
+ silver: "Gris clair",
+ gray: "Gris",
+ maroon: "Marron",
+ red: "Rouge",
+ purple: "Pourpre",
+ green: "Vert",
+ olive: "Olive",
+ navy: "Bleu marine",
+ blue: "Bleu",
+ orange: "Orange"
+ }
+ };
+}(jQuery));
\ No newline at end of file
Added: trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.js
===================================================================
--- trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.js (rev 0)
+++ trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.js 2014-04-15 08:01:22 UTC (rev 1885)
@@ -0,0 +1,458 @@
+!function($, wysi) {
+ "use strict";
+
+ var templates = function(key, locale) {
+
+ var tpl = {
+ "font-styles":
+ "<li class='dropdown'>" +
+ "<a class='btn dropdown-toggle' data-toggle='dropdown' href='#'>" +
+ "<i class='icon-font'></i> <span class='current-font'>" + locale.font_styles.normal + "</span> <b class='caret'></b>" +
+ "</a>" +
+ "<ul class='dropdown-menu'>" +
+ "<li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='div'>" + locale.font_styles.normal + "</a></li>" +
+ "<li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='h1'>" + locale.font_styles.h1 + "</a></li>" +
+ "<li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='h2'>" + locale.font_styles.h2 + "</a></li>" +
+ "<li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='h3'>" + locale.font_styles.h3 + "</a></li>" +
+ "</ul>" +
+ "</li>",
+
+ "emphasis":
+ "<li>" +
+ "<div class='btn-group'>" +
+ "<a class='btn' data-wysihtml5-command='bold' title='CTRL+B'>" + locale.emphasis.bold + "</a>" +
+ "<a class='btn' data-wysihtml5-command='italic' title='CTRL+I'>" + locale.emphasis.italic + "</a>" +
+ "<a class='btn' data-wysihtml5-command='underline' title='CTRL+U'>" + locale.emphasis.underline + "</a>" +
+ "</div>" +
+ "</li>",
+
+ "lists":
+ "<li>" +
+ "<div class='btn-group'>" +
+ "<a class='btn' data-wysihtml5-command='insertUnorderedList' title='" + locale.lists.unordered + "'><i class='icon-list'></i></a>" +
+ "<a class='btn' data-wysihtml5-command='insertOrderedList' title='" + locale.lists.ordered + "'><i class='icon-th-list'></i></a>" +
+ "<a class='btn' data-wysihtml5-command='Outdent' title='" + locale.lists.outdent + "'><i class='icon-indent-right'></i></a>" +
+ "<a class='btn' data-wysihtml5-command='Indent' title='" + locale.lists.indent + "'><i class='icon-indent-left'></i></a>" +
+ "</div>" +
+ "</li>",
+
+ "link":
+ "<li>" +
+ "<div class='bootstrap-wysihtml5-insert-link-modal modal hide fade'>" +
+ "<div class='modal-header'>" +
+ "<a class='close' data-dismiss='modal'>×</a>" +
+ "<h3>" + locale.link.insert + "</h3>" +
+ "</div>" +
+ "<div class='modal-body'>" +
+ "<input value='http://' class='bootstrap-wysihtml5-insert-link-url input-xlarge'>" +
+ "</div>" +
+ "<div class='modal-footer'>" +
+ "<a href='#' class='btn' data-dismiss='modal'>" + locale.link.cancel + "</a>" +
+ "<a href='#' class='btn btn-primary' data-dismiss='modal'>" + locale.link.insert + "</a>" +
+ "</div>" +
+ "</div>" +
+ "<a class='btn' data-wysihtml5-command='createLink' title='" + locale.link.insert + "'><i class='icon-share'></i></a>" +
+ "</li>",
+
+ "image":
+ "<li>" +
+ "<div class='bootstrap-wysihtml5-insert-image-modal modal hide fade'>" +
+ "<div class='modal-header'>" +
+ "<a class='close' data-dismiss='modal'>×</a>" +
+ "<h3>" + locale.image.insert + "</h3>" +
+ "</div>" +
+ "<div class='modal-body'>" +
+ "<input value='http://' class='bootstrap-wysihtml5-insert-image-url input-xlarge'>" +
+ "</div>" +
+ "<div class='modal-footer'>" +
+ "<a href='#' class='btn' data-dismiss='modal'>" + locale.image.cancel + "</a>" +
+ "<a href='#' class='btn btn-primary' data-dismiss='modal'>" + locale.image.insert + "</a>" +
+ "</div>" +
+ "</div>" +
+ "<a class='btn' data-wysihtml5-command='insertImage' title='" + locale.image.insert + "'><i class='icon-picture'></i></a>" +
+ "</li>",
+
+ "html":
+ "<li>" +
+ "<div class='btn-group'>" +
+ "<a class='btn' data-wysihtml5-action='change_view' title='" + locale.html.edit + "'><i class='icon-pencil'></i></a>" +
+ "</div>" +
+ "</li>",
+
+ "color":
+ "<li class='dropdown'>" +
+ "<a class='btn dropdown-toggle' data-toggle='dropdown' href='#'>" +
+ "<span class='current-color'>" + locale.colours.black + "</span> <b class='caret'></b>" +
+ "</a>" +
+ "<ul class='dropdown-menu'>" +
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='black'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='black'>" + locale.colours.black + "</a></li>" +
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='silver'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='silver'>" + locale.colours.silver + "</a></li>" +
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='gray'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='gray'>" + locale.colours.gray + "</a></li>" +
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='maroon'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='maroon'>" + locale.colours.maroon + "</a></li>" +
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='red'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='red'>" + locale.colours.red + "</a></li>" +
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='purple'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='purple'>" + locale.colours.purple + "</a></li>" +
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='green'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='green'>" + locale.colours.green + "</a></li>" +
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='olive'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='olive'>" + locale.colours.olive + "</a></li>" +
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='navy'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='navy'>" + locale.colours.navy + "</a></li>" +
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='blue'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='blue'>" + locale.colours.blue + "</a></li>" +
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='orange'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='orange'>" + locale.colours.orange + "</a></li>" +
+ "</ul>" +
+ "</li>"
+ };
+ return tpl[key];
+ };
+
+
+ var Wysihtml5 = function(el, options) {
+ this.el = el;
+ this.toolbar = this.createToolbar(el, options || defaultOptions);
+ this.editor = this.createEditor(options);
+
+ window.editor = this.editor;
+
+ $('iframe.wysihtml5-sandbox').each(function(i, el){
+ $(el.contentWindow).off('focus.wysihtml5').on({
+ 'focus.wysihtml5' : function(){
+ $('li.dropdown').removeClass('open');
+ }
+ });
+ });
+ };
+
+ Wysihtml5.prototype = {
+
+ constructor: Wysihtml5,
+
+ createEditor: function(options) {
+ options = options || {};
+ options.toolbar = this.toolbar[0];
+
+ var editor = new wysi.Editor(this.el[0], options);
+
+ if(options && options.events) {
+ for(var eventName in options.events) {
+ editor.on(eventName, options.events[eventName]);
+ }
+ }
+ return editor;
+ },
+
+ createToolbar: function(el, options) {
+ var self = this;
+ var toolbar = $("<ul/>", {
+ 'class' : "wysihtml5-toolbar",
+ 'style': "display:none"
+ });
+ var culture = options.locale || defaultOptions.locale || "en";
+ for(var key in defaultOptions) {
+ var value = false;
+
+ if(options[key] !== undefined) {
+ if(options[key] === true) {
+ value = true;
+ }
+ } else {
+ value = defaultOptions[key];
+ }
+
+ if(value === true) {
+ toolbar.append(templates(key, locale[culture]));
+
+ if(key === "html") {
+ this.initHtml(toolbar);
+ }
+
+ if(key === "link") {
+ this.initInsertLink(toolbar);
+ }
+
+ if(key === "image") {
+ this.initInsertImage(toolbar);
+ }
+ }
+ }
+
+ if(options.toolbar) {
+ for(key in options.toolbar) {
+ toolbar.append(options.toolbar[key]);
+ }
+ }
+
+ toolbar.find("a[data-wysihtml5-command='formatBlock']").click(function(e) {
+ var target = e.target || e.srcElement;
+ var el = $(target);
+ self.toolbar.find('.current-font').text(el.html());
+ });
+
+ toolbar.find("a[data-wysihtml5-command='foreColor']").click(function(e) {
+ var target = e.target || e.srcElement;
+ var el = $(target);
+ self.toolbar.find('.current-color').text(el.html());
+ });
+
+ this.el.before(toolbar);
+
+ return toolbar;
+ },
+
+ initHtml: function(toolbar) {
+ var changeViewSelector = "a[data-wysihtml5-action='change_view']";
+ toolbar.find(changeViewSelector).click(function(e) {
+ toolbar.find('a.btn').not(changeViewSelector).toggleClass('disabled');
+ });
+ },
+
+ initInsertImage: function(toolbar) {
+ var self = this;
+ var insertImageModal = toolbar.find('.bootstrap-wysihtml5-insert-image-modal');
+ var urlInput = insertImageModal.find('.bootstrap-wysihtml5-insert-image-url');
+ var insertButton = insertImageModal.find('a.btn-primary');
+ var initialValue = urlInput.val();
+
+ var insertImage = function() {
+ var url = urlInput.val();
+ urlInput.val(initialValue);
+ self.editor.composer.commands.exec("insertImage", url);
+ };
+
+ urlInput.keypress(function(e) {
+ if(e.which == 13) {
+ insertImage();
+ insertImageModal.modal('hide');
+ }
+ });
+
+ insertButton.click(insertImage);
+
+ insertImageModal.on('shown', function() {
+ urlInput.focus();
+ });
+
+ insertImageModal.on('hide', function() {
+ self.editor.currentView.element.focus();
+ });
+
+ toolbar.find('a[data-wysihtml5-command=insertImage]').click(function() {
+ var activeButton = $(this).hasClass("wysihtml5-command-active");
+
+ if (!activeButton) {
+ insertImageModal.modal('show');
+ insertImageModal.on('click.dismiss.modal', '[data-dismiss="modal"]', function(e) {
+ e.stopPropagation();
+ });
+ return false;
+ }
+ else {
+ return true;
+ }
+ });
+ },
+
+ initInsertLink: function(toolbar) {
+ var self = this;
+ var insertLinkModal = toolbar.find('.bootstrap-wysihtml5-insert-link-modal');
+ var urlInput = insertLinkModal.find('.bootstrap-wysihtml5-insert-link-url');
+ var insertButton = insertLinkModal.find('a.btn-primary');
+ var initialValue = urlInput.val();
+
+ var insertLink = function() {
+ var url = urlInput.val();
+ urlInput.val(initialValue);
+ self.editor.composer.commands.exec("createLink", {
+ href: url,
+ target: "_blank",
+ rel: "nofollow"
+ });
+ };
+ var pressedEnter = false;
+
+ urlInput.keypress(function(e) {
+ if(e.which == 13) {
+ insertLink();
+ insertLinkModal.modal('hide');
+ }
+ });
+
+ insertButton.click(insertLink);
+
+ insertLinkModal.on('shown', function() {
+ urlInput.focus();
+ });
+
+ insertLinkModal.on('hide', function() {
+ self.editor.currentView.element.focus();
+ });
+
+ toolbar.find('a[data-wysihtml5-command=createLink]').click(function() {
+ var activeButton = $(this).hasClass("wysihtml5-command-active");
+
+ if (!activeButton) {
+ insertLinkModal.appendTo('body').modal('show');
+ insertLinkModal.on('click.dismiss.modal', '[data-dismiss="modal"]', function(e) {
+ e.stopPropagation();
+ });
+ return false;
+ }
+ else {
+ return true;
+ }
+ });
+ }
+ };
+
+ // these define our public api
+ var methods = {
+ resetDefaults: function() {
+ $.fn.wysihtml5.defaultOptions = $.extend(true, {}, $.fn.wysihtml5.defaultOptionsCache);
+ },
+ bypassDefaults: function(options) {
+ return this.each(function () {
+ var $this = $(this);
+ $this.data('wysihtml5', new Wysihtml5($this, options));
+ });
+ },
+ shallowExtend: function (options) {
+ var settings = $.extend({}, $.fn.wysihtml5.defaultOptions, options || {});
+ var that = this;
+ return methods.bypassDefaults.apply(that, [settings]);
+ },
+ deepExtend: function(options) {
+ var settings = $.extend(true, {}, $.fn.wysihtml5.defaultOptions, options || {});
+ var that = this;
+ return methods.bypassDefaults.apply(that, [settings]);
+ },
+ init: function(options) {
+ var that = this;
+ return methods.shallowExtend.apply(that, [options]);
+ }
+ };
+
+ $.fn.wysihtml5 = function ( method ) {
+ if ( methods[method] ) {
+ return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
+ } else if ( typeof method === 'object' || ! method ) {
+ return methods.init.apply( this, arguments );
+ } else {
+ $.error( 'Method ' + method + ' does not exist on jQuery.wysihtml5' );
+ }
+ };
+
+ $.fn.wysihtml5.Constructor = Wysihtml5;
+
+ var defaultOptions = $.fn.wysihtml5.defaultOptions = {
+ "font-styles": true,
+ "color": false,
+ "emphasis": true,
+ "lists": true,
+ "html": false,
+ "link": true,
+ "image": true,
+ events: {},
+ parserRules: {
+ classes: {
+ // (path_to_project/lib/css/wysiwyg-color.css)
+ "wysiwyg-color-silver" : 1,
+ "wysiwyg-color-gray" : 1,
+ "wysiwyg-color-white" : 1,
+ "wysiwyg-color-maroon" : 1,
+ "wysiwyg-color-red" : 1,
+ "wysiwyg-color-purple" : 1,
+ "wysiwyg-color-fuchsia" : 1,
+ "wysiwyg-color-green" : 1,
+ "wysiwyg-color-lime" : 1,
+ "wysiwyg-color-olive" : 1,
+ "wysiwyg-color-yellow" : 1,
+ "wysiwyg-color-navy" : 1,
+ "wysiwyg-color-blue" : 1,
+ "wysiwyg-color-teal" : 1,
+ "wysiwyg-color-aqua" : 1,
+ "wysiwyg-color-orange" : 1,
+ },
+ tags: {
+ "b": {},
+ "i": {},
+ "br": {},
+ "ol": {},
+ "ul": {},
+ "li": {},
+ "h1": {},
+ "h2": {},
+ "h3": {},
+ "blockquote": {},
+ "u": 1,
+ "img": {
+ "check_attributes": {
+ "width": "numbers",
+ "alt": "alt",
+ "src": "url",
+ "height": "numbers"
+ }
+ },
+ "a": {
+ set_attributes: {
+ target: "_blank",
+ rel: "nofollow"
+ },
+ check_attributes: {
+ href: "url" // important to avoid XSS
+ }
+ },
+ "span": 1,
+ "div": 1
+ }
+ },
+ stylesheets: ["./lib/css/wysiwyg-color.css"], // (path_to_project/lib/css/wysiwyg-color.css)
+ locale: "en"
+ };
+
+ if (typeof $.fn.wysihtml5.defaultOptionsCache === 'undefined') {
+ $.fn.wysihtml5.defaultOptionsCache = $.extend(true, {}, $.fn.wysihtml5.defaultOptions);
+ }
+
+ var locale = $.fn.wysihtml5.locale = {
+ en: {
+ font_styles: {
+ normal: "Normal text",
+ h1: "Heading 1",
+ h2: "Heading 2",
+ h3: "Heading 3"
+ },
+ emphasis: {
+ bold: "Bold",
+ italic: "Italic",
+ underline: "Underline"
+ },
+ lists: {
+ unordered: "Unordered list",
+ ordered: "Ordered list",
+ outdent: "Outdent",
+ indent: "Indent"
+ },
+ link: {
+ insert: "Insert link",
+ cancel: "Cancel"
+ },
+ image: {
+ insert: "Insert image",
+ cancel: "Cancel"
+ },
+ html: {
+ edit: "Edit HTML"
+ },
+ colours: {
+ black: "Black",
+ silver: "Silver",
+ gray: "Grey",
+ maroon: "Maroon",
+ red: "Red",
+ purple: "Purple",
+ green: "Green",
+ olive: "Olive",
+ navy: "Navy",
+ blue: "Blue",
+ orange: "Orange"
+ }
+ }
+ };
+
+}(window.jQuery, window.wysihtml5);
Added: trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.min.js
===================================================================
--- trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.min.js (rev 0)
+++ trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5.min.js 2014-04-15 08:01:22 UTC (rev 1885)
@@ -0,0 +1 @@
+!function(a,b){"use strict";var c={"font-styles":"<li class='dropdown'><a class='btn dropdown-toggle' data-toggle='dropdown' href='#'><i class='icon-font'></i> <span class='current-font'>Normal text</span> <b class='caret'></b></a><ul class='dropdown-menu'><li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='div'>Normal text</a></li><li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='h1'>Heading 1</a></li><li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='h2'>Heading 2</a></li></ul></li>",emphasis:"<li><div class='btn-group'><a class='btn' data-wysihtml5-command='bold' title='CTRL+B'>Bold</a><a class='btn' data-wysihtml5-command='italic' title='CTRL+I'>Italic</a></div></li>",lists:"<li><div class='btn-group'><a class='btn' data-wysihtml5-command='insertUnorderedList' title='Unordered List'><i class='icon-list'></i></a><a class='btn' data-wysihtml5-command='insertOrderedList' title='Ordered List'><i class='icon-th-list'></i></a><a class='btn' data-wysihtml5-command='Outdent' title='Outdent'><i class='icon-indent-right'></i></a><a class='btn' data-wysihtml5-command='Indent' title='Indent'><i class='icon-indent-left'></i></a></div></li>",link:"<li><div class='bootstrap-wysihtml5-insert-link-modal modal hide fade'><div class='modal-header'><a class='close' data-dismiss='modal'>×</a><h3>Insert Link</h3></div><div class='modal-body'><input value='http://' class='bootstrap-wysihtml5-insert-link-url input-xlarge'></div><div class='modal-footer'><a href='#' class='btn' data-dismiss='modal'>Cancel</a><a href='#' class='btn btn-primary' data-dismiss='modal'>Insert link</a></div></div><a class='btn' data-wysihtml5-command='createLink' title='Link'><i class='icon-share'></i></a></li>",image:"<li><div class='bootstrap-wysihtml5-insert-image-modal modal hide fade'><div class='modal-header'><a class='close' data-dismiss='modal'>×</a><h3>Insert Image</h3></div><div class='modal-body'><input value='http://' class='bootstrap-wysihtml5-insert-image-url input-xlarge'></div><div class='modal-footer'><a href='#' class='btn' data-dismiss='modal'>Cancel</a><a href='#' class='btn btn-primary' data-dismiss='modal'>Insert image</a></div></div><a class='btn' data-wysihtml5-command='insertImage' title='Insert image'><i class='icon-picture'></i></a></li>",html:"<li><div class='btn-group'><a class='btn' data-wysihtml5-action='change_view' title='Edit HTML'><i class='icon-pencil'></i></a></div></li>"},d={"font-styles":!0,emphasis:!0,lists:!0,html:!1,link:!0,image:!0,events:{},parserRules:{tags:{b:{},i:{},br:{},ol:{},ul:{},li:{},h1:{},h2:{},u:1,img:{check_attributes:{width:"numbers",alt:"alt",src:"url",height:"numbers"}},a:{set_attributes:{target:"_blank",rel:"nofollow"},check_attributes:{href:"url"}}}}},e=function(b,c){this.el=b,this.toolbar=this.createToolbar(b,c||d),this.editor=this.createEditor(c),window.editor=this.editor,a("iframe.wysihtml5-sandbox").each(function(b,c){a(c.contentWindow).off("focus.wysihtml5").on({"focus.wysihtml5":function(){a("li.dropdown").removeClass("open")}})})};e.prototype={constructor:e,createEditor:function(a){var c=d.parserRules;a&&a.parserRules&&(c=a.parserRules);var e=new b.Editor(this.el.attr("id"),{toolbar:this.toolbar.attr("id"),parserRules:c});if(a&&a.events)for(var f in a.events)e.on(f,a.events[f]);return e},createToolbar:function(b,e){var f=this,g=a("<ul/>",{id:b.attr("id")+"-wysihtml5-toolbar","class":"wysihtml5-toolbar",style:"display:none"});for(var h in d){var i=!1;e[h]!=undefined?e[h]==1&&(i=!0):i=d[h],i==1&&(g.append(c[h]),h=="html"&&this.initHtml(g),h=="link"&&this.initInsertLink(g),h=="image"&&this.initInsertImage(g))}var f=this;return g.find("a[data-wysihtml5-command='formatBlock']").click(function(b){var c=a(b.srcElement);f.toolbar.find(".current-font").text(c.html())}),this.el.before(g),g},initHtml:function(a){var b="a[data-wysihtml5-action='change_view']";a.find(b).click(function(c){a.find("a.btn").not(b).toggleClass("disabled")})},initInsertImage:function(a){var b=this,c=a.find(".bootstrap-wysihtml5-insert-image-modal"),d=c.find(".bootstrap-wysihtml5-insert-image-url"),e=c.find("a.btn-primary"),f=d.val(),g=function(){var a=d.val();d.val(f),b.editor.composer.commands.exec("insertImage",a)};d.keypress(function(a){a.which==13&&(g(),c.modal("hide"))}),e.click(g),c.on("shown",function(){d.focus()}),c.on("hide",function(){b.editor.currentView.element.focus()}),a.find("a[data-wysihtml5-command=insertImage]").click(function(){c.modal("show")})},initInsertLink:function(a){var b=this,c=a.find(".bootstrap-wysihtml5-insert-link-modal"),d=c.find(".bootstrap-wysihtml5-insert-link-url"),e=c.find("a.btn-primary"),f=d.val(),g=function(){var a=d.val();d.val(f),b.editor.composer.commands.exec("createLink",{href:a,target:"_blank",rel:"nofollow"})},h=!1;d.keypress(function(a){a.which==13&&(g(),c.modal("hide"))}),e.click(g),c.on("shown",function(){d.focus()}),c.on("hide",function(){b.editor.currentView.element.focus()}),a.find("a[data-wysihtml5-command=createLink]").click(function(){c.modal("show")})}},a.fn.wysihtml5=function(b){return this.each(function(){var c=a(this);c.data("wysihtml5",new e(c,b))})},a.fn.wysihtml5.Constructor=e}(window.jQuery,window.wysihtml5);
\ No newline at end of file
Added: trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/wysiwyg-color.css
===================================================================
--- trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/wysiwyg-color.css (rev 0)
+++ trunk/wao-web/src/main/webapp/bootstrap-wysihtml5-0.0.2/wysiwyg-color.css 2014-04-15 08:01:22 UTC (rev 1885)
@@ -0,0 +1,67 @@
+.wysiwyg-color-black {
+ color: black;
+}
+
+.wysiwyg-color-silver {
+ color: silver;
+}
+
+.wysiwyg-color-gray {
+ color: gray;
+}
+
+.wysiwyg-color-white {
+ color: white;
+}
+
+.wysiwyg-color-maroon {
+ color: maroon;
+}
+
+.wysiwyg-color-red {
+ color: red;
+}
+
+.wysiwyg-color-purple {
+ color: purple;
+}
+
+.wysiwyg-color-fuchsia {
+ color: fuchsia;
+}
+
+.wysiwyg-color-green {
+ color: green;
+}
+
+.wysiwyg-color-lime {
+ color: lime;
+}
+
+.wysiwyg-color-olive {
+ color: olive;
+}
+
+.wysiwyg-color-yellow {
+ color: yellow;
+}
+
+.wysiwyg-color-navy {
+ color: navy;
+}
+
+.wysiwyg-color-blue {
+ color: blue;
+}
+
+.wysiwyg-color-teal {
+ color: teal;
+}
+
+.wysiwyg-color-aqua {
+ color: aqua;
+}
+
+.wysiwyg-color-orange {
+ color: orange;
+}
\ No newline at end of file
Added: trunk/wao-web/src/main/webapp/wysihtml5-0.3.0/wysihtml5.js
===================================================================
--- trunk/wao-web/src/main/webapp/wysihtml5-0.3.0/wysihtml5.js (rev 0)
+++ trunk/wao-web/src/main/webapp/wysihtml5-0.3.0/wysihtml5.js 2014-04-15 08:01:22 UTC (rev 1885)
@@ -0,0 +1,9521 @@
+/**
+ * @license wysihtml5 v0.3.0
+ * https://github.com/xing/wysihtml5
+ *
+ * Author: Christopher Blum (https://github.com/tiff)
+ *
+ * Copyright (C) 2012 XING AG
+ * Licensed under the MIT license (MIT)
+ *
+ */
+var wysihtml5 = {
+ version: "0.3.0",
+
+ // namespaces
+ commands: {},
+ dom: {},
+ quirks: {},
+ toolbar: {},
+ lang: {},
+ selection: {},
+ views: {},
+
+ INVISIBLE_SPACE: "\uFEFF",
+
+ EMPTY_FUNCTION: function() {},
+
+ ELEMENT_NODE: 1,
+ TEXT_NODE: 3,
+
+ BACKSPACE_KEY: 8,
+ ENTER_KEY: 13,
+ ESCAPE_KEY: 27,
+ SPACE_KEY: 32,
+ DELETE_KEY: 46
+};/**
+ * @license Rangy, a cross-browser JavaScript range and selection library
+ * http://code.google.com/p/rangy/
+ *
+ * Copyright 2011, Tim Down
+ * Licensed under the MIT license.
+ * Version: 1.2.2
+ * Build date: 13 November 2011
+ */
+window['rangy'] = (function() {
+
+
+ var OBJECT = "object", FUNCTION = "function", UNDEFINED = "undefined";
+
+ var domRangeProperties = ["startContainer", "startOffset", "endContainer", "endOffset", "collapsed",
+ "commonAncestorContainer", "START_TO_START", "START_TO_END", "END_TO_START", "END_TO_END"];
+
+ var domRangeMethods = ["setStart", "setStartBefore", "setStartAfter", "setEnd", "setEndBefore",
+ "setEndAfter", "collapse", "selectNode", "selectNodeContents", "compareBoundaryPoints", "deleteContents",
+ "extractContents", "cloneContents", "insertNode", "surroundContents", "cloneRange", "toString", "detach"];
+
+ var textRangeProperties = ["boundingHeight", "boundingLeft", "boundingTop", "boundingWidth", "htmlText", "text"];
+
+ // Subset of TextRange's full set of methods that we're interested in
+ var textRangeMethods = ["collapse", "compareEndPoints", "duplicate", "getBookmark", "moveToBookmark",
+ "moveToElementText", "parentElement", "pasteHTML", "select", "setEndPoint", "getBoundingClientRect"];
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // Trio of functions taken from Peter Michaux's article:
+ // http://peter.michaux.ca/articles/feature-detection-state-of-the-art-browser…
+ function isHostMethod(o, p) {
+ var t = typeof o[p];
+ return t == FUNCTION || (!!(t == OBJECT && o[p])) || t == "unknown";
+ }
+
+ function isHostObject(o, p) {
+ return !!(typeof o[p] == OBJECT && o[p]);
+ }
+
+ function isHostProperty(o, p) {
+ return typeof o[p] != UNDEFINED;
+ }
+
+ // Creates a convenience function to save verbose repeated calls to tests functions
+ function createMultiplePropertyTest(testFunc) {
+ return function(o, props) {
+ var i = props.length;
+ while (i--) {
+ if (!testFunc(o, props[i])) {
+ return false;
+ }
+ }
+ return true;
+ };
+ }
+
+ // Next trio of functions are a convenience to save verbose repeated calls to previous two functions
+ var areHostMethods = createMultiplePropertyTest(isHostMethod);
+ var areHostObjects = createMultiplePropertyTest(isHostObject);
+ var areHostProperties = createMultiplePropertyTest(isHostProperty);
+
+ function isTextRange(range) {
+ return range && areHostMethods(range, textRangeMethods) && areHostProperties(range, textRangeProperties);
+ }
+
+ var api = {
+ version: "1.2.2",
+ initialized: false,
+ supported: true,
+
+ util: {
+ isHostMethod: isHostMethod,
+ isHostObject: isHostObject,
+ isHostProperty: isHostProperty,
+ areHostMethods: areHostMethods,
+ areHostObjects: areHostObjects,
+ areHostProperties: areHostProperties,
+ isTextRange: isTextRange
+ },
+
+ features: {},
+
+ modules: {},
+ config: {
+ alertOnWarn: false,
+ preferTextRange: false
+ }
+ };
+
+ function fail(reason) {
+ window.alert("Rangy not supported in your browser. Reason: " + reason);
+ api.initialized = true;
+ api.supported = false;
+ }
+
+ api.fail = fail;
+
+ function warn(msg) {
+ var warningMessage = "Rangy warning: " + msg;
+ if (api.config.alertOnWarn) {
+ window.alert(warningMessage);
+ } else if (typeof window.console != UNDEFINED && typeof window.console.log != UNDEFINED) {
+ window.console.log(warningMessage);
+ }
+ }
+
+ api.warn = warn;
+
+ if ({}.hasOwnProperty) {
+ api.util.extend = function(o, props) {
+ for (var i in props) {
+ if (props.hasOwnProperty(i)) {
+ o[i] = props[i];
+ }
+ }
+ };
+ } else {
+ fail("hasOwnProperty not supported");
+ }
+
+ var initListeners = [];
+ var moduleInitializers = [];
+
+ // Initialization
+ function init() {
+ if (api.initialized) {
+ return;
+ }
+ var testRange;
+ var implementsDomRange = false, implementsTextRange = false;
+
+ // First, perform basic feature tests
+
+ if (isHostMethod(document, "createRange")) {
+ testRange = document.createRange();
+ if (areHostMethods(testRange, domRangeMethods) && areHostProperties(testRange, domRangeProperties)) {
+ implementsDomRange = true;
+ }
+ testRange.detach();
+ }
+
+ var body = isHostObject(document, "body") ? document.body : document.getElementsByTagName("body")[0];
+
+ if (body && isHostMethod(body, "createTextRange")) {
+ testRange = body.createTextRange();
+ if (isTextRange(testRange)) {
+ implementsTextRange = true;
+ }
+ }
+
+ if (!implementsDomRange && !implementsTextRange) {
+ fail("Neither Range nor TextRange are implemented");
+ }
+
+ api.initialized = true;
+ api.features = {
+ implementsDomRange: implementsDomRange,
+ implementsTextRange: implementsTextRange
+ };
+
+ // Initialize modules and call init listeners
+ var allListeners = moduleInitializers.concat(initListeners);
+ for (var i = 0, len = allListeners.length; i < len; ++i) {
+ try {
+ allListeners[i](api);
+ } catch (ex) {
+ if (isHostObject(window, "console") && isHostMethod(window.console, "log")) {
+ window.console.log("Init listener threw an exception. Continuing.", ex);
+ }
+
+ }
+ }
+ }
+
+ // Allow external scripts to initialize this library in case it's loaded after the document has loaded
+ api.init = init;
+
+ // Execute listener immediately if already initialized
+ api.addInitListener = function(listener) {
+ if (api.initialized) {
+ listener(api);
+ } else {
+ initListeners.push(listener);
+ }
+ };
+
+ var createMissingNativeApiListeners = [];
+
+ api.addCreateMissingNativeApiListener = function(listener) {
+ createMissingNativeApiListeners.push(listener);
+ };
+
+ function createMissingNativeApi(win) {
+ win = win || window;
+ init();
+
+ // Notify listeners
+ for (var i = 0, len = createMissingNativeApiListeners.length; i < len; ++i) {
+ createMissingNativeApiListeners[i](win);
+ }
+ }
+
+ api.createMissingNativeApi = createMissingNativeApi;
+
+ /**
+ * @constructor
+ */
+ function Module(name) {
+ this.name = name;
+ this.initialized = false;
+ this.supported = false;
+ }
+
+ Module.prototype.fail = function(reason) {
+ this.initialized = true;
+ this.supported = false;
+
+ throw new Error("Module '" + this.name + "' failed to load: " + reason);
+ };
+
+ Module.prototype.warn = function(msg) {
+ api.warn("Module " + this.name + ": " + msg);
+ };
+
+ Module.prototype.createError = function(msg) {
+ return new Error("Error in Rangy " + this.name + " module: " + msg);
+ };
+
+ api.createModule = function(name, initFunc) {
+ var module = new Module(name);
+ api.modules[name] = module;
+
+ moduleInitializers.push(function(api) {
+ initFunc(api, module);
+ module.initialized = true;
+ module.supported = true;
+ });
+ };
+
+ api.requireModules = function(modules) {
+ for (var i = 0, len = modules.length, module, moduleName; i < len; ++i) {
+ moduleName = modules[i];
+ module = api.modules[moduleName];
+ if (!module || !(module instanceof Module)) {
+ throw new Error("Module '" + moduleName + "' not found");
+ }
+ if (!module.supported) {
+ throw new Error("Module '" + moduleName + "' not supported");
+ }
+ }
+ };
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // Wait for document to load before running tests
+
+ var docReady = false;
+
+ var loadHandler = function(e) {
+
+ if (!docReady) {
+ docReady = true;
+ if (!api.initialized) {
+ init();
+ }
+ }
+ };
+
+ // Test whether we have window and document objects that we will need
+ if (typeof window == UNDEFINED) {
+ fail("No window found");
+ return;
+ }
+ if (typeof document == UNDEFINED) {
+ fail("No document found");
+ return;
+ }
+
+ if (isHostMethod(document, "addEventListener")) {
+ document.addEventListener("DOMContentLoaded", loadHandler, false);
+ }
+
+ // Add a fallback in case the DOMContentLoaded event isn't supported
+ if (isHostMethod(window, "addEventListener")) {
+ window.addEventListener("load", loadHandler, false);
+ } else if (isHostMethod(window, "attachEvent")) {
+ window.attachEvent("onload", loadHandler);
+ } else {
+ fail("Window does not have required addEventListener or attachEvent method");
+ }
+
+ return api;
+})();
+rangy.createModule("DomUtil", function(api, module) {
+
+ var UNDEF = "undefined";
+ var util = api.util;
+
+ // Perform feature tests
+ if (!util.areHostMethods(document, ["createDocumentFragment", "createElement", "createTextNode"])) {
+ module.fail("document missing a Node creation method");
+ }
+
+ if (!util.isHostMethod(document, "getElementsByTagName")) {
+ module.fail("document missing getElementsByTagName method");
+ }
+
+ var el = document.createElement("div");
+ if (!util.areHostMethods(el, ["insertBefore", "appendChild", "cloneNode"] ||
+ !util.areHostObjects(el, ["previousSibling", "nextSibling", "childNodes", "parentNode"]))) {
+ module.fail("Incomplete Element implementation");
+ }
+
+ // innerHTML is required for Range's createContextualFragment method
+ if (!util.isHostProperty(el, "innerHTML")) {
+ module.fail("Element is missing innerHTML property");
+ }
+
+ var textNode = document.createTextNode("test");
+ if (!util.areHostMethods(textNode, ["splitText", "deleteData", "insertData", "appendData", "cloneNode"] ||
+ !util.areHostObjects(el, ["previousSibling", "nextSibling", "childNodes", "parentNode"]) ||
+ !util.areHostProperties(textNode, ["data"]))) {
+ module.fail("Incomplete Text Node implementation");
+ }
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // Removed use of indexOf because of a bizarre bug in Opera that is thrown in one of the Acid3 tests. I haven't been
+ // able to replicate it outside of the test. The bug is that indexOf returns -1 when called on an Array that
+ // contains just the document as a single element and the value searched for is the document.
+ var arrayContains = /*Array.prototype.indexOf ?
+ function(arr, val) {
+ return arr.indexOf(val) > -1;
+ }:*/
+
+ function(arr, val) {
+ var i = arr.length;
+ while (i--) {
+ if (arr[i] === val) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ // Opera 11 puts HTML elements in the null namespace, it seems, and IE 7 has undefined namespaceURI
+ function isHtmlNamespace(node) {
+ var ns;
+ return typeof node.namespaceURI == UNDEF || ((ns = node.namespaceURI) === null || ns == "http://www.w3.org/1999/xhtml");
+ }
+
+ function parentElement(node) {
+ var parent = node.parentNode;
+ return (parent.nodeType == 1) ? parent : null;
+ }
+
+ function getNodeIndex(node) {
+ var i = 0;
+ while( (node = node.previousSibling) ) {
+ i++;
+ }
+ return i;
+ }
+
+ function getNodeLength(node) {
+ var childNodes;
+ return isCharacterDataNode(node) ? node.length : ((childNodes = node.childNodes) ? childNodes.length : 0);
+ }
+
+ function getCommonAncestor(node1, node2) {
+ var ancestors = [], n;
+ for (n = node1; n; n = n.parentNode) {
+ ancestors.push(n);
+ }
+
+ for (n = node2; n; n = n.parentNode) {
+ if (arrayContains(ancestors, n)) {
+ return n;
+ }
+ }
+
+ return null;
+ }
+
+ function isAncestorOf(ancestor, descendant, selfIsAncestor) {
+ var n = selfIsAncestor ? descendant : descendant.parentNode;
+ while (n) {
+ if (n === ancestor) {
+ return true;
+ } else {
+ n = n.parentNode;
+ }
+ }
+ return false;
+ }
+
+ function getClosestAncestorIn(node, ancestor, selfIsAncestor) {
+ var p, n = selfIsAncestor ? node : node.parentNode;
+ while (n) {
+ p = n.parentNode;
+ if (p === ancestor) {
+ return n;
+ }
+ n = p;
+ }
+ return null;
+ }
+
+ function isCharacterDataNode(node) {
+ var t = node.nodeType;
+ return t == 3 || t == 4 || t == 8 ; // Text, CDataSection or Comment
+ }
+
+ function insertAfter(node, precedingNode) {
+ var nextNode = precedingNode.nextSibling, parent = precedingNode.parentNode;
+ if (nextNode) {
+ parent.insertBefore(node, nextNode);
+ } else {
+ parent.appendChild(node);
+ }
+ return node;
+ }
+
+ // Note that we cannot use splitText() because it is bugridden in IE 9.
+ function splitDataNode(node, index) {
+ var newNode = node.cloneNode(false);
+ newNode.deleteData(0, index);
+ node.deleteData(index, node.length - index);
+ insertAfter(newNode, node);
+ return newNode;
+ }
+
+ function getDocument(node) {
+ if (node.nodeType == 9) {
+ return node;
+ } else if (typeof node.ownerDocument != UNDEF) {
+ return node.ownerDocument;
+ } else if (typeof node.document != UNDEF) {
+ return node.document;
+ } else if (node.parentNode) {
+ return getDocument(node.parentNode);
+ } else {
+ throw new Error("getDocument: no document found for node");
+ }
+ }
+
+ function getWindow(node) {
+ var doc = getDocument(node);
+ if (typeof doc.defaultView != UNDEF) {
+ return doc.defaultView;
+ } else if (typeof doc.parentWindow != UNDEF) {
+ return doc.parentWindow;
+ } else {
+ throw new Error("Cannot get a window object for node");
+ }
+ }
+
+ function getIframeDocument(iframeEl) {
+ if (typeof iframeEl.contentDocument != UNDEF) {
+ return iframeEl.contentDocument;
+ } else if (typeof iframeEl.contentWindow != UNDEF) {
+ return iframeEl.contentWindow.document;
+ } else {
+ throw new Error("getIframeWindow: No Document object found for iframe element");
+ }
+ }
+
+ function getIframeWindow(iframeEl) {
+ if (typeof iframeEl.contentWindow != UNDEF) {
+ return iframeEl.contentWindow;
+ } else if (typeof iframeEl.contentDocument != UNDEF) {
+ return iframeEl.contentDocument.defaultView;
+ } else {
+ throw new Error("getIframeWindow: No Window object found for iframe element");
+ }
+ }
+
+ function getBody(doc) {
+ return util.isHostObject(doc, "body") ? doc.body : doc.getElementsByTagName("body")[0];
+ }
+
+ function getRootContainer(node) {
+ var parent;
+ while ( (parent = node.parentNode) ) {
+ node = parent;
+ }
+ return node;
+ }
+
+ function comparePoints(nodeA, offsetA, nodeB, offsetB) {
+ // See http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-…
+ var nodeC, root, childA, childB, n;
+ if (nodeA == nodeB) {
+
+ // Case 1: nodes are the same
+ return offsetA === offsetB ? 0 : (offsetA < offsetB) ? -1 : 1;
+ } else if ( (nodeC = getClosestAncestorIn(nodeB, nodeA, true)) ) {
+
+ // Case 2: node C (container B or an ancestor) is a child node of A
+ return offsetA <= getNodeIndex(nodeC) ? -1 : 1;
+ } else if ( (nodeC = getClosestAncestorIn(nodeA, nodeB, true)) ) {
+
+ // Case 3: node C (container A or an ancestor) is a child node of B
+ return getNodeIndex(nodeC) < offsetB ? -1 : 1;
+ } else {
+
+ // Case 4: containers are siblings or descendants of siblings
+ root = getCommonAncestor(nodeA, nodeB);
+ childA = (nodeA === root) ? root : getClosestAncestorIn(nodeA, root, true);
+ childB = (nodeB === root) ? root : getClosestAncestorIn(nodeB, root, true);
+
+ if (childA === childB) {
+ // This shouldn't be possible
+
+ throw new Error("comparePoints got to case 4 and childA and childB are the same!");
+ } else {
+ n = root.firstChild;
+ while (n) {
+ if (n === childA) {
+ return -1;
+ } else if (n === childB) {
+ return 1;
+ }
+ n = n.nextSibling;
+ }
+ throw new Error("Should not be here!");
+ }
+ }
+ }
+
+ function fragmentFromNodeChildren(node) {
+ var fragment = getDocument(node).createDocumentFragment(), child;
+ while ( (child = node.firstChild) ) {
+ fragment.appendChild(child);
+ }
+ return fragment;
+ }
+
+ function inspectNode(node) {
+ if (!node) {
+ return "[No node]";
+ }
+ if (isCharacterDataNode(node)) {
+ return '"' + node.data + '"';
+ } else if (node.nodeType == 1) {
+ var idAttr = node.id ? ' id="' + node.id + '"' : "";
+ return "<" + node.nodeName + idAttr + ">[" + node.childNodes.length + "]";
+ } else {
+ return node.nodeName;
+ }
+ }
+
+ /**
+ * @constructor
+ */
+ function NodeIterator(root) {
+ this.root = root;
+ this._next = root;
+ }
+
+ NodeIterator.prototype = {
+ _current: null,
+
+ hasNext: function() {
+ return !!this._next;
+ },
+
+ next: function() {
+ var n = this._current = this._next;
+ var child, next;
+ if (this._current) {
+ child = n.firstChild;
+ if (child) {
+ this._next = child;
+ } else {
+ next = null;
+ while ((n !== this.root) && !(next = n.nextSibling)) {
+ n = n.parentNode;
+ }
+ this._next = next;
+ }
+ }
+ return this._current;
+ },
+
+ detach: function() {
+ this._current = this._next = this.root = null;
+ }
+ };
+
+ function createIterator(root) {
+ return new NodeIterator(root);
+ }
+
+ /**
+ * @constructor
+ */
+ function DomPosition(node, offset) {
+ this.node = node;
+ this.offset = offset;
+ }
+
+ DomPosition.prototype = {
+ equals: function(pos) {
+ return this.node === pos.node & this.offset == pos.offset;
+ },
+
+ inspect: function() {
+ return "[DomPosition(" + inspectNode(this.node) + ":" + this.offset + ")]";
+ }
+ };
+
+ /**
+ * @constructor
+ */
+ function DOMException(codeName) {
+ this.code = this[codeName];
+ this.codeName = codeName;
+ this.message = "DOMException: " + this.codeName;
+ }
+
+ DOMException.prototype = {
+ INDEX_SIZE_ERR: 1,
+ HIERARCHY_REQUEST_ERR: 3,
+ WRONG_DOCUMENT_ERR: 4,
+ NO_MODIFICATION_ALLOWED_ERR: 7,
+ NOT_FOUND_ERR: 8,
+ NOT_SUPPORTED_ERR: 9,
+ INVALID_STATE_ERR: 11
+ };
+
+ DOMException.prototype.toString = function() {
+ return this.message;
+ };
+
+ api.dom = {
+ arrayContains: arrayContains,
+ isHtmlNamespace: isHtmlNamespace,
+ parentElement: parentElement,
+ getNodeIndex: getNodeIndex,
+ getNodeLength: getNodeLength,
+ getCommonAncestor: getCommonAncestor,
+ isAncestorOf: isAncestorOf,
+ getClosestAncestorIn: getClosestAncestorIn,
+ isCharacterDataNode: isCharacterDataNode,
+ insertAfter: insertAfter,
+ splitDataNode: splitDataNode,
+ getDocument: getDocument,
+ getWindow: getWindow,
+ getIframeWindow: getIframeWindow,
+ getIframeDocument: getIframeDocument,
+ getBody: getBody,
+ getRootContainer: getRootContainer,
+ comparePoints: comparePoints,
+ inspectNode: inspectNode,
+ fragmentFromNodeChildren: fragmentFromNodeChildren,
+ createIterator: createIterator,
+ DomPosition: DomPosition
+ };
+
+ api.DOMException = DOMException;
+});rangy.createModule("DomRange", function(api, module) {
+ api.requireModules( ["DomUtil"] );
+
+
+ var dom = api.dom;
+ var DomPosition = dom.DomPosition;
+ var DOMException = api.DOMException;
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // Utility functions
+
+ function isNonTextPartiallySelected(node, range) {
+ return (node.nodeType != 3) &&
+ (dom.isAncestorOf(node, range.startContainer, true) || dom.isAncestorOf(node, range.endContainer, true));
+ }
+
+ function getRangeDocument(range) {
+ return dom.getDocument(range.startContainer);
+ }
+
+ function dispatchEvent(range, type, args) {
+ var listeners = range._listeners[type];
+ if (listeners) {
+ for (var i = 0, len = listeners.length; i < len; ++i) {
+ listeners[i].call(range, {target: range, args: args});
+ }
+ }
+ }
+
+ function getBoundaryBeforeNode(node) {
+ return new DomPosition(node.parentNode, dom.getNodeIndex(node));
+ }
+
+ function getBoundaryAfterNode(node) {
+ return new DomPosition(node.parentNode, dom.getNodeIndex(node) + 1);
+ }
+
+ function insertNodeAtPosition(node, n, o) {
+ var firstNodeInserted = node.nodeType == 11 ? node.firstChild : node;
+ if (dom.isCharacterDataNode(n)) {
+ if (o == n.length) {
+ dom.insertAfter(node, n);
+ } else {
+ n.parentNode.insertBefore(node, o == 0 ? n : dom.splitDataNode(n, o));
+ }
+ } else if (o >= n.childNodes.length) {
+ n.appendChild(node);
+ } else {
+ n.insertBefore(node, n.childNodes[o]);
+ }
+ return firstNodeInserted;
+ }
+
+ function cloneSubtree(iterator) {
+ var partiallySelected;
+ for (var node, frag = getRangeDocument(iterator.range).createDocumentFragment(), subIterator; node = iterator.next(); ) {
+ partiallySelected = iterator.isPartiallySelectedSubtree();
+
+ node = node.cloneNode(!partiallySelected);
+ if (partiallySelected) {
+ subIterator = iterator.getSubtreeIterator();
+ node.appendChild(cloneSubtree(subIterator));
+ subIterator.detach(true);
+ }
+
+ if (node.nodeType == 10) { // DocumentType
+ throw new DOMException("HIERARCHY_REQUEST_ERR");
+ }
+ frag.appendChild(node);
+ }
+ return frag;
+ }
+
+ function iterateSubtree(rangeIterator, func, iteratorState) {
+ var it, n;
+ iteratorState = iteratorState || { stop: false };
+ for (var node, subRangeIterator; node = rangeIterator.next(); ) {
+ //log.debug("iterateSubtree, partially selected: " + rangeIterator.isPartiallySelectedSubtree(), nodeToString(node));
+ if (rangeIterator.isPartiallySelectedSubtree()) {
+ // The node is partially selected by the Range, so we can use a new RangeIterator on the portion of the
+ // node selected by the Range.
+ if (func(node) === false) {
+ iteratorState.stop = true;
+ return;
+ } else {
+ subRangeIterator = rangeIterator.getSubtreeIterator();
+ iterateSubtree(subRangeIterator, func, iteratorState);
+ subRangeIterator.detach(true);
+ if (iteratorState.stop) {
+ return;
+ }
+ }
+ } else {
+ // The whole node is selected, so we can use efficient DOM iteration to iterate over the node and its
+ // descendant
+ it = dom.createIterator(node);
+ while ( (n = it.next()) ) {
+ if (func(n) === false) {
+ iteratorState.stop = true;
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ function deleteSubtree(iterator) {
+ var subIterator;
+ while (iterator.next()) {
+ if (iterator.isPartiallySelectedSubtree()) {
+ subIterator = iterator.getSubtreeIterator();
+ deleteSubtree(subIterator);
+ subIterator.detach(true);
+ } else {
+ iterator.remove();
+ }
+ }
+ }
+
+ function extractSubtree(iterator) {
+
+ for (var node, frag = getRangeDocument(iterator.range).createDocumentFragment(), subIterator; node = iterator.next(); ) {
+
+
+ if (iterator.isPartiallySelectedSubtree()) {
+ node = node.cloneNode(false);
+ subIterator = iterator.getSubtreeIterator();
+ node.appendChild(extractSubtree(subIterator));
+ subIterator.detach(true);
+ } else {
+ iterator.remove();
+ }
+ if (node.nodeType == 10) { // DocumentType
+ throw new DOMException("HIERARCHY_REQUEST_ERR");
+ }
+ frag.appendChild(node);
+ }
+ return frag;
+ }
+
+ function getNodesInRange(range, nodeTypes, filter) {
+ //log.info("getNodesInRange, " + nodeTypes.join(","));
+ var filterNodeTypes = !!(nodeTypes && nodeTypes.length), regex;
+ var filterExists = !!filter;
+ if (filterNodeTypes) {
+ regex = new RegExp("^(" + nodeTypes.join("|") + ")$");
+ }
+
+ var nodes = [];
+ iterateSubtree(new RangeIterator(range, false), function(node) {
+ if ((!filterNodeTypes || regex.test(node.nodeType)) && (!filterExists || filter(node))) {
+ nodes.push(node);
+ }
+ });
+ return nodes;
+ }
+
+ function inspect(range) {
+ var name = (typeof range.getName == "undefined") ? "Range" : range.getName();
+ return "[" + name + "(" + dom.inspectNode(range.startContainer) + ":" + range.startOffset + ", " +
+ dom.inspectNode(range.endContainer) + ":" + range.endOffset + ")]";
+ }
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // RangeIterator code partially borrows from IERange by Tim Ryan (http://github.com/timcameronryan/IERange)
+
+ /**
+ * @constructor
+ */
+ function RangeIterator(range, clonePartiallySelectedTextNodes) {
+ this.range = range;
+ this.clonePartiallySelectedTextNodes = clonePartiallySelectedTextNodes;
+
+
+
+ if (!range.collapsed) {
+ this.sc = range.startContainer;
+ this.so = range.startOffset;
+ this.ec = range.endContainer;
+ this.eo = range.endOffset;
+ var root = range.commonAncestorContainer;
+
+ if (this.sc === this.ec && dom.isCharacterDataNode(this.sc)) {
+ this.isSingleCharacterDataNode = true;
+ this._first = this._last = this._next = this.sc;
+ } else {
+ this._first = this._next = (this.sc === root && !dom.isCharacterDataNode(this.sc)) ?
+ this.sc.childNodes[this.so] : dom.getClosestAncestorIn(this.sc, root, true);
+ this._last = (this.ec === root && !dom.isCharacterDataNode(this.ec)) ?
+ this.ec.childNodes[this.eo - 1] : dom.getClosestAncestorIn(this.ec, root, true);
+ }
+
+ }
+ }
+
+ RangeIterator.prototype = {
+ _current: null,
+ _next: null,
+ _first: null,
+ _last: null,
+ isSingleCharacterDataNode: false,
+
+ reset: function() {
+ this._current = null;
+ this._next = this._first;
+ },
+
+ hasNext: function() {
+ return !!this._next;
+ },
+
+ next: function() {
+ // Move to next node
+ var current = this._current = this._next;
+ if (current) {
+ this._next = (current !== this._last) ? current.nextSibling : null;
+
+ // Check for partially selected text nodes
+ if (dom.isCharacterDataNode(current) && this.clonePartiallySelectedTextNodes) {
+ if (current === this.ec) {
+
+ (current = current.cloneNode(true)).deleteData(this.eo, current.length - this.eo);
+ }
+ if (this._current === this.sc) {
+
+ (current = current.cloneNode(true)).deleteData(0, this.so);
+ }
+ }
+ }
+
+ return current;
+ },
+
+ remove: function() {
+ var current = this._current, start, end;
+
+ if (dom.isCharacterDataNode(current) && (current === this.sc || current === this.ec)) {
+ start = (current === this.sc) ? this.so : 0;
+ end = (current === this.ec) ? this.eo : current.length;
+ if (start != end) {
+ current.deleteData(start, end - start);
+ }
+ } else {
+ if (current.parentNode) {
+ current.parentNode.removeChild(current);
+ } else {
+
+ }
+ }
+ },
+
+ // Checks if the current node is partially selected
+ isPartiallySelectedSubtree: function() {
+ var current = this._current;
+ return isNonTextPartiallySelected(current, this.range);
+ },
+
+ getSubtreeIterator: function() {
+ var subRange;
+ if (this.isSingleCharacterDataNode) {
+ subRange = this.range.cloneRange();
+ subRange.collapse();
+ } else {
+ subRange = new Range(getRangeDocument(this.range));
+ var current = this._current;
+ var startContainer = current, startOffset = 0, endContainer = current, endOffset = dom.getNodeLength(current);
+
+ if (dom.isAncestorOf(current, this.sc, true)) {
+ startContainer = this.sc;
+ startOffset = this.so;
+ }
+ if (dom.isAncestorOf(current, this.ec, true)) {
+ endContainer = this.ec;
+ endOffset = this.eo;
+ }
+
+ updateBoundaries(subRange, startContainer, startOffset, endContainer, endOffset);
+ }
+ return new RangeIterator(subRange, this.clonePartiallySelectedTextNodes);
+ },
+
+ detach: function(detachRange) {
+ if (detachRange) {
+ this.range.detach();
+ }
+ this.range = this._current = this._next = this._first = this._last = this.sc = this.so = this.ec = this.eo = null;
+ }
+ };
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // Exceptions
+
+ /**
+ * @constructor
+ */
+ function RangeException(codeName) {
+ this.code = this[codeName];
+ this.codeName = codeName;
+ this.message = "RangeException: " + this.codeName;
+ }
+
+ RangeException.prototype = {
+ BAD_BOUNDARYPOINTS_ERR: 1,
+ INVALID_NODE_TYPE_ERR: 2
+ };
+
+ RangeException.prototype.toString = function() {
+ return this.message;
+ };
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ /**
+ * Currently iterates through all nodes in the range on creation until I think of a decent way to do it
+ * TODO: Look into making this a proper iterator, not requiring preloading everything first
+ * @constructor
+ */
+ function RangeNodeIterator(range, nodeTypes, filter) {
+ this.nodes = getNodesInRange(range, nodeTypes, filter);
+ this._next = this.nodes[0];
+ this._position = 0;
+ }
+
+ RangeNodeIterator.prototype = {
+ _current: null,
+
+ hasNext: function() {
+ return !!this._next;
+ },
+
+ next: function() {
+ this._current = this._next;
+ this._next = this.nodes[ ++this._position ];
+ return this._current;
+ },
+
+ detach: function() {
+ this._current = this._next = this.nodes = null;
+ }
+ };
+
+ var beforeAfterNodeTypes = [1, 3, 4, 5, 7, 8, 10];
+ var rootContainerNodeTypes = [2, 9, 11];
+ var readonlyNodeTypes = [5, 6, 10, 12];
+ var insertableNodeTypes = [1, 3, 4, 5, 7, 8, 10, 11];
+ var surroundNodeTypes = [1, 3, 4, 5, 7, 8];
+
+ function createAncestorFinder(nodeTypes) {
+ return function(node, selfIsAncestor) {
+ var t, n = selfIsAncestor ? node : node.parentNode;
+ while (n) {
+ t = n.nodeType;
+ if (dom.arrayContains(nodeTypes, t)) {
+ return n;
+ }
+ n = n.parentNode;
+ }
+ return null;
+ };
+ }
+
+ var getRootContainer = dom.getRootContainer;
+ var getDocumentOrFragmentContainer = createAncestorFinder( [9, 11] );
+ var getReadonlyAncestor = createAncestorFinder(readonlyNodeTypes);
+ var getDocTypeNotationEntityAncestor = createAncestorFinder( [6, 10, 12] );
+
+ function assertNoDocTypeNotationEntityAncestor(node, allowSelf) {
+ if (getDocTypeNotationEntityAncestor(node, allowSelf)) {
+ throw new RangeException("INVALID_NODE_TYPE_ERR");
+ }
+ }
+
+ function assertNotDetached(range) {
+ if (!range.startContainer) {
+ throw new DOMException("INVALID_STATE_ERR");
+ }
+ }
+
+ function assertValidNodeType(node, invalidTypes) {
+ if (!dom.arrayContains(invalidTypes, node.nodeType)) {
+ throw new RangeException("INVALID_NODE_TYPE_ERR");
+ }
+ }
+
+ function assertValidOffset(node, offset) {
+ if (offset < 0 || offset > (dom.isCharacterDataNode(node) ? node.length : node.childNodes.length)) {
+ throw new DOMException("INDEX_SIZE_ERR");
+ }
+ }
+
+ function assertSameDocumentOrFragment(node1, node2) {
+ if (getDocumentOrFragmentContainer(node1, true) !== getDocumentOrFragmentContainer(node2, true)) {
+ throw new DOMException("WRONG_DOCUMENT_ERR");
+ }
+ }
+
+ function assertNodeNotReadOnly(node) {
+ if (getReadonlyAncestor(node, true)) {
+ throw new DOMException("NO_MODIFICATION_ALLOWED_ERR");
+ }
+ }
+
+ function assertNode(node, codeName) {
+ if (!node) {
+ throw new DOMException(codeName);
+ }
+ }
+
+ function isOrphan(node) {
+ return !dom.arrayContains(rootContainerNodeTypes, node.nodeType) && !getDocumentOrFragmentContainer(node, true);
+ }
+
+ function isValidOffset(node, offset) {
+ return offset <= (dom.isCharacterDataNode(node) ? node.length : node.childNodes.length);
+ }
+
+ function assertRangeValid(range) {
+ assertNotDetached(range);
+ if (isOrphan(range.startContainer) || isOrphan(range.endContainer) ||
+ !isValidOffset(range.startContainer, range.startOffset) ||
+ !isValidOffset(range.endContainer, range.endOffset)) {
+ throw new Error("Range error: Range is no longer valid after DOM mutation (" + range.inspect() + ")");
+ }
+ }
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // Test the browser's innerHTML support to decide how to implement createContextualFragment
+ var styleEl = document.createElement("style");
+ var htmlParsingConforms = false;
+ try {
+ styleEl.innerHTML = "<b>x</b>";
+ htmlParsingConforms = (styleEl.firstChild.nodeType == 3); // Opera incorrectly creates an element node
+ } catch (e) {
+ // IE 6 and 7 throw
+ }
+
+ api.features.htmlParsingConforms = htmlParsingConforms;
+
+ var createContextualFragment = htmlParsingConforms ?
+
+ // Implementation as per HTML parsing spec, trusting in the browser's implementation of innerHTML. See
+ // discussion and base code for this implementation at issue 67.
+ // Spec: http://html5.org/specs/dom-parsing.html#extensions-to-the-range-interface
+ // Thanks to Aleks Williams.
+ function(fragmentStr) {
+ // "Let node the context object's start's node."
+ var node = this.startContainer;
+ var doc = dom.getDocument(node);
+
+ // "If the context object's start's node is null, raise an INVALID_STATE_ERR
+ // exception and abort these steps."
+ if (!node) {
+ throw new DOMException("INVALID_STATE_ERR");
+ }
+
+ // "Let element be as follows, depending on node's interface:"
+ // Document, Document Fragment: null
+ var el = null;
+
+ // "Element: node"
+ if (node.nodeType == 1) {
+ el = node;
+
+ // "Text, Comment: node's parentElement"
+ } else if (dom.isCharacterDataNode(node)) {
+ el = dom.parentElement(node);
+ }
+
+ // "If either element is null or element's ownerDocument is an HTML document
+ // and element's local name is "html" and element's namespace is the HTML
+ // namespace"
+ if (el === null || (
+ el.nodeName == "HTML"
+ && dom.isHtmlNamespace(dom.getDocument(el).documentElement)
+ && dom.isHtmlNamespace(el)
+ )) {
+
+ // "let element be a new Element with "body" as its local name and the HTML
+ // namespace as its namespace.""
+ el = doc.createElement("body");
+ } else {
+ el = el.cloneNode(false);
+ }
+
+ // "If the node's document is an HTML document: Invoke the HTML fragment parsing algorithm."
+ // "If the node's document is an XML document: Invoke the XML fragment parsing algorithm."
+ // "In either case, the algorithm must be invoked with fragment as the input
+ // and element as the context element."
+ el.innerHTML = fragmentStr;
+
+ // "If this raises an exception, then abort these steps. Otherwise, let new
+ // children be the nodes returned."
+
+ // "Let fragment be a new DocumentFragment."
+ // "Append all new children to fragment."
+ // "Return fragment."
+ return dom.fragmentFromNodeChildren(el);
+ } :
+
+ // In this case, innerHTML cannot be trusted, so fall back to a simpler, non-conformant implementation that
+ // previous versions of Rangy used (with the exception of using a body element rather than a div)
+ function(fragmentStr) {
+ assertNotDetached(this);
+ var doc = getRangeDocument(this);
+ var el = doc.createElement("body");
+ el.innerHTML = fragmentStr;
+
+ return dom.fragmentFromNodeChildren(el);
+ };
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ var rangeProperties = ["startContainer", "startOffset", "endContainer", "endOffset", "collapsed",
+ "commonAncestorContainer"];
+
+ var s2s = 0, s2e = 1, e2e = 2, e2s = 3;
+ var n_b = 0, n_a = 1, n_b_a = 2, n_i = 3;
+
+ function RangePrototype() {}
+
+ RangePrototype.prototype = {
+ attachListener: function(type, listener) {
+ this._listeners[type].push(listener);
+ },
+
+ compareBoundaryPoints: function(how, range) {
+ assertRangeValid(this);
+ assertSameDocumentOrFragment(this.startContainer, range.startContainer);
+
+ var nodeA, offsetA, nodeB, offsetB;
+ var prefixA = (how == e2s || how == s2s) ? "start" : "end";
+ var prefixB = (how == s2e || how == s2s) ? "start" : "end";
+ nodeA = this[prefixA + "Container"];
+ offsetA = this[prefixA + "Offset"];
+ nodeB = range[prefixB + "Container"];
+ offsetB = range[prefixB + "Offset"];
+ return dom.comparePoints(nodeA, offsetA, nodeB, offsetB);
+ },
+
+ insertNode: function(node) {
+ assertRangeValid(this);
+ assertValidNodeType(node, insertableNodeTypes);
+ assertNodeNotReadOnly(this.startContainer);
+
+ if (dom.isAncestorOf(node, this.startContainer, true)) {
+ throw new DOMException("HIERARCHY_REQUEST_ERR");
+ }
+
+ // No check for whether the container of the start of the Range is of a type that does not allow
+ // children of the type of node: the browser's DOM implementation should do this for us when we attempt
+ // to add the node
+
+ var firstNodeInserted = insertNodeAtPosition(node, this.startContainer, this.startOffset);
+ this.setStartBefore(firstNodeInserted);
+ },
+
+ cloneContents: function() {
+ assertRangeValid(this);
+
+ var clone, frag;
+ if (this.collapsed) {
+ return getRangeDocument(this).createDocumentFragment();
+ } else {
+ if (this.startContainer === this.endContainer && dom.isCharacterDataNode(this.startContainer)) {
+ clone = this.startContainer.cloneNode(true);
+ clone.data = clone.data.slice(this.startOffset, this.endOffset);
+ frag = getRangeDocument(this).createDocumentFragment();
+ frag.appendChild(clone);
+ return frag;
+ } else {
+ var iterator = new RangeIterator(this, true);
+ clone = cloneSubtree(iterator);
+ iterator.detach();
+ }
+ return clone;
+ }
+ },
+
+ canSurroundContents: function() {
+ assertRangeValid(this);
+ assertNodeNotReadOnly(this.startContainer);
+ assertNodeNotReadOnly(this.endContainer);
+
+ // Check if the contents can be surrounded. Specifically, this means whether the range partially selects
+ // no non-text nodes.
+ var iterator = new RangeIterator(this, true);
+ var boundariesInvalid = (iterator._first && (isNonTextPartiallySelected(iterator._first, this)) ||
+ (iterator._last && isNonTextPartiallySelected(iterator._last, this)));
+ iterator.detach();
+ return !boundariesInvalid;
+ },
+
+ surroundContents: function(node) {
+ assertValidNodeType(node, surroundNodeTypes);
+
+ if (!this.canSurroundContents()) {
+ throw new RangeException("BAD_BOUNDARYPOINTS_ERR");
+ }
+
+ // Extract the contents
+ var content = this.extractContents();
+
+ // Clear the children of the node
+ if (node.hasChildNodes()) {
+ while (node.lastChild) {
+ node.removeChild(node.lastChild);
+ }
+ }
+
+ // Insert the new node and add the extracted contents
+ insertNodeAtPosition(node, this.startContainer, this.startOffset);
+ node.appendChild(content);
+
+ this.selectNode(node);
+ },
+
+ cloneRange: function() {
+ assertRangeValid(this);
+ var range = new Range(getRangeDocument(this));
+ var i = rangeProperties.length, prop;
+ while (i--) {
+ prop = rangeProperties[i];
+ range[prop] = this[prop];
+ }
+ return range;
+ },
+
+ toString: function() {
+ assertRangeValid(this);
+ var sc = this.startContainer;
+ if (sc === this.endContainer && dom.isCharacterDataNode(sc)) {
+ return (sc.nodeType == 3 || sc.nodeType == 4) ? sc.data.slice(this.startOffset, this.endOffset) : "";
+ } else {
+ var textBits = [], iterator = new RangeIterator(this, true);
+
+ iterateSubtree(iterator, function(node) {
+ // Accept only text or CDATA nodes, not comments
+
+ if (node.nodeType == 3 || node.nodeType == 4) {
+ textBits.push(node.data);
+ }
+ });
+ iterator.detach();
+ return textBits.join("");
+ }
+ },
+
+ // The methods below are all non-standard. The following batch were introduced by Mozilla but have since
+ // been removed from Mozilla.
+
+ compareNode: function(node) {
+ assertRangeValid(this);
+
+ var parent = node.parentNode;
+ var nodeIndex = dom.getNodeIndex(node);
+
+ if (!parent) {
+ throw new DOMException("NOT_FOUND_ERR");
+ }
+
+ var startComparison = this.comparePoint(parent, nodeIndex),
+ endComparison = this.comparePoint(parent, nodeIndex + 1);
+
+ if (startComparison < 0) { // Node starts before
+ return (endComparison > 0) ? n_b_a : n_b;
+ } else {
+ return (endComparison > 0) ? n_a : n_i;
+ }
+ },
+
+ comparePoint: function(node, offset) {
+ assertRangeValid(this);
+ assertNode(node, "HIERARCHY_REQUEST_ERR");
+ assertSameDocumentOrFragment(node, this.startContainer);
+
+ if (dom.comparePoints(node, offset, this.startContainer, this.startOffset) < 0) {
+ return -1;
+ } else if (dom.comparePoints(node, offset, this.endContainer, this.endOffset) > 0) {
+ return 1;
+ }
+ return 0;
+ },
+
+ createContextualFragment: createContextualFragment,
+
+ toHtml: function() {
+ assertRangeValid(this);
+ var container = getRangeDocument(this).createElement("div");
+ container.appendChild(this.cloneContents());
+ return container.innerHTML;
+ },
+
+ // touchingIsIntersecting determines whether this method considers a node that borders a range intersects
+ // with it (as in WebKit) or not (as in Gecko pre-1.9, and the default)
+ intersectsNode: function(node, touchingIsIntersecting) {
+ assertRangeValid(this);
+ assertNode(node, "NOT_FOUND_ERR");
+ if (dom.getDocument(node) !== getRangeDocument(this)) {
+ return false;
+ }
+
+ var parent = node.parentNode, offset = dom.getNodeIndex(node);
+ assertNode(parent, "NOT_FOUND_ERR");
+
+ var startComparison = dom.comparePoints(parent, offset, this.endContainer, this.endOffset),
+ endComparison = dom.comparePoints(parent, offset + 1, this.startContainer, this.startOffset);
+
+ return touchingIsIntersecting ? startComparison <= 0 && endComparison >= 0 : startComparison < 0 && endComparison > 0;
+ },
+
+
+ isPointInRange: function(node, offset) {
+ assertRangeValid(this);
+ assertNode(node, "HIERARCHY_REQUEST_ERR");
+ assertSameDocumentOrFragment(node, this.startContainer);
+
+ return (dom.comparePoints(node, offset, this.startContainer, this.startOffset) >= 0) &&
+ (dom.comparePoints(node, offset, this.endContainer, this.endOffset) <= 0);
+ },
+
+ // The methods below are non-standard and invented by me.
+
+ // Sharing a boundary start-to-end or end-to-start does not count as intersection.
+ intersectsRange: function(range, touchingIsIntersecting) {
+ assertRangeValid(this);
+
+ if (getRangeDocument(range) != getRangeDocument(this)) {
+ throw new DOMException("WRONG_DOCUMENT_ERR");
+ }
+
+ var startComparison = dom.comparePoints(this.startContainer, this.startOffset, range.endContainer, range.endOffset),
+ endComparison = dom.comparePoints(this.endContainer, this.endOffset, range.startContainer, range.startOffset);
+
+ return touchingIsIntersecting ? startComparison <= 0 && endComparison >= 0 : startComparison < 0 && endComparison > 0;
+ },
+
+ intersection: function(range) {
+ if (this.intersectsRange(range)) {
+ var startComparison = dom.comparePoints(this.startContainer, this.startOffset, range.startContainer, range.startOffset),
+ endComparison = dom.comparePoints(this.endContainer, this.endOffset, range.endContainer, range.endOffset);
+
+ var intersectionRange = this.cloneRange();
+
+ if (startComparison == -1) {
+ intersectionRange.setStart(range.startContainer, range.startOffset);
+ }
+ if (endComparison == 1) {
+ intersectionRange.setEnd(range.endContainer, range.endOffset);
+ }
+ return intersectionRange;
+ }
+ return null;
+ },
+
+ union: function(range) {
+ if (this.intersectsRange(range, true)) {
+ var unionRange = this.cloneRange();
+ if (dom.comparePoints(range.startContainer, range.startOffset, this.startContainer, this.startOffset) == -1) {
+ unionRange.setStart(range.startContainer, range.startOffset);
+ }
+ if (dom.comparePoints(range.endContainer, range.endOffset, this.endContainer, this.endOffset) == 1) {
+ unionRange.setEnd(range.endContainer, range.endOffset);
+ }
+ return unionRange;
+ } else {
+ throw new RangeException("Ranges do not intersect");
+ }
+ },
+
+ containsNode: function(node, allowPartial) {
+ if (allowPartial) {
+ return this.intersectsNode(node, false);
+ } else {
+ return this.compareNode(node) == n_i;
+ }
+ },
+
+ containsNodeContents: function(node) {
+ return this.comparePoint(node, 0) >= 0 && this.comparePoint(node, dom.getNodeLength(node)) <= 0;
+ },
+
+ containsRange: function(range) {
+ return this.intersection(range).equals(range);
+ },
+
+ containsNodeText: function(node) {
+ var nodeRange = this.cloneRange();
+ nodeRange.selectNode(node);
+ var textNodes = nodeRange.getNodes([3]);
+ if (textNodes.length > 0) {
+ nodeRange.setStart(textNodes[0], 0);
+ var lastTextNode = textNodes.pop();
+ nodeRange.setEnd(lastTextNode, lastTextNode.length);
+ var contains = this.containsRange(nodeRange);
+ nodeRange.detach();
+ return contains;
+ } else {
+ return this.containsNodeContents(node);
+ }
+ },
+
+ createNodeIterator: function(nodeTypes, filter) {
+ assertRangeValid(this);
+ return new RangeNodeIterator(this, nodeTypes, filter);
+ },
+
+ getNodes: function(nodeTypes, filter) {
+ assertRangeValid(this);
+ return getNodesInRange(this, nodeTypes, filter);
+ },
+
+ getDocument: function() {
+ return getRangeDocument(this);
+ },
+
+ collapseBefore: function(node) {
+ assertNotDetached(this);
+
+ this.setEndBefore(node);
+ this.collapse(false);
+ },
+
+ collapseAfter: function(node) {
+ assertNotDetached(this);
+
+ this.setStartAfter(node);
+ this.collapse(true);
+ },
+
+ getName: function() {
+ return "DomRange";
+ },
+
+ equals: function(range) {
+ return Range.rangesEqual(this, range);
+ },
+
+ inspect: function() {
+ return inspect(this);
+ }
+ };
+
+ function copyComparisonConstantsToObject(obj) {
+ obj.START_TO_START = s2s;
+ obj.START_TO_END = s2e;
+ obj.END_TO_END = e2e;
+ obj.END_TO_START = e2s;
+
+ obj.NODE_BEFORE = n_b;
+ obj.NODE_AFTER = n_a;
+ obj.NODE_BEFORE_AND_AFTER = n_b_a;
+ obj.NODE_INSIDE = n_i;
+ }
+
+ function copyComparisonConstants(constructor) {
+ copyComparisonConstantsToObject(constructor);
+ copyComparisonConstantsToObject(constructor.prototype);
+ }
+
+ function createRangeContentRemover(remover, boundaryUpdater) {
+ return function() {
+ assertRangeValid(this);
+
+ var sc = this.startContainer, so = this.startOffset, root = this.commonAncestorContainer;
+
+ var iterator = new RangeIterator(this, true);
+
+ // Work out where to position the range after content removal
+ var node, boundary;
+ if (sc !== root) {
+ node = dom.getClosestAncestorIn(sc, root, true);
+ boundary = getBoundaryAfterNode(node);
+ sc = boundary.node;
+ so = boundary.offset;
+ }
+
+ // Check none of the range is read-only
+ iterateSubtree(iterator, assertNodeNotReadOnly);
+
+ iterator.reset();
+
+ // Remove the content
+ var returnValue = remover(iterator);
+ iterator.detach();
+
+ // Move to the new position
+ boundaryUpdater(this, sc, so, sc, so);
+
+ return returnValue;
+ };
+ }
+
+ function createPrototypeRange(constructor, boundaryUpdater, detacher) {
+ function createBeforeAfterNodeSetter(isBefore, isStart) {
+ return function(node) {
+ assertNotDetached(this);
+ assertValidNodeType(node, beforeAfterNodeTypes);
+ assertValidNodeType(getRootContainer(node), rootContainerNodeTypes);
+
+ var boundary = (isBefore ? getBoundaryBeforeNode : getBoundaryAfterNode)(node);
+ (isStart ? setRangeStart : setRangeEnd)(this, boundary.node, boundary.offset);
+ };
+ }
+
+ function setRangeStart(range, node, offset) {
+ var ec = range.endContainer, eo = range.endOffset;
+ if (node !== range.startContainer || offset !== range.startOffset) {
+ // Check the root containers of the range and the new boundary, and also check whether the new boundary
+ // is after the current end. In either case, collapse the range to the new position
+ if (getRootContainer(node) != getRootContainer(ec) || dom.comparePoints(node, offset, ec, eo) == 1) {
+ ec = node;
+ eo = offset;
+ }
+ boundaryUpdater(range, node, offset, ec, eo);
+ }
+ }
+
+ function setRangeEnd(range, node, offset) {
+ var sc = range.startContainer, so = range.startOffset;
+ if (node !== range.endContainer || offset !== range.endOffset) {
+ // Check the root containers of the range and the new boundary, and also check whether the new boundary
+ // is after the current end. In either case, collapse the range to the new position
+ if (getRootContainer(node) != getRootContainer(sc) || dom.comparePoints(node, offset, sc, so) == -1) {
+ sc = node;
+ so = offset;
+ }
+ boundaryUpdater(range, sc, so, node, offset);
+ }
+ }
+
+ function setRangeStartAndEnd(range, node, offset) {
+ if (node !== range.startContainer || offset !== range.startOffset || node !== range.endContainer || offset !== range.endOffset) {
+ boundaryUpdater(range, node, offset, node, offset);
+ }
+ }
+
+ constructor.prototype = new RangePrototype();
+
+ api.util.extend(constructor.prototype, {
+ setStart: function(node, offset) {
+ assertNotDetached(this);
+ assertNoDocTypeNotationEntityAncestor(node, true);
+ assertValidOffset(node, offset);
+
+ setRangeStart(this, node, offset);
+ },
+
+ setEnd: function(node, offset) {
+ assertNotDetached(this);
+ assertNoDocTypeNotationEntityAncestor(node, true);
+ assertValidOffset(node, offset);
+
+ setRangeEnd(this, node, offset);
+ },
+
+ setStartBefore: createBeforeAfterNodeSetter(true, true),
+ setStartAfter: createBeforeAfterNodeSetter(false, true),
+ setEndBefore: createBeforeAfterNodeSetter(true, false),
+ setEndAfter: createBeforeAfterNodeSetter(false, false),
+
+ collapse: function(isStart) {
+ assertRangeValid(this);
+ if (isStart) {
+ boundaryUpdater(this, this.startContainer, this.startOffset, this.startContainer, this.startOffset);
+ } else {
+ boundaryUpdater(this, this.endContainer, this.endOffset, this.endContainer, this.endOffset);
+ }
+ },
+
+ selectNodeContents: function(node) {
+ // This doesn't seem well specified: the spec talks only about selecting the node's contents, which
+ // could be taken to mean only its children. However, browsers implement this the same as selectNode for
+ // text nodes, so I shall do likewise
+ assertNotDetached(this);
+ assertNoDocTypeNotationEntityAncestor(node, true);
+
+ boundaryUpdater(this, node, 0, node, dom.getNodeLength(node));
+ },
+
+ selectNode: function(node) {
+ assertNotDetached(this);
+ assertNoDocTypeNotationEntityAncestor(node, false);
+ assertValidNodeType(node, beforeAfterNodeTypes);
+
+ var start = getBoundaryBeforeNode(node), end = getBoundaryAfterNode(node);
+ boundaryUpdater(this, start.node, start.offset, end.node, end.offset);
+ },
+
+ extractContents: createRangeContentRemover(extractSubtree, boundaryUpdater),
+
+ deleteContents: createRangeContentRemover(deleteSubtree, boundaryUpdater),
+
+ canSurroundContents: function() {
+ assertRangeValid(this);
+ assertNodeNotReadOnly(this.startContainer);
+ assertNodeNotReadOnly(this.endContainer);
+
+ // Check if the contents can be surrounded. Specifically, this means whether the range partially selects
+ // no non-text nodes.
+ var iterator = new RangeIterator(this, true);
+ var boundariesInvalid = (iterator._first && (isNonTextPartiallySelected(iterator._first, this)) ||
+ (iterator._last && isNonTextPartiallySelected(iterator._last, this)));
+ iterator.detach();
+ return !boundariesInvalid;
+ },
+
+ detach: function() {
+ detacher(this);
+ },
+
+ splitBoundaries: function() {
+ assertRangeValid(this);
+
+
+ var sc = this.startContainer, so = this.startOffset, ec = this.endContainer, eo = this.endOffset;
+ var startEndSame = (sc === ec);
+
+ if (dom.isCharacterDataNode(ec) && eo > 0 && eo < ec.length) {
+ dom.splitDataNode(ec, eo);
+
+ }
+
+ if (dom.isCharacterDataNode(sc) && so > 0 && so < sc.length) {
+
+ sc = dom.splitDataNode(sc, so);
+ if (startEndSame) {
+ eo -= so;
+ ec = sc;
+ } else if (ec == sc.parentNode && eo >= dom.getNodeIndex(sc)) {
+ eo++;
+ }
+ so = 0;
+
+ }
+ boundaryUpdater(this, sc, so, ec, eo);
+ },
+
+ normalizeBoundaries: function() {
+ assertRangeValid(this);
+
+ var sc = this.startContainer, so = this.startOffset, ec = this.endContainer, eo = this.endOffset;
+
+ var mergeForward = function(node) {
+ var sibling = node.nextSibling;
+ if (sibling && sibling.nodeType == node.nodeType) {
+ ec = node;
+ eo = node.length;
+ node.appendData(sibling.data);
+ sibling.parentNode.removeChild(sibling);
+ }
+ };
+
+ var mergeBackward = function(node) {
+ var sibling = node.previousSibling;
+ if (sibling && sibling.nodeType == node.nodeType) {
+ sc = node;
+ var nodeLength = node.length;
+ so = sibling.length;
+ node.insertData(0, sibling.data);
+ sibling.parentNode.removeChild(sibling);
+ if (sc == ec) {
+ eo += so;
+ ec = sc;
+ } else if (ec == node.parentNode) {
+ var nodeIndex = dom.getNodeIndex(node);
+ if (eo == nodeIndex) {
+ ec = node;
+ eo = nodeLength;
+ } else if (eo > nodeIndex) {
+ eo--;
+ }
+ }
+ }
+ };
+
+ var normalizeStart = true;
+
+ if (dom.isCharacterDataNode(ec)) {
+ if (ec.length == eo) {
+ mergeForward(ec);
+ }
+ } else {
+ if (eo > 0) {
+ var endNode = ec.childNodes[eo - 1];
+ if (endNode && dom.isCharacterDataNode(endNode)) {
+ mergeForward(endNode);
+ }
+ }
+ normalizeStart = !this.collapsed;
+ }
+
+ if (normalizeStart) {
+ if (dom.isCharacterDataNode(sc)) {
+ if (so == 0) {
+ mergeBackward(sc);
+ }
+ } else {
+ if (so < sc.childNodes.length) {
+ var startNode = sc.childNodes[so];
+ if (startNode && dom.isCharacterDataNode(startNode)) {
+ mergeBackward(startNode);
+ }
+ }
+ }
+ } else {
+ sc = ec;
+ so = eo;
+ }
+
+ boundaryUpdater(this, sc, so, ec, eo);
+ },
+
+ collapseToPoint: function(node, offset) {
+ assertNotDetached(this);
+
+ assertNoDocTypeNotationEntityAncestor(node, true);
+ assertValidOffset(node, offset);
+
+ setRangeStartAndEnd(this, node, offset);
+ }
+ });
+
+ copyComparisonConstants(constructor);
+ }
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ // Updates commonAncestorContainer and collapsed after boundary change
+ function updateCollapsedAndCommonAncestor(range) {
+ range.collapsed = (range.startContainer === range.endContainer && range.startOffset === range.endOffset);
+ range.commonAncestorContainer = range.collapsed ?
+ range.startContainer : dom.getCommonAncestor(range.startContainer, range.endContainer);
+ }
+
+ function updateBoundaries(range, startContainer, startOffset, endContainer, endOffset) {
+ var startMoved = (range.startContainer !== startContainer || range.startOffset !== startOffset);
+ var endMoved = (range.endContainer !== endContainer || range.endOffset !== endOffset);
+
+ range.startContainer = startContainer;
+ range.startOffset = startOffset;
+ range.endContainer = endContainer;
+ range.endOffset = endOffset;
+
+ updateCollapsedAndCommonAncestor(range);
+ dispatchEvent(range, "boundarychange", {startMoved: startMoved, endMoved: endMoved});
+ }
+
+ function detach(range) {
+ assertNotDetached(range);
+ range.startContainer = range.startOffset = range.endContainer = range.endOffset = null;
+ range.collapsed = range.commonAncestorContainer = null;
+ dispatchEvent(range, "detach", null);
+ range._listeners = null;
+ }
+
+ /**
+ * @constructor
+ */
+ function Range(doc) {
+ this.startContainer = doc;
+ this.startOffset = 0;
+ this.endContainer = doc;
+ this.endOffset = 0;
+ this._listeners = {
+ boundarychange: [],
+ detach: []
+ };
+ updateCollapsedAndCommonAncestor(this);
+ }
+
+ createPrototypeRange(Range, updateBoundaries, detach);
+
+ api.rangePrototype = RangePrototype.prototype;
+
+ Range.rangeProperties = rangeProperties;
+ Range.RangeIterator = RangeIterator;
+ Range.copyComparisonConstants = copyComparisonConstants;
+ Range.createPrototypeRange = createPrototypeRange;
+ Range.inspect = inspect;
+ Range.getRangeDocument = getRangeDocument;
+ Range.rangesEqual = function(r1, r2) {
+ return r1.startContainer === r2.startContainer &&
+ r1.startOffset === r2.startOffset &&
+ r1.endContainer === r2.endContainer &&
+ r1.endOffset === r2.endOffset;
+ };
+
+ api.DomRange = Range;
+ api.RangeException = RangeException;
+});rangy.createModule("WrappedRange", function(api, module) {
+ api.requireModules( ["DomUtil", "DomRange"] );
+
+ /**
+ * @constructor
+ */
+ var WrappedRange;
+ var dom = api.dom;
+ var DomPosition = dom.DomPosition;
+ var DomRange = api.DomRange;
+
+
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ /*
+ This is a workaround for a bug where IE returns the wrong container element from the TextRange's parentElement()
+ method. For example, in the following (where pipes denote the selection boundaries):
+
+ <ul id="ul"><li id="a">| a </li><li id="b"> b |</li></ul>
+
+ var range = document.selection.createRange();
+ alert(range.parentElement().id); // Should alert "ul" but alerts "b"
+
+ This method returns the common ancestor node of the following:
+ - the parentElement() of the textRange
+ - the parentElement() of the textRange after calling collapse(true)
+ - the parentElement() of the textRange after calling collapse(false)
+ */
+ function getTextRangeContainerElement(textRange) {
+ var parentEl = textRange.parentElement();
+
+ var range = textRange.duplicate();
+ range.collapse(true);
+ var startEl = range.parentElement();
+ range = textRange.duplicate();
+ range.collapse(false);
+ var endEl = range.parentElement();
+ var startEndContainer = (startEl == endEl) ? startEl : dom.getCommonAncestor(startEl, endEl);
+
+ return startEndContainer == parentEl ? startEndContainer : dom.getCommonAncestor(parentEl, startEndContainer);
+ }
+
+ function textRangeIsCollapsed(textRange) {
+ return textRange.compareEndPoints("StartToEnd", textRange) == 0;
+ }
+
+ // Gets the boundary of a TextRange expressed as a node and an offset within that node. This function started out as
+ // an improved version of code found in Tim Cameron Ryan's IERange (http://code.google.com/p/ierange/) but has
+ // grown, fixing problems with line breaks in preformatted text, adding workaround for IE TextRange bugs, handling
+ // for inputs and images, plus optimizations.
+ function getTextRangeBoundaryPosition(textRange, wholeRangeContainerElement, isStart, isCollapsed) {
+ var workingRange = textRange.duplicate();
+
+ workingRange.collapse(isStart);
+ var containerElement = workingRange.parentElement();
+
+ // Sometimes collapsing a TextRange that's at the start of a text node can move it into the previous node, so
+ // check for that
+ // TODO: Find out when. Workaround for wholeRangeContainerElement may break this
+ if (!dom.isAncestorOf(wholeRangeContainerElement, containerElement, true)) {
+ containerElement = wholeRangeContainerElement;
+
+ }
+
+
+
+ // Deal with nodes that cannot "contain rich HTML markup". In practice, this means form inputs, images and
+ // similar. See http://msdn.microsoft.com/en-us/library/aa703950%28VS.85%29.aspx
+ if (!containerElement.canHaveHTML) {
+ return new DomPosition(containerElement.parentNode, dom.getNodeIndex(containerElement));
+ }
+
+ var workingNode = dom.getDocument(containerElement).createElement("span");
+ var comparison, workingComparisonType = isStart ? "StartToStart" : "StartToEnd";
+ var previousNode, nextNode, boundaryPosition, boundaryNode;
+
+ // Move the working range through the container's children, starting at the end and working backwards, until the
+ // working range reaches or goes past the boundary we're interested in
+ do {
+ containerElement.insertBefore(workingNode, workingNode.previousSibling);
+ workingRange.moveToElementText(workingNode);
+ } while ( (comparison = workingRange.compareEndPoints(workingComparisonType, textRange)) > 0 &&
+ workingNode.previousSibling);
+
+ // We've now reached or gone past the boundary of the text range we're interested in
+ // so have identified the node we want
+ boundaryNode = workingNode.nextSibling;
+
+ if (comparison == -1 && boundaryNode && dom.isCharacterDataNode(boundaryNode)) {
+ // This is a character data node (text, comment, cdata). The working range is collapsed at the start of the
+ // node containing the text range's boundary, so we move the end of the working range to the boundary point
+ // and measure the length of its text to get the boundary's offset within the node.
+ workingRange.setEndPoint(isStart ? "EndToStart" : "EndToEnd", textRange);
+
+
+ var offset;
+
+ if (/[\r\n]/.test(boundaryNode.data)) {
+ /*
+ For the particular case of a boundary within a text node containing line breaks (within a <pre> element,
+ for example), we need a slightly complicated approach to get the boundary's offset in IE. The facts:
+
+ - Each line break is represented as \r in the text node's data/nodeValue properties
+ - Each line break is represented as \r\n in the TextRange's 'text' property
+ - The 'text' property of the TextRange does not contain trailing line breaks
+
+ To get round the problem presented by the final fact above, we can use the fact that TextRange's
+ moveStart() and moveEnd() methods return the actual number of characters moved, which is not necessarily
+ the same as the number of characters it was instructed to move. The simplest approach is to use this to
+ store the characters moved when moving both the start and end of the range to the start of the document
+ body and subtracting the start offset from the end offset (the "move-negative-gazillion" method).
+ However, this is extremely slow when the document is large and the range is near the end of it. Clearly
+ doing the mirror image (i.e. moving the range boundaries to the end of the document) has the same
+ problem.
+
+ Another approach that works is to use moveStart() to move the start boundary of the range up to the end
+ boundary one character at a time and incrementing a counter with the value returned by the moveStart()
+ call. However, the check for whether the start boundary has reached the end boundary is expensive, so
+ this method is slow (although unlike "move-negative-gazillion" is largely unaffected by the location of
+ the range within the document).
+
+ The method below is a hybrid of the two methods above. It uses the fact that a string containing the
+ TextRange's 'text' property with each \r\n converted to a single \r character cannot be longer than the
+ text of the TextRange, so the start of the range is moved that length initially and then a character at
+ a time to make up for any trailing line breaks not contained in the 'text' property. This has good
+ performance in most situations compared to the previous two methods.
+ */
+ var tempRange = workingRange.duplicate();
+ var rangeLength = tempRange.text.replace(/\r\n/g, "\r").length;
+
+ offset = tempRange.moveStart("character", rangeLength);
+ while ( (comparison = tempRange.compareEndPoints("StartToEnd", tempRange)) == -1) {
+ offset++;
+ tempRange.moveStart("character", 1);
+ }
+ } else {
+ offset = workingRange.text.length;
+ }
+ boundaryPosition = new DomPosition(boundaryNode, offset);
+ } else {
+
+
+ // If the boundary immediately follows a character data node and this is the end boundary, we should favour
+ // a position within that, and likewise for a start boundary preceding a character data node
+ previousNode = (isCollapsed || !isStart) && workingNode.previousSibling;
+ nextNode = (isCollapsed || isStart) && workingNode.nextSibling;
+
+
+
+ if (nextNode && dom.isCharacterDataNode(nextNode)) {
+ boundaryPosition = new DomPosition(nextNode, 0);
+ } else if (previousNode && dom.isCharacterDataNode(previousNode)) {
+ boundaryPosition = new DomPosition(previousNode, previousNode.length);
+ } else {
+ boundaryPosition = new DomPosition(containerElement, dom.getNodeIndex(workingNode));
+ }
+ }
+
+ // Clean up
+ workingNode.parentNode.removeChild(workingNode);
+
+ return boundaryPosition;
+ }
+
+ // Returns a TextRange representing the boundary of a TextRange expressed as a node and an offset within that node.
+ // This function started out as an optimized version of code found in Tim Cameron Ryan's IERange
+ // (http://code.google.com/p/ierange/)
+ function createBoundaryTextRange(boundaryPosition, isStart) {
+ var boundaryNode, boundaryParent, boundaryOffset = boundaryPosition.offset;
+ var doc = dom.getDocument(boundaryPosition.node);
+ var workingNode, childNodes, workingRange = doc.body.createTextRange();
+ var nodeIsDataNode = dom.isCharacterDataNode(boundaryPosition.node);
+
+ if (nodeIsDataNode) {
+ boundaryNode = boundaryPosition.node;
+ boundaryParent = boundaryNode.parentNode;
+ } else {
+ childNodes = boundaryPosition.node.childNodes;
+ boundaryNode = (boundaryOffset < childNodes.length) ? childNodes[boundaryOffset] : null;
+ boundaryParent = boundaryPosition.node;
+ }
+
+ // Position the range immediately before the node containing the boundary
+ workingNode = doc.createElement("span");
+
+ // Making the working element non-empty element persuades IE to consider the TextRange boundary to be within the
+ // element rather than immediately before or after it, which is what we want
+ workingNode.innerHTML = "&#feff;";
+
+ // insertBefore is supposed to work like appendChild if the second parameter is null. However, a bug report
+ // for IERange suggests that it can crash the browser: http://code.google.com/p/ierange/issues/detail?id=12
+ if (boundaryNode) {
+ boundaryParent.insertBefore(workingNode, boundaryNode);
+ } else {
+ boundaryParent.appendChild(workingNode);
+ }
+
+ workingRange.moveToElementText(workingNode);
+ workingRange.collapse(!isStart);
+
+ // Clean up
+ boundaryParent.removeChild(workingNode);
+
+ // Move the working range to the text offset, if required
+ if (nodeIsDataNode) {
+ workingRange[isStart ? "moveStart" : "moveEnd"]("character", boundaryOffset);
+ }
+
+ return workingRange;
+ }
+
+ /*----------------------------------------------------------------------------------------------------------------*/
+
+ if (api.features.implementsDomRange && (!api.features.implementsTextRange || !api.config.preferTextRange)) {
+ // This is a wrapper around the browser's native DOM Range. It has two aims:
+ // - Provide workarounds for specific browser bugs
+ // - provide convenient extensions, which are inherited from Rangy's DomRange
+
+ (function() {
+ var rangeProto;
+ var rangeProperties = DomRange.rangeProperties;
+ var canSetRangeStartAfterEnd;
+
+ function updateRangeProperties(range) {
+ var i = rangeProperties.length, prop;
+ while (i--) {
+ prop = rangeProperties[i];
+ range[prop] = range.nativeRange[prop];
+ }
+ }
+
+ function updateNativeRange(range, startContainer, startOffset, endContainer,endOffset) {
+ var startMoved = (range.startContainer !== startContainer || range.startOffset != startOffset);
+ var endMoved = (range.endContainer !== endContainer || range.endOffset != endOffset);
+
+ // Always set both boundaries for the benefit of IE9 (see issue 35)
+ if (startMoved || endMoved) {
+ range.setEnd(endContainer, endOffset);
+ range.setStart(startContainer, startOffset);
+ }
+ }
+
+ function detach(range) {
+ range.nativeRange.detach();
+ range.detached = true;
+ var i = rangeProperties.length, prop;
+ while (i--) {
+ prop = rangeProperties[i];
+ range[prop] = null;
+ }
+ }
+
+ var createBeforeAfterNodeSetter;
+
+ WrappedRange = function(range) {
+ if (!range) {
+ throw new Error("Range must be specified");
+ }
+ this.nativeRange = range;
+ updateRangeProperties(this);
+ };
+
+ DomRange.createPrototypeRange(WrappedRange, updateNativeRange, detach);
+
+ rangeProto = WrappedRange.prototype;
+
+ rangeProto.selectNode = function(node) {
+ this.nativeRange.selectNode(node);
+ updateRangeProperties(this);
+ };
+
+ rangeProto.deleteContents = function() {
+ this.nativeRange.deleteContents();
+ updateRangeProperties(this);
+ };
+
+ rangeProto.extractContents = function() {
+ var frag = this.nativeRange.extractContents();
+ updateRangeProperties(this);
+ return frag;
+ };
+
+ rangeProto.cloneContents = function() {
+ return this.nativeRange.cloneContents();
+ };
+
+ // TODO: Until I can find a way to programmatically trigger the Firefox bug (apparently long-standing, still
+ // present in 3.6.8) that throws "Index or size is negative or greater than the allowed amount" for
+ // insertNode in some circumstances, all browsers will have to use the Rangy's own implementation of
+ // insertNode, which works but is almost certainly slower than the native implementation.
+/*
+ rangeProto.insertNode = function(node) {
+ this.nativeRange.insertNode(node);
+ updateRangeProperties(this);
+ };
+*/
+
+ rangeProto.surroundContents = function(node) {
+ this.nativeRange.surroundContents(node);
+ updateRangeProperties(this);
+ };
+
+ rangeProto.collapse = function(isStart) {
+ this.nativeRange.collapse(isStart);
+ updateRangeProperties(this);
+ };
+
+ rangeProto.cloneRange = function() {
+ return new WrappedRange(this.nativeRange.cloneRange());
+ };
+
+ rangeProto.refresh = function() {
+ updateRangeProperties(this);
+ };
+
+ rangeProto.toString = function() {
+ return this.nativeRange.toString();
+ };
+
+ // Create test range and node for feature detection
+
+ var testTextNode = document.createTextNode("test");
+ dom.getBody(document).appendChild(testTextNode);
+ var range = document.createRange();
+
+ /*--------------------------------------------------------------------------------------------------------*/
+
+ // Test for Firefox 2 bug that prevents moving the start of a Range to a point after its current end and
+ // correct for it
+
+ range.setStart(testTextNode, 0);
+ range.setEnd(testTextNode, 0);
+
+ try {
+ range.setStart(testTextNode, 1);
+ canSetRangeStartAfterEnd = true;
+
+ rangeProto.setStart = function(node, offset) {
+ this.nativeRange.setStart(node, offset);
+ updateRangeProperties(this);
+ };
+
+ rangeProto.setEnd = function(node, offset) {
+ this.nativeRange.setEnd(node, offset);
+ updateRangeProperties(this);
+ };
+
+ createBeforeAfterNodeSetter = function(name) {
+ return function(node) {
+ this.nativeRange[name](node);
+ updateRangeProperties(this);
+ };
+ };
+
+ } catch(ex) {
+
+
+ canSetRangeStartAfterEnd = false;
+
+ rangeProto.setStart = function(node, offset) {
+ try {
+ this.nativeRange.setStart(node, offset);
+ } catch (ex) {
+ this.nativeRange.setEnd(node, offset);
+ this.nativeRange.setStart(node, offset);
+ }
+ updateRangeProperties(this);
+ };
+
+ rangeProto.setEnd = function(node, offset) {
+ try {
+ this.nativeRange.setEnd(node, offset);
+ } catch (ex) {
+ this.nativeRange.setStart(node, offset);
+ this.nativeRange.setEnd(node, offset);
+ }
+ updateRangeProperties(this);
+ };
+
+ createBeforeAfterNodeSetter = function(name, oppositeName) {
+ return function(node) {
+ try {
+ this.nativeRange[name](node);
+ } catch (ex) {
+ this.nativeRange[oppositeName](node);
+ this.nativeRange[name](node);
+ }
+ updateRangeProperties(this);
+ };
+ };
+ }
+
+ rangeProto.setStartBefore = createBeforeAfterNodeSetter("setStartBefore", "setEndBefore");
+ rangeProto.setStartAfter = createBeforeAfterNodeSetter("setStartAfter", "setEndAfter");
+ rangeProto.setEndBefore = createBeforeAfterNodeSetter("setEndBefore", "setStartBefore");
+ rangeProto.setEndAfter = createBeforeAfterNodeSetter("setEndAfter", "setStartAfter");
+
+ /*--------------------------------------------------------------------------------------------------------*/
+
+ // Test for and correct Firefox 2 behaviour with selectNodeContents on text nodes: it collapses the range to
+ // the 0th character of the text node
+ range.selectNodeContents(testTextNode);
+ if (range.startContainer == testTextNode && range.endContainer == testTextNode &&
+ range.startOffset == 0 && range.endOffset == testTextNode.length) {
+ rangeProto.selectNodeContents = function(node) {
+ this.nativeRange.selectNodeContents(node);
+ updateRangeProperties(this);
+ };
+ } else {
+ rangeProto.selectNodeContents = function(node) {
+ this.setStart(node, 0);
+ this.setEnd(node, DomRange.getEndOffset(node));
+ };
+ }
+
+ /*--------------------------------------------------------------------------------------------------------*/
+
+ // Test for WebKit bug that has the beahviour of compareBoundaryPoints round the wrong way for constants
+ // START_TO_END and END_TO_START: https://bugs.webkit.org/show_bug.cgi?id=20738
+
+ range.selectNodeContents(testTextNode);
+ range.setEnd(testTextNode, 3);
+
+ var range2 = document.createRange();
+ range2.selectNodeContents(testTextNode);
+ range2.setEnd(testTextNode, 4);
+ range2.setStart(testTextNode, 2);
+
+ if (range.compareBoundaryPoints(range.START_TO_END, range2) == -1 &
+ range.compareBoundaryPoints(range.END_TO_START, range2) == 1) {
+ // This is the wrong way round, so correct for it
+
+
+ rangeProto.compareBoundaryPoints = function(type, range) {
+ range = range.nativeRange || range;
+ if (type == range.START_TO_END) {
+ type = range.END_TO_START;
+ } else if (type == range.END_TO_START) {
+ type = range.START_TO_END;
+ }
+ return this.nativeRange.compareBoundaryPoints(type, range);
+ };
+ } else {
+ rangeProto.compareBoundaryPoints = function(type, range) {
+ return this.nativeRange.compareBoundaryPoints(type, range.nativeRange || range);
+ };
+ }
+
+ /*--------------------------------------------------------------------------------------------------------*/
+
+ // Test for existence of createContextualFragment and delegate to it if it exists
+ if (api.util.isHostMethod(range, "createContextualFragment")) {
+ rangeProto.createContextualFragment = function(fragmentStr) {
+ return this.nativeRange.createContextualFragment(fragmentStr);
+ };
+ }
+
+ /*--------------------------------------------------------------------------------------------------------*/
+
+ // Clean up
+ dom.getBody(document).removeChild(testTextNode);
+ range.detach();
+ range2.detach();
+ })();
+
+ api.createNativeRange = function(doc) {
+ doc = doc || document;
+ return doc.createRange();
+ };
+ } else if (api.features.implementsTextRange) {
+ // This is a wrapper around a TextRange, providing full DOM Range functionality using rangy's DomRange as a
+ // prototype
+
+ WrappedRange = function(textRange) {
+ this.textRange = textRange;
+ this.refresh();
+ };
+
+ WrappedRange.prototype = new DomRange(document);
+
+ WrappedRange.prototype.refresh = function() {
+ var start, end;
+
+ // TextRange's parentElement() method cannot be trusted. getTextRangeContainerElement() works around that.
+ var rangeContainerElement = getTextRangeContainerElement(this.textRange);
+
+ if (textRangeIsCollapsed(this.textRange)) {
+ end = start = getTextRangeBoundaryPosition(this.textRange, rangeContainerElement, true, true);
+ } else {
+
+ start = getTextRangeBoundaryPosition(this.textRange, rangeContainerElement, true, false);
+ end = getTextRangeBoundaryPosition(this.textRange, rangeContainerElement, false, false);
+ }
+
+ this.setStart(start.node, start.offset);
+ this.setEnd(end.node, end.offset);
+ };
+
+ DomRange.copyComparisonConstants(WrappedRange);
+
+ // Add WrappedRange as the Range property of the global object to allow expression like Range.END_TO_END to work
+ var globalObj = (function() { return this; })();
+ if (typeof globalObj.Range == "undefined") {
+ globalObj.Range = WrappedRange;
+ }
+
+ api.createNativeRange = function(doc) {
+ doc = doc || document;
+ return doc.body.createTextRange();
+ };
+ }
+
+ if (api.features.implementsTextRange) {
+ WrappedRange.rangeToTextRange = function(range) {
+ if (range.collapsed) {
+ var tr = createBoundaryTextRange(new DomPosition(range.startContainer, range.startOffset), true);
+
+
+
+ return tr;
+
+ //return createBoundaryTextRange(new DomPosition(range.startContainer, range.startOffset), true);
+ } else {
+ var startRange = createBoundaryTextRange(new DomPosition(range.startContainer, range.startOffset), true);
+ var endRange = createBoundaryTextRange(new DomPosition(range.endContainer, range.endOffset), false);
+ var textRange = dom.getDocument(range.startContainer).body.createTextRange();
+ textRange.setEndPoint("StartToStart", startRange);
+ textRange.setEndPoint("EndToEnd", endRange);
+ return textRange;
+ }
+ };
+ }
+
+ WrappedRange.prototype.getName = function() {
+ return "WrappedRange";
+ };
+
+ api.WrappedRange = WrappedRange;
+
+ api.createRange = function(doc) {
+ doc = doc || document;
+ return new WrappedRange(api.createNativeRange(doc));
+ };
+
+ api.createRangyRange = function(doc) {
+ doc = doc || document;
+ return new DomRange(doc);
+ };
+
+ api.createIframeRange = function(iframeEl) {
+ return api.createRange(dom.getIframeDocument(iframeEl));
+ };
+
+ api.createIframeRangyRange = function(iframeEl) {
+ return api.createRangyRange(dom.getIframeDocument(iframeEl));
+ };
+
+ api.addCreateMissingNativeApiListener(function(win) {
+ var doc = win.document;
+ if (typeof doc.createRange == "undefined") {
+ doc.createRange = function() {
+ return api.createRange(this);
+ };
+ }
+ doc = win = null;
+ });
+});rangy.createModule("WrappedSelection", function(api, module) {
+ // This will create a selection object wrapper that follows the Selection object found in the WHATWG draft DOM Range
+ // spec (http://html5.org/specs/dom-range.html)
+
+ api.requireModules( ["DomUtil", "DomRange", "WrappedRange"] );
+
+ api.config.checkSelectionRanges = true;
+
+ var BOOLEAN = "boolean",
+ windowPropertyName = "_rangySelection",
+ dom = api.dom,
+ util = api.util,
+ DomRange = api.DomRange,
+ WrappedRange = api.WrappedRange,
+ DOMException = api.DOMException,
+ DomPosition = dom.DomPosition,
+ getSelection,
+ selectionIsCollapsed,
+ CONTROL = "Control";
+
+
+
+ function getWinSelection(winParam) {
+ return (winParam || window).getSelection();
+ }
+
+ function getDocSelection(winParam) {
+ return (winParam || window).document.selection;
+ }
+
+ // Test for the Range/TextRange and Selection features required
+ // Test for ability to retrieve selection
+ var implementsWinGetSelection = api.util.isHostMethod(window, "getSelection"),
+ implementsDocSelection = api.util.isHostObject(document, "selection");
+
+ var useDocumentSelection = implementsDocSelection && (!implementsWinGetSelection || api.config.preferTextRange);
+
+ if (useDocumentSelection) {
+ getSelection = getDocSelection;
+ api.isSelectionValid = function(winParam) {
+ var doc = (winParam || window).document, nativeSel = doc.selection;
+
+ // Check whether the selection TextRange is actually contained within the correct document
+ return (nativeSel.type != "None" || dom.getDocument(nativeSel.createRange().parentElement()) == doc);
+ };
+ } else if (implementsWinGetSelection) {
+ getSelection = getWinSelection;
+ api.isSelectionValid = function() {
+ return true;
+ };
+ } else {
+ module.fail("Neither document.selection or window.getSelection() detected.");
+ }
+
+ api.getNativeSelection = getSelection;
+
+ var testSelection = getSelection();
+ var testRange = api.createNativeRange(document);
+ var body = dom.getBody(document);
+
+ // Obtaining a range from a selection
+ var selectionHasAnchorAndFocus = util.areHostObjects(testSelection, ["anchorNode", "focusNode"] &&
+ util.areHostProperties(testSelection, ["anchorOffset", "focusOffset"]));
+ api.features.selectionHasAnchorAndFocus = selectionHasAnchorAndFocus;
+
+ // Test for existence of native selection extend() method
+ var selectionHasExtend = util.isHostMethod(testSelection, "extend");
+ api.features.selectionHasExtend = selectionHasExtend;
+
+ // Test if rangeCount exists
+ var selectionHasRangeCount = (typeof testSelection.rangeCount == "number");
+ api.features.selectionHasRangeCount = selectionHasRangeCount;
+
+ var selectionSupportsMultipleRanges = false;
+ var collapsedNonEditableSelectionsSupported = true;
+
+ if (util.areHostMethods(testSelection, ["addRange", "getRangeAt", "removeAllRanges"]) &&
+ typeof testSelection.rangeCount == "number" && api.features.implementsDomRange) {
+
+ (function() {
+ var iframe = document.createElement("iframe");
+ body.appendChild(iframe);
+
+ var iframeDoc = dom.getIframeDocument(iframe);
+ iframeDoc.open();
+ iframeDoc.write("<html><head></head><body>12</body></html>");
+ iframeDoc.close();
+
+ var sel = dom.getIframeWindow(iframe).getSelection();
+ var docEl = iframeDoc.documentElement;
+ var iframeBody = docEl.lastChild, textNode = iframeBody.firstChild;
+
+ // Test whether the native selection will allow a collapsed selection within a non-editable element
+ var r1 = iframeDoc.createRange();
+ r1.setStart(textNode, 1);
+ r1.collapse(true);
+ sel.addRange(r1);
+ collapsedNonEditableSelectionsSupported = (sel.rangeCount == 1);
+ sel.removeAllRanges();
+
+ // Test whether the native selection is capable of supporting multiple ranges
+ var r2 = r1.cloneRange();
+ r1.setStart(textNode, 0);
+ r2.setEnd(textNode, 2);
+ sel.addRange(r1);
+ sel.addRange(r2);
+
+ selectionSupportsMultipleRanges = (sel.rangeCount == 2);
+
+ // Clean up
+ r1.detach();
+ r2.detach();
+
+ body.removeChild(iframe);
+ })();
+ }
+
+ api.features.selectionSupportsMultipleRanges = selectionSupportsMultipleRanges;
+ api.features.collapsedNonEditableSelectionsSupported = collapsedNonEditableSelectionsSupported;
+
+ // ControlRanges
+ var implementsControlRange = false, testControlRange;
+
+ if (body && util.isHostMethod(body, "createControlRange")) {
+ testControlRange = body.createControlRange();
+ if (util.areHostProperties(testControlRange, ["item", "add"])) {
+ implementsControlRange = true;
+ }
+ }
+ api.features.implementsControlRange = implementsControlRange;
+
+ // Selection collapsedness
+ if (selectionHasAnchorAndFocus) {
+ selectionIsCollapsed = function(sel) {
+ return sel.anchorNode === sel.focusNode && sel.anchorOffset === sel.focusOffset;
+ };
+ } else {
+ selectionIsCollapsed = function(sel) {
+ return sel.rangeCount ? sel.getRangeAt(sel.rangeCount - 1).collapsed : false;
+ };
+ }
+
+ function updateAnchorAndFocusFromRange(sel, range, backwards) {
+ var anchorPrefix = backwards ? "end" : "start", focusPrefix = backwards ? "start" : "end";
+ sel.anchorNode = range[anchorPrefix + "Container"];
+ sel.anchorOffset = range[anchorPrefix + "Offset"];
+ sel.focusNode = range[focusPrefix + "Container"];
+ sel.focusOffset = range[focusPrefix + "Offset"];
+ }
+
+ function updateAnchorAndFocusFromNativeSelection(sel) {
+ var nativeSel = sel.nativeSelection;
+ sel.anchorNode = nativeSel.anchorNode;
+ sel.anchorOffset = nativeSel.anchorOffset;
+ sel.focusNode = nativeSel.focusNode;
+ sel.focusOffset = nativeSel.focusOffset;
+ }
+
+ function updateEmptySelection(sel) {
+ sel.anchorNode = sel.focusNode = null;
+ sel.anchorOffset = sel.focusOffset = 0;
+ sel.rangeCount = 0;
+ sel.isCollapsed = true;
+ sel._ranges.length = 0;
+ }
+
+ function getNativeRange(range) {
+ var nativeRange;
+ if (range instanceof DomRange) {
+ nativeRange = range._selectionNativeRange;
+ if (!nativeRange) {
+ nativeRange = api.createNativeRange(dom.getDocument(range.startContainer));
+ nativeRange.setEnd(range.endContainer, range.endOffset);
+ nativeRange.setStart(range.startContainer, range.startOffset);
+ range._selectionNativeRange = nativeRange;
+ range.attachListener("detach", function() {
+
+ this._selectionNativeRange = null;
+ });
+ }
+ } else if (range instanceof WrappedRange) {
+ nativeRange = range.nativeRange;
+ } else if (api.features.implementsDomRange && (range instanceof dom.getWindow(range.startContainer).Range)) {
+ nativeRange = range;
+ }
+ return nativeRange;
+ }
+
+ function rangeContainsSingleElement(rangeNodes) {
+ if (!rangeNodes.length || rangeNodes[0].nodeType != 1) {
+ return false;
+ }
+ for (var i = 1, len = rangeNodes.length; i < len; ++i) {
+ if (!dom.isAncestorOf(rangeNodes[0], rangeNodes[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ function getSingleElementFromRange(range) {
+ var nodes = range.getNodes();
+ if (!rangeContainsSingleElement(nodes)) {
+ throw new Error("getSingleElementFromRange: range " + range.inspect() + " did not consist of a single element");
+ }
+ return nodes[0];
+ }
+
+ function isTextRange(range) {
+ return !!range && typeof range.text != "undefined";
+ }
+
+ function updateFromTextRange(sel, range) {
+ // Create a Range from the selected TextRange
+ var wrappedRange = new WrappedRange(range);
+ sel._ranges = [wrappedRange];
+
+ updateAnchorAndFocusFromRange(sel, wrappedRange, false);
+ sel.rangeCount = 1;
+ sel.isCollapsed = wrappedRange.collapsed;
+ }
+
+ function updateControlSelection(sel) {
+ // Update the wrapped selection based on what's now in the native selection
+ sel._ranges.length = 0;
+ if (sel.docSelection.type == "None") {
+ updateEmptySelection(sel);
+ } else {
+ var controlRange = sel.docSelection.createRange();
+ if (isTextRange(controlRange)) {
+ // This case (where the selection type is "Control" and calling createRange() on the selection returns
+ // a TextRange) can happen in IE 9. It happens, for example, when all elements in the selected
+ // ControlRange have been removed from the ControlRange and removed from the document.
+ updateFromTextRange(sel, controlRange);
+ } else {
+ sel.rangeCount = controlRange.length;
+ var range, doc = dom.getDocument(controlRange.item(0));
+ for (var i = 0; i < sel.rangeCount; ++i) {
+ range = api.createRange(doc);
+ range.selectNode(controlRange.item(i));
+ sel._ranges.push(range);
+ }
+ sel.isCollapsed = sel.rangeCount == 1 && sel._ranges[0].collapsed;
+ updateAnchorAndFocusFromRange(sel, sel._ranges[sel.rangeCount - 1], false);
+ }
+ }
+ }
+
+ function addRangeToControlSelection(sel, range) {
+ var controlRange = sel.docSelection.createRange();
+ var rangeElement = getSingleElementFromRange(range);
+
+ // Create a new ControlRange containing all the elements in the selected ControlRange plus the element
+ // contained by the supplied range
+ var doc = dom.getDocument(controlRange.item(0));
+ var newControlRange = dom.getBody(doc).createControlRange();
+ for (var i = 0, len = controlRange.length; i < len; ++i) {
+ newControlRange.add(controlRange.item(i));
+ }
+ try {
+ newControlRange.add(rangeElement);
+ } catch (ex) {
+ throw new Error("addRange(): Element within the specified Range could not be added to control selection (does it have layout?)");
+ }
+ newControlRange.select();
+
+ // Update the wrapped selection based on what's now in the native selection
+ updateControlSelection(sel);
+ }
+
+ var getSelectionRangeAt;
+
+ if (util.isHostMethod(testSelection, "getRangeAt")) {
+ getSelectionRangeAt = function(sel, index) {
+ try {
+ return sel.getRangeAt(index);
+ } catch(ex) {
+ return null;
+ }
+ };
+ } else if (selectionHasAnchorAndFocus) {
+ getSelectionRangeAt = function(sel) {
+ var doc = dom.getDocument(sel.anchorNode);
+ var range = api.createRange(doc);
+ range.setStart(sel.anchorNode, sel.anchorOffset);
+ range.setEnd(sel.focusNode, sel.focusOffset);
+
+ // Handle the case when the selection was selected backwards (from the end to the start in the
+ // document)
+ if (range.collapsed !== this.isCollapsed) {
+ range.setStart(sel.focusNode, sel.focusOffset);
+ range.setEnd(sel.anchorNode, sel.anchorOffset);
+ }
+
+ return range;
+ };
+ }
+
+ /**
+ * @constructor
+ */
+ function WrappedSelection(selection, docSelection, win) {
+ this.nativeSelection = selection;
+ this.docSelection = docSelection;
+ this._ranges = [];
+ this.win = win;
+ this.refresh();
+ }
+
+ api.getSelection = function(win) {
+ win = win || window;
+ var sel = win[windowPropertyName];
+ var nativeSel = getSelection(win), docSel = implementsDocSelection ? getDocSelection(win) : null;
+ if (sel) {
+ sel.nativeSelection = nativeSel;
+ sel.docSelection = docSel;
+ sel.refresh(win);
+ } else {
+ sel = new WrappedSelection(nativeSel, docSel, win);
+ win[windowPropertyName] = sel;
+ }
+ return sel;
+ };
+
+ api.getIframeSelection = function(iframeEl) {
+ return api.getSelection(dom.getIframeWindow(iframeEl));
+ };
+
+ var selProto = WrappedSelection.prototype;
+
+ function createControlSelection(sel, ranges) {
+ // Ensure that the selection becomes of type "Control"
+ var doc = dom.getDocument(ranges[0].startContainer);
+ var controlRange = dom.getBody(doc).createControlRange();
+ for (var i = 0, el; i < rangeCount; ++i) {
+ el = getSingleElementFromRange(ranges[i]);
+ try {
+ controlRange.add(el);
+ } catch (ex) {
+ throw new Error("setRanges(): Element within the one of the specified Ranges could not be added to control selection (does it have layout?)");
+ }
+ }
+ controlRange.select();
+
+ // Update the wrapped selection based on what's now in the native selection
+ updateControlSelection(sel);
+ }
+
+ // Selecting a range
+ if (!useDocumentSelection && selectionHasAnchorAndFocus && util.areHostMethods(testSelection, ["removeAllRanges", "addRange"])) {
+ selProto.removeAllRanges = function() {
+ this.nativeSelection.removeAllRanges();
+ updateEmptySelection(this);
+ };
+
+ var addRangeBackwards = function(sel, range) {
+ var doc = DomRange.getRangeDocument(range);
+ var endRange = api.createRange(doc);
+ endRange.collapseToPoint(range.endContainer, range.endOffset);
+ sel.nativeSelection.addRange(getNativeRange(endRange));
+ sel.nativeSelection.extend(range.startContainer, range.startOffset);
+ sel.refresh();
+ };
+
+ if (selectionHasRangeCount) {
+ selProto.addRange = function(range, backwards) {
+ if (implementsControlRange && implementsDocSelection && this.docSelection.type == CONTROL) {
+ addRangeToControlSelection(this, range);
+ } else {
+ if (backwards && selectionHasExtend) {
+ addRangeBackwards(this, range);
+ } else {
+ var previousRangeCount;
+ if (selectionSupportsMultipleRanges) {
+ previousRangeCount = this.rangeCount;
+ } else {
+ this.removeAllRanges();
+ previousRangeCount = 0;
+ }
+ this.nativeSelection.addRange(getNativeRange(range));
+
+ // Check whether adding the range was successful
+ this.rangeCount = this.nativeSelection.rangeCount;
+
+ if (this.rangeCount == previousRangeCount + 1) {
+ // The range was added successfully
+
+ // Check whether the range that we added to the selection is reflected in the last range extracted from
+ // the selection
+ if (api.config.checkSelectionRanges) {
+ var nativeRange = getSelectionRangeAt(this.nativeSelection, this.rangeCount - 1);
+ if (nativeRange && !DomRange.rangesEqual(nativeRange, range)) {
+ // Happens in WebKit with, for example, a selection placed at the start of a text node
+ range = new WrappedRange(nativeRange);
+ }
+ }
+ this._ranges[this.rangeCount - 1] = range;
+ updateAnchorAndFocusFromRange(this, range, selectionIsBackwards(this.nativeSelection));
+ this.isCollapsed = selectionIsCollapsed(this);
+ } else {
+ // The range was not added successfully. The simplest thing is to refresh
+ this.refresh();
+ }
+ }
+ }
+ };
+ } else {
+ selProto.addRange = function(range, backwards) {
+ if (backwards && selectionHasExtend) {
+ addRangeBackwards(this, range);
+ } else {
+ this.nativeSelection.addRange(getNativeRange(range));
+ this.refresh();
+ }
+ };
+ }
+
+ selProto.setRanges = function(ranges) {
+ if (implementsControlRange && ranges.length > 1) {
+ createControlSelection(this, ranges);
+ } else {
+ this.removeAllRanges();
+ for (var i = 0, len = ranges.length; i < len; ++i) {
+ this.addRange(ranges[i]);
+ }
+ }
+ };
+ } else if (util.isHostMethod(testSelection, "empty") && util.isHostMethod(testRange, "select") &&
+ implementsControlRange && useDocumentSelection) {
+
+ selProto.removeAllRanges = function() {
+ // Added try/catch as fix for issue #21
+ try {
+ this.docSelection.empty();
+
+ // Check for empty() not working (issue #24)
+ if (this.docSelection.type != "None") {
+ // Work around failure to empty a control selection by instead selecting a TextRange and then
+ // calling empty()
+ var doc;
+ if (this.anchorNode) {
+ doc = dom.getDocument(this.anchorNode);
+ } else if (this.docSelection.type == CONTROL) {
+ var controlRange = this.docSelection.createRange();
+ if (controlRange.length) {
+ doc = dom.getDocument(controlRange.item(0)).body.createTextRange();
+ }
+ }
+ if (doc) {
+ var textRange = doc.body.createTextRange();
+ textRange.select();
+ this.docSelection.empty();
+ }
+ }
+ } catch(ex) {}
+ updateEmptySelection(this);
+ };
+
+ selProto.addRange = function(range) {
+ if (this.docSelection.type == CONTROL) {
+ addRangeToControlSelection(this, range);
+ } else {
+ WrappedRange.rangeToTextRange(range).select();
+ this._ranges[0] = range;
+ this.rangeCount = 1;
+ this.isCollapsed = this._ranges[0].collapsed;
+ updateAnchorAndFocusFromRange(this, range, false);
+ }
+ };
+
+ selProto.setRanges = function(ranges) {
+ this.removeAllRanges();
+ var rangeCount = ranges.length;
+ if (rangeCount > 1) {
+ createControlSelection(this, ranges);
+ } else if (rangeCount) {
+ this.addRange(ranges[0]);
+ }
+ };
+ } else {
+ module.fail("No means of selecting a Range or TextRange was found");
+ return false;
+ }
+
+ selProto.getRangeAt = function(index) {
+ if (index < 0 || index >= this.rangeCount) {
+ throw new DOMException("INDEX_SIZE_ERR");
+ } else {
+ return this._ranges[index];
+ }
+ };
+
+ var refreshSelection;
+
+ if (useDocumentSelection) {
+ refreshSelection = function(sel) {
+ var range;
+ if (api.isSelectionValid(sel.win)) {
+ range = sel.docSelection.createRange();
+ } else {
+ range = dom.getBody(sel.win.document).createTextRange();
+ range.collapse(true);
+ }
+
+
+ if (sel.docSelection.type == CONTROL) {
+ updateControlSelection(sel);
+ } else if (isTextRange(range)) {
+ updateFromTextRange(sel, range);
+ } else {
+ updateEmptySelection(sel);
+ }
+ };
+ } else if (util.isHostMethod(testSelection, "getRangeAt") && typeof testSelection.rangeCount == "number") {
+ refreshSelection = function(sel) {
+ if (implementsControlRange && implementsDocSelection && sel.docSelection.type == CONTROL) {
+ updateControlSelection(sel);
+ } else {
+ sel._ranges.length = sel.rangeCount = sel.nativeSelection.rangeCount;
+ if (sel.rangeCount) {
+ for (var i = 0, len = sel.rangeCount; i < len; ++i) {
+ sel._ranges[i] = new api.WrappedRange(sel.nativeSelection.getRangeAt(i));
+ }
+ updateAnchorAndFocusFromRange(sel, sel._ranges[sel.rangeCount - 1], selectionIsBackwards(sel.nativeSelection));
+ sel.isCollapsed = selectionIsCollapsed(sel);
+ } else {
+ updateEmptySelection(sel);
+ }
+ }
+ };
+ } else if (selectionHasAnchorAndFocus && typeof testSelection.isCollapsed == BOOLEAN && typeof testRange.collapsed == BOOLEAN && api.features.implementsDomRange) {
+ refreshSelection = function(sel) {
+ var range, nativeSel = sel.nativeSelection;
+ if (nativeSel.anchorNode) {
+ range = getSelectionRangeAt(nativeSel, 0);
+ sel._ranges = [range];
+ sel.rangeCount = 1;
+ updateAnchorAndFocusFromNativeSelection(sel);
+ sel.isCollapsed = selectionIsCollapsed(sel);
+ } else {
+ updateEmptySelection(sel);
+ }
+ };
+ } else {
+ module.fail("No means of obtaining a Range or TextRange from the user's selection was found");
+ return false;
+ }
+
+ selProto.refresh = function(checkForChanges) {
+ var oldRanges = checkForChanges ? this._ranges.slice(0) : null;
+ refreshSelection(this);
+ if (checkForChanges) {
+ var i = oldRanges.length;
+ if (i != this._ranges.length) {
+ return false;
+ }
+ while (i--) {
+ if (!DomRange.rangesEqual(oldRanges[i], this._ranges[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+ };
+
+ // Removal of a single range
+ var removeRangeManually = function(sel, range) {
+ var ranges = sel.getAllRanges(), removed = false;
+ sel.removeAllRanges();
+ for (var i = 0, len = ranges.length; i < len; ++i) {
+ if (removed || range !== ranges[i]) {
+ sel.addRange(ranges[i]);
+ } else {
+ // According to the draft WHATWG Range spec, the same range may be added to the selection multiple
+ // times. removeRange should only remove the first instance, so the following ensures only the first
+ // instance is removed
+ removed = true;
+ }
+ }
+ if (!sel.rangeCount) {
+ updateEmptySelection(sel);
+ }
+ };
+
+ if (implementsControlRange) {
+ selProto.removeRange = function(range) {
+ if (this.docSelection.type == CONTROL) {
+ var controlRange = this.docSelection.createRange();
+ var rangeElement = getSingleElementFromRange(range);
+
+ // Create a new ControlRange containing all the elements in the selected ControlRange minus the
+ // element contained by the supplied range
+ var doc = dom.getDocument(controlRange.item(0));
+ var newControlRange = dom.getBody(doc).createControlRange();
+ var el, removed = false;
+ for (var i = 0, len = controlRange.length; i < len; ++i) {
+ el = controlRange.item(i);
+ if (el !== rangeElement || removed) {
+ newControlRange.add(controlRange.item(i));
+ } else {
+ removed = true;
+ }
+ }
+ newControlRange.select();
+
+ // Update the wrapped selection based on what's now in the native selection
+ updateControlSelection(this);
+ } else {
+ removeRangeManually(this, range);
+ }
+ };
+ } else {
+ selProto.removeRange = function(range) {
+ removeRangeManually(this, range);
+ };
+ }
+
+ // Detecting if a selection is backwards
+ var selectionIsBackwards;
+ if (!useDocumentSelection && selectionHasAnchorAndFocus && api.features.implementsDomRange) {
+ selectionIsBackwards = function(sel) {
+ var backwards = false;
+ if (sel.anchorNode) {
+ backwards = (dom.comparePoints(sel.anchorNode, sel.anchorOffset, sel.focusNode, sel.focusOffset) == 1);
+ }
+ return backwards;
+ };
+
+ selProto.isBackwards = function() {
+ return selectionIsBackwards(this);
+ };
+ } else {
+ selectionIsBackwards = selProto.isBackwards = function() {
+ return false;
+ };
+ }
+
+ // Selection text
+ // This is conformant to the new WHATWG DOM Range draft spec but differs from WebKit and Mozilla's implementation
+ selProto.toString = function() {
+
+ var rangeTexts = [];
+ for (var i = 0, len = this.rangeCount; i < len; ++i) {
+ rangeTexts[i] = "" + this._ranges[i];
+ }
+ return rangeTexts.join("");
+ };
+
+ function assertNodeInSameDocument(sel, node) {
+ if (sel.anchorNode && (dom.getDocument(sel.anchorNode) !== dom.getDocument(node))) {
+ throw new DOMException("WRONG_DOCUMENT_ERR");
+ }
+ }
+
+ // No current browsers conform fully to the HTML 5 draft spec for this method, so Rangy's own method is always used
+ selProto.collapse = function(node, offset) {
+ assertNodeInSameDocument(this, node);
+ var range = api.createRange(dom.getDocument(node));
+ range.collapseToPoint(node, offset);
+ this.removeAllRanges();
+ this.addRange(range);
+ this.isCollapsed = true;
+ };
+
+ selProto.collapseToStart = function() {
+ if (this.rangeCount) {
+ var range = this._ranges[0];
+ this.collapse(range.startContainer, range.startOffset);
+ } else {
+ throw new DOMException("INVALID_STATE_ERR");
+ }
+ };
+
+ selProto.collapseToEnd = function() {
+ if (this.rangeCount) {
+ var range = this._ranges[this.rangeCount - 1];
+ this.collapse(range.endContainer, range.endOffset);
+ } else {
+ throw new DOMException("INVALID_STATE_ERR");
+ }
+ };
+
+ // The HTML 5 spec is very specific on how selectAllChildren should be implemented so the native implementation is
+ // never used by Rangy.
+ selProto.selectAllChildren = function(node) {
+ assertNodeInSameDocument(this, node);
+ var range = api.createRange(dom.getDocument(node));
+ range.selectNodeContents(node);
+ this.removeAllRanges();
+ this.addRange(range);
+ };
+
+ selProto.deleteFromDocument = function() {
+ // Sepcial behaviour required for Control selections
+ if (implementsControlRange && implementsDocSelection && this.docSelection.type == CONTROL) {
+ var controlRange = this.docSelection.createRange();
+ var element;
+ while (controlRange.length) {
+ element = controlRange.item(0);
+ controlRange.remove(element);
+ element.parentNode.removeChild(element);
+ }
+ this.refresh();
+ } else if (this.rangeCount) {
+ var ranges = this.getAllRanges();
+ this.removeAllRanges();
+ for (var i = 0, len = ranges.length; i < len; ++i) {
+ ranges[i].deleteContents();
+ }
+ // The HTML5 spec says nothing about what the selection should contain after calling deleteContents on each
+ // range. Firefox moves the selection to where the final selected range was, so we emulate that
+ this.addRange(ranges[len - 1]);
+ }
+ };
+
+ // The following are non-standard extensions
+ selProto.getAllRanges = function() {
+ return this._ranges.slice(0);
+ };
+
+ selProto.setSingleRange = function(range) {
+ this.setRanges( [range] );
+ };
+
+ selProto.containsNode = function(node, allowPartial) {
+ for (var i = 0, len = this._ranges.length; i < len; ++i) {
+ if (this._ranges[i].containsNode(node, allowPartial)) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ selProto.toHtml = function() {
+ var html = "";
+ if (this.rangeCount) {
+ var container = DomRange.getRangeDocument(this._ranges[0]).createElement("div");
+ for (var i = 0, len = this._ranges.length; i < len; ++i) {
+ container.appendChild(this._ranges[i].cloneContents());
+ }
+ html = container.innerHTML;
+ }
+ return html;
+ };
+
+ function inspect(sel) {
+ var rangeInspects = [];
+ var anchor = new DomPosition(sel.anchorNode, sel.anchorOffset);
+ var focus = new DomPosition(sel.focusNode, sel.focusOffset);
+ var name = (typeof sel.getName == "function") ? sel.getName() : "Selection";
+
+ if (typeof sel.rangeCount != "undefined") {
+ for (var i = 0, len = sel.rangeCount; i < len; ++i) {
+ rangeInspects[i] = DomRange.inspect(sel.getRangeAt(i));
+ }
+ }
+ return "[" + name + "(Ranges: " + rangeInspects.join(", ") +
+ ")(anchor: " + anchor.inspect() + ", focus: " + focus.inspect() + "]";
+
+ }
+
+ selProto.getName = function() {
+ return "WrappedSelection";
+ };
+
+ selProto.inspect = function() {
+ return inspect(this);
+ };
+
+ selProto.detach = function() {
+ this.win[windowPropertyName] = null;
+ this.win = this.anchorNode = this.focusNode = null;
+ };
+
+ WrappedSelection.inspect = inspect;
+
+ api.Selection = WrappedSelection;
+
+ api.selectionPrototype = selProto;
+
+ api.addCreateMissingNativeApiListener(function(win) {
+ if (typeof win.getSelection == "undefined") {
+ win.getSelection = function() {
+ return api.getSelection(this);
+ };
+ }
+ win = null;
+ });
+});
+/*
+ Base.js, version 1.1a
+ Copyright 2006-2010, Dean Edwards
+ License: http://www.opensource.org/licenses/mit-license.php
+*/
+
+var Base = function() {
+ // dummy
+};
+
+Base.extend = function(_instance, _static) { // subclass
+ var extend = Base.prototype.extend;
+
+ // build the prototype
+ Base._prototyping = true;
+ var proto = new this;
+ extend.call(proto, _instance);
+ proto.base = function() {
+ // call this method from any other method to invoke that method's ancestor
+ };
+ delete Base._prototyping;
+
+ // create the wrapper for the constructor function
+ //var constructor = proto.constructor.valueOf(); //-dean
+ var constructor = proto.constructor;
+ var klass = proto.constructor = function() {
+ if (!Base._prototyping) {
+ if (this._constructing || this.constructor == klass) { // instantiation
+ this._constructing = true;
+ constructor.apply(this, arguments);
+ delete this._constructing;
+ } else if (arguments[0] != null) { // casting
+ return (arguments[0].extend || extend).call(arguments[0], proto);
+ }
+ }
+ };
+
+ // build the class interface
+ klass.ancestor = this;
+ klass.extend = this.extend;
+ klass.forEach = this.forEach;
+ klass.implement = this.implement;
+ klass.prototype = proto;
+ klass.toString = this.toString;
+ klass.valueOf = function(type) {
+ //return (type == "object") ? klass : constructor; //-dean
+ return (type == "object") ? klass : constructor.valueOf();
+ };
+ extend.call(klass, _static);
+ // class initialisation
+ if (typeof klass.init == "function") klass.init();
+ return klass;
+};
+
+Base.prototype = {
+ extend: function(source, value) {
+ if (arguments.length > 1) { // extending with a name/value pair
+ var ancestor = this[source];
+ if (ancestor && (typeof value == "function") && // overriding a method?
+ // the valueOf() comparison is to avoid circular references
+ (!ancestor.valueOf || ancestor.valueOf() != value.valueOf()) &&
+ /\bbase\b/.test(value)) {
+ // get the underlying method
+ var method = value.valueOf();
+ // override
+ value = function() {
+ var previous = this.base || Base.prototype.base;
+ this.base = ancestor;
+ var returnValue = method.apply(this, arguments);
+ this.base = previous;
+ return returnValue;
+ };
+ // point to the underlying method
+ value.valueOf = function(type) {
+ return (type == "object") ? value : method;
+ };
+ value.toString = Base.toString;
+ }
+ this[source] = value;
+ } else if (source) { // extending with an object literal
+ var extend = Base.prototype.extend;
+ // if this object has a customised extend method then use it
+ if (!Base._prototyping && typeof this != "function") {
+ extend = this.extend || extend;
+ }
+ var proto = {toSource: null};
+ // do the "toString" and other methods manually
+ var hidden = ["constructor", "toString", "valueOf"];
+ // if we are prototyping then include the constructor
+ var i = Base._prototyping ? 0 : 1;
+ while (key = hidden[i++]) {
+ if (source[key] != proto[key]) {
+ extend.call(this, key, source[key]);
+
+ }
+ }
+ // copy each of the source object's properties to this object
+ for (var key in source) {
+ if (!proto[key]) extend.call(this, key, source[key]);
+ }
+ }
+ return this;
+ }
+};
+
+// initialise
+Base = Base.extend({
+ constructor: function() {
+ this.extend(arguments[0]);
+ }
+}, {
+ ancestor: Object,
+ version: "1.1",
+
+ forEach: function(object, block, context) {
+ for (var key in object) {
+ if (this.prototype[key] === undefined) {
+ block.call(context, object[key], key, object);
+ }
+ }
+ },
+
+ implement: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ if (typeof arguments[i] == "function") {
+ // if it's a function, call it
+ arguments[i](this.prototype);
+ } else {
+ // add the interface using the extend method
+ this.prototype.extend(arguments[i]);
+ }
+ }
+ return this;
+ },
+
+ toString: function() {
+ return String(this.valueOf());
+ }
+});/**
+ * Detect browser support for specific features
+ */
+wysihtml5.browser = (function() {
+ var userAgent = navigator.userAgent,
+ testElement = document.createElement("div"),
+ // Browser sniffing is unfortunately needed since some behaviors are impossible to feature detect
+ isIE = userAgent.indexOf("MSIE") !== -1 && userAgent.indexOf("Opera") === -1,
+ isGecko = userAgent.indexOf("Gecko") !== -1 && userAgent.indexOf("KHTML") === -1,
+ isWebKit = userAgent.indexOf("AppleWebKit/") !== -1,
+ isChrome = userAgent.indexOf("Chrome/") !== -1,
+ isOpera = userAgent.indexOf("Opera/") !== -1;
+
+ function iosVersion(userAgent) {
+ return ((/ipad|iphone|ipod/.test(userAgent) && userAgent.match(/ os (\d+).+? like mac os x/)) || [, 0])[1];
+ }
+
+ return {
+ // Static variable needed, publicly accessible, to be able override it in unit tests
+ USER_AGENT: userAgent,
+
+ /**
+ * Exclude browsers that are not capable of displaying and handling
+ * contentEditable as desired:
+ * - iPhone, iPad (tested iOS 4.2.2) and Android (tested 2.2) refuse to make contentEditables focusable
+ * - IE < 8 create invalid markup and crash randomly from time to time
+ *
+ * @return {Boolean}
+ */
+ supported: function() {
+ var userAgent = this.USER_AGENT.toLowerCase(),
+ // Essential for making html elements editable
+ hasContentEditableSupport = "contentEditable" in testElement,
+ // Following methods are needed in order to interact with the contentEditable area
+ hasEditingApiSupport = document.execCommand && document.queryCommandSupported && document.queryCommandState,
+ // document selector apis are only supported by IE 8+, Safari 4+, Chrome and Firefox 3.5+
+ hasQuerySelectorSupport = document.querySelector && document.querySelectorAll,
+ // contentEditable is unusable in mobile browsers (tested iOS 4.2.2, Android 2.2, Opera Mobile, WebOS 3.05)
+ isIncompatibleMobileBrowser = (this.isIos() && iosVersion(userAgent) < 5) || userAgent.indexOf("opera mobi") !== -1 || userAgent.indexOf("hpwos/") !== -1;
+
+ return hasContentEditableSupport
+ && hasEditingApiSupport
+ && hasQuerySelectorSupport
+ && !isIncompatibleMobileBrowser;
+ },
+
+ isTouchDevice: function() {
+ return this.supportsEvent("touchmove");
+ },
+
+ isIos: function() {
+ var userAgent = this.USER_AGENT.toLowerCase();
+ return userAgent.indexOf("webkit") !== -1 && userAgent.indexOf("mobile") !== -1;
+ },
+
+ /**
+ * Whether the browser supports sandboxed iframes
+ * Currently only IE 6+ offers such feature <iframe security="restricted">
+ *
+ * http://msdn.microsoft.com/en-us/library/ms534622(v=vs.85).aspx
+ * http://blogs.msdn.com/b/ie/archive/2008/01/18/using-frames-more-securely.as…
+ *
+ * HTML5 sandboxed iframes are still buggy and their DOM is not reachable from the outside (except when using postMessage)
+ */
+ supportsSandboxedIframes: function() {
+ return isIE;
+ },
+
+ /**
+ * IE6+7 throw a mixed content warning when the src of an iframe
+ * is empty/unset or about:blank
+ * window.querySelector is implemented as of IE8
+ */
+ throwsMixedContentWarningWhenIframeSrcIsEmpty: function() {
+ return !("querySelector" in document);
+ },
+
+ /**
+ * Whether the caret is correctly displayed in contentEditable elements
+ * Firefox sometimes shows a huge caret in the beginning after focusing
+ */
+ displaysCaretInEmptyContentEditableCorrectly: function() {
+ return !isGecko;
+ },
+
+ /**
+ * Opera and IE are the only browsers who offer the css value
+ * in the original unit, thx to the currentStyle object
+ * All other browsers provide the computed style in px via window.getComputedStyle
+ */
+ hasCurrentStyleProperty: function() {
+ return "currentStyle" in testElement;
+ },
+
+ /**
+ * Whether the browser inserts a <br> when pressing enter in a contentEditable element
+ */
+ insertsLineBreaksOnReturn: function() {
+ return isGecko;
+ },
+
+ supportsPlaceholderAttributeOn: function(element) {
+ return "placeholder" in element;
+ },
+
+ supportsEvent: function(eventName) {
+ return "on" + eventName in testElement || (function() {
+ testElement.setAttribute("on" + eventName, "return;");
+ return typeof(testElement["on" + eventName]) === "function";
+ })();
+ },
+
+ /**
+ * Opera doesn't correctly fire focus/blur events when clicking in- and outside of iframe
+ */
+ supportsEventsInIframeCorrectly: function() {
+ return !isOpera;
+ },
+
+ /**
+ * Chrome & Safari only fire the ondrop/ondragend/... events when the ondragover event is cancelled
+ * with event.preventDefault
+ * Firefox 3.6 fires those events anyway, but the mozilla doc says that the dragover/dragenter event needs
+ * to be cancelled
+ */
+ firesOnDropOnlyWhenOnDragOverIsCancelled: function() {
+ return isWebKit || isGecko;
+ },
+
+ /**
+ * Whether the browser supports the event.dataTransfer property in a proper way
+ */
+ supportsDataTransfer: function() {
+ try {
+ // Firefox doesn't support dataTransfer in a safe way, it doesn't strip script code in the html payload (like Chrome does)
+ return isWebKit && (window.Clipboard || window.DataTransfer).prototype.getData;
+ } catch(e) {
+ return false;
+ }
+ },
+
+ /**
+ * Everything below IE9 doesn't know how to treat HTML5 tags
+ *
+ * @param {Object} context The document object on which to check HTML5 support
+ *
+ * @example
+ * wysihtml5.browser.supportsHTML5Tags(document);
+ */
+ supportsHTML5Tags: function(context) {
+ var element = context.createElement("div"),
+ html5 = "<article>foo</article>";
+ element.innerHTML = html5;
+ return element.innerHTML.toLowerCase() === html5;
+ },
+
+ /**
+ * Checks whether a document supports a certain queryCommand
+ * In particular, Opera needs a reference to a document that has a contentEditable in it's dom tree
+ * in oder to report correct results
+ *
+ * @param {Object} doc Document object on which to check for a query command
+ * @param {String} command The query command to check for
+ * @return {Boolean}
+ *
+ * @example
+ * wysihtml5.browser.supportsCommand(document, "bold");
+ */
+ supportsCommand: (function() {
+ // Following commands are supported but contain bugs in some browsers
+ var buggyCommands = {
+ // formatBlock fails with some tags (eg. <blockquote>)
+ "formatBlock": isIE,
+ // When inserting unordered or ordered lists in Firefox, Chrome or Safari, the current selection or line gets
+ // converted into a list (<ul><li>...</li></ul>, <ol><li>...</li></ol>)
+ // IE and Opera act a bit different here as they convert the entire content of the current block element into a list
+ "insertUnorderedList": isIE || isOpera || isWebKit,
+ "insertOrderedList": isIE || isOpera || isWebKit
+ };
+
+ // Firefox throws errors for queryCommandSupported, so we have to build up our own object of supported commands
+ var supported = {
+ "insertHTML": isGecko
+ };
+
+ return function(doc, command) {
+ var isBuggy = buggyCommands[command];
+ if (!isBuggy) {
+ // Firefox throws errors when invoking queryCommandSupported or queryCommandEnabled
+ try {
+ return doc.queryCommandSupported(command);
+ } catch(e1) {}
+
+ try {
+ return doc.queryCommandEnabled(command);
+ } catch(e2) {
+ return !!supported[command];
+ }
+ }
+ return false;
+ };
+ })(),
+
+ /**
+ * IE: URLs starting with:
+ * www., http://, https://, ftp://, gopher://, mailto:, new:, snews:, telnet:, wasis:, file://,
+ * nntp://, newsrc:, ldap://, ldaps://, outlook:, mic:// and url:
+ * will automatically be auto-linked when either the user inserts them via copy&paste or presses the
+ * space bar when the caret is directly after such an url.
+ * This behavior cannot easily be avoided in IE < 9 since the logic is hardcoded in the mshtml.dll
+ * (related blog post on msdn
+ * http://blogs.msdn.com/b/ieinternals/archive/2009/09/17/prevent-automatic-hy…)
+ */
+ doesAutoLinkingInContentEditable: function() {
+ return isIE;
+ },
+
+ /**
+ * As stated above, IE auto links urls typed into contentEditable elements
+ * Since IE9 it's possible to prevent this behavior
+ */
+ canDisableAutoLinking: function() {
+ return this.supportsCommand(document, "AutoUrlDetect");
+ },
+
+ /**
+ * IE leaves an empty paragraph in the contentEditable element after clearing it
+ * Chrome/Safari sometimes an empty <div>
+ */
+ clearsContentEditableCorrectly: function() {
+ return isGecko || isOpera || isWebKit;
+ },
+
+ /**
+ * IE gives wrong results for getAttribute
+ */
+ supportsGetAttributeCorrectly: function() {
+ var td = document.createElement("td");
+ return td.getAttribute("rowspan") != "1";
+ },
+
+ /**
+ * When clicking on images in IE, Opera and Firefox, they are selected, which makes it easy to interact with them.
+ * Chrome and Safari both don't support this
+ */
+ canSelectImagesInContentEditable: function() {
+ return isGecko || isIE || isOpera;
+ },
+
+ /**
+ * When the caret is in an empty list (<ul><li>|</li></ul>) which is the first child in an contentEditable container
+ * pressing backspace doesn't remove the entire list as done in other browsers
+ */
+ clearsListsInContentEditableCorrectly: function() {
+ return isGecko || isIE || isWebKit;
+ },
+
+ /**
+ * All browsers except Safari and Chrome automatically scroll the range/caret position into view
+ */
+ autoScrollsToCaret: function() {
+ return !isWebKit;
+ },
+
+ /**
+ * Check whether the browser automatically closes tags that don't need to be opened
+ */
+ autoClosesUnclosedTags: function() {
+ var clonedTestElement = testElement.cloneNode(false),
+ returnValue,
+ innerHTML;
+
+ clonedTestElement.innerHTML = "<p><div></div>";
+ innerHTML = clonedTestElement.innerHTML.toLowerCase();
+ returnValue = innerHTML === "<p></p><div></div>" || innerHTML === "<p><div></div></p>";
+
+ // Cache result by overwriting current function
+ this.autoClosesUnclosedTags = function() { return returnValue; };
+
+ return returnValue;
+ },
+
+ /**
+ * Whether the browser supports the native document.getElementsByClassName which returns live NodeLists
+ */
+ supportsNativeGetElementsByClassName: function() {
+ return String(document.getElementsByClassName).indexOf("[native code]") !== -1;
+ },
+
+ /**
+ * As of now (19.04.2011) only supported by Firefox 4 and Chrome
+ * See https://developer.mozilla.org/en/DOM/Selection/modify
+ */
+ supportsSelectionModify: function() {
+ return "getSelection" in window && "modify" in window.getSelection();
+ },
+
+ /**
+ * Whether the browser supports the classList object for fast className manipulation
+ * See https://developer.mozilla.org/en/DOM/element.classList
+ */
+ supportsClassList: function() {
+ return "classList" in testElement;
+ },
+
+ /**
+ * Opera needs a white space after a <br> in order to position the caret correctly
+ */
+ needsSpaceAfterLineBreak: function() {
+ return isOpera;
+ },
+
+ /**
+ * Whether the browser supports the speech api on the given element
+ * See http://mikepultz.com/2011/03/accessing-google-speech-api-chrome-11/
+ *
+ * @example
+ * var input = document.createElement("input");
+ * if (wysihtml5.browser.supportsSpeechApiOn(input)) {
+ * // ...
+ * }
+ */
+ supportsSpeechApiOn: function(input) {
+ var chromeVersion = userAgent.match(/Chrome\/(\d+)/) || [, 0];
+ return chromeVersion[1] >= 11 && ("onwebkitspeechchange" in input || "speech" in input);
+ },
+
+ /**
+ * IE9 crashes when setting a getter via Object.defineProperty on XMLHttpRequest or XDomainRequest
+ * See https://connect.microsoft.com/ie/feedback/details/650112
+ * or try the POC http://tifftiff.de/ie9_crash/
+ */
+ crashesWhenDefineProperty: function(property) {
+ return isIE && (property === "XMLHttpRequest" || property === "XDomainRequest");
+ },
+
+ /**
+ * IE is the only browser who fires the "focus" event not immediately when .focus() is called on an element
+ */
+ doesAsyncFocus: function() {
+ return isIE;
+ },
+
+ /**
+ * In IE it's impssible for the user and for the selection library to set the caret after an <img> when it's the lastChild in the document
+ */
+ hasProblemsSettingCaretAfterImg: function() {
+ return isIE;
+ },
+
+ hasUndoInContextMenu: function() {
+ return isGecko || isChrome || isOpera;
+ }
+ };
+})();wysihtml5.lang.array = function(arr) {
+ return {
+ /**
+ * Check whether a given object exists in an array
+ *
+ * @example
+ * wysihtml5.lang.array([1, 2]).contains(1);
+ * // => true
+ */
+ contains: function(needle) {
+ if (arr.indexOf) {
+ return arr.indexOf(needle) !== -1;
+ } else {
+ for (var i=0, length=arr.length; i<length; i++) {
+ if (arr[i] === needle) { return true; }
+ }
+ return false;
+ }
+ },
+
+ /**
+ * Substract one array from another
+ *
+ * @example
+ * wysihtml5.lang.array([1, 2, 3, 4]).without([3, 4]);
+ * // => [1, 2]
+ */
+ without: function(arrayToSubstract) {
+ arrayToSubstract = wysihtml5.lang.array(arrayToSubstract);
+ var newArr = [],
+ i = 0,
+ length = arr.length;
+ for (; i<length; i++) {
+ if (!arrayToSubstract.contains(arr[i])) {
+ newArr.push(arr[i]);
+ }
+ }
+ return newArr;
+ },
+
+ /**
+ * Return a clean native array
+ *
+ * Following will convert a Live NodeList to a proper Array
+ * @example
+ * var childNodes = wysihtml5.lang.array(document.body.childNodes).get();
+ */
+ get: function() {
+ var i = 0,
+ length = arr.length,
+ newArray = [];
+ for (; i<length; i++) {
+ newArray.push(arr[i]);
+ }
+ return newArray;
+ }
+ };
+};wysihtml5.lang.Dispatcher = Base.extend(
+ /** @scope wysihtml5.lang.Dialog.prototype */ {
+ observe: function(eventName, handler) {
+ this.events = this.events || {};
+ this.events[eventName] = this.events[eventName] || [];
+ this.events[eventName].push(handler);
+ return this;
+ },
+
+ on: function() {
+ return this.observe.apply(this, wysihtml5.lang.array(arguments).get());
+ },
+
+ fire: function(eventName, payload) {
+ this.events = this.events || {};
+ var handlers = this.events[eventName] || [],
+ i = 0;
+ for (; i<handlers.length; i++) {
+ handlers[i].call(this, payload);
+ }
+ return this;
+ },
+
+ stopObserving: function(eventName, handler) {
+ this.events = this.events || {};
+ var i = 0,
+ handlers,
+ newHandlers;
+ if (eventName) {
+ handlers = this.events[eventName] || [],
+ newHandlers = [];
+ for (; i<handlers.length; i++) {
+ if (handlers[i] !== handler && handler) {
+ newHandlers.push(handlers[i]);
+ }
+ }
+ this.events[eventName] = newHandlers;
+ } else {
+ // Clean up all events
+ this.events = {};
+ }
+ return this;
+ }
+});wysihtml5.lang.object = function(obj) {
+ return {
+ /**
+ * @example
+ * wysihtml5.lang.object({ foo: 1, bar: 1 }).merge({ bar: 2, baz: 3 }).get();
+ * // => { foo: 1, bar: 2, baz: 3 }
+ */
+ merge: function(otherObj) {
+ for (var i in otherObj) {
+ obj[i] = otherObj[i];
+ }
+ return this;
+ },
+
+ get: function() {
+ return obj;
+ },
+
+ /**
+ * @example
+ * wysihtml5.lang.object({ foo: 1 }).clone();
+ * // => { foo: 1 }
+ */
+ clone: function() {
+ var newObj = {},
+ i;
+ for (i in obj) {
+ newObj[i] = obj[i];
+ }
+ return newObj;
+ },
+
+ /**
+ * @example
+ * wysihtml5.lang.object([]).isArray();
+ * // => true
+ */
+ isArray: function() {
+ return Object.prototype.toString.call(obj) === "[object Array]";
+ }
+ };
+};(function() {
+ var WHITE_SPACE_START = /^\s+/,
+ WHITE_SPACE_END = /\s+$/;
+ wysihtml5.lang.string = function(str) {
+ str = String(str);
+ return {
+ /**
+ * @example
+ * wysihtml5.lang.string(" foo ").trim();
+ * // => "foo"
+ */
+ trim: function() {
+ return str.replace(WHITE_SPACE_START, "").replace(WHITE_SPACE_END, "");
+ },
+
+ /**
+ * @example
+ * wysihtml5.lang.string("Hello #{name}").interpolate({ name: "Christopher" });
+ * // => "Hello Christopher"
+ */
+ interpolate: function(vars) {
+ for (var i in vars) {
+ str = this.replace("#{" + i + "}").by(vars[i]);
+ }
+ return str;
+ },
+
+ /**
+ * @example
+ * wysihtml5.lang.string("Hello Tom").replace("Tom").with("Hans");
+ * // => "Hello Hans"
+ */
+ replace: function(search) {
+ return {
+ by: function(replace) {
+ return str.split(search).join(replace);
+ }
+ }
+ }
+ };
+ };
+})();/**
+ * Find urls in descendant text nodes of an element and auto-links them
+ * Inspired by http://james.padolsey.com/javascript/find-and-replace-text-with-javascript/
+ *
+ * @param {Element} element Container element in which to search for urls
+ *
+ * @example
+ * <div id="text-container">Please click here: www.google.com</div>
+ * <script>wysihtml5.dom.autoLink(document.getElementById("text-container"));</script>
+ */
+(function(wysihtml5) {
+ var /**
+ * Don't auto-link urls that are contained in the following elements:
+ */
+ IGNORE_URLS_IN = wysihtml5.lang.array(["CODE", "PRE", "A", "SCRIPT", "HEAD", "TITLE", "STYLE"]),
+ /**
+ * revision 1:
+ * /(\S+\.{1}[^\s\,\.\!]+)/g
+ *
+ * revision 2:
+ * /(\b(((https?|ftp):\/\/)|(www\.))[-A-Z0-9+&@#\/%?=~_|!:,.;\[\]]*[-A-Z0-9+&@#\/%=~_|])/gim
+ *
+ * put this in the beginning if you don't wan't to match within a word
+ * (^|[\>\(\{\[\s\>])
+ */
+ URL_REG_EXP = /((https?:\/\/|www\.)[^\s<]{3,})/gi,
+ TRAILING_CHAR_REG_EXP = /([^\w\/\-](,?))$/i,
+ MAX_DISPLAY_LENGTH = 100,
+ BRACKETS = { ")": "(", "]": "[", "}": "{" };
+
+ function autoLink(element) {
+ if (_hasParentThatShouldBeIgnored(element)) {
+ return element;
+ }
+
+ if (element === element.ownerDocument.documentElement) {
+ element = element.ownerDocument.body;
+ }
+
+ return _parseNode(element);
+ }
+
+ /**
+ * This is basically a rebuild of
+ * the rails auto_link_urls text helper
+ */
+ function _convertUrlsToLinks(str) {
+ return str.replace(URL_REG_EXP, function(match, url) {
+ var punctuation = (url.match(TRAILING_CHAR_REG_EXP) || [])[1] || "",
+ opening = BRACKETS[punctuation];
+ url = url.replace(TRAILING_CHAR_REG_EXP, "");
+
+ if (url.split(opening).length > url.split(punctuation).length) {
+ url = url + punctuation;
+ punctuation = "";
+ }
+ var realUrl = url,
+ displayUrl = url;
+ if (url.length > MAX_DISPLAY_LENGTH) {
+ displayUrl = displayUrl.substr(0, MAX_DISPLAY_LENGTH) + "...";
+ }
+ // Add http prefix if necessary
+ if (realUrl.substr(0, 4) === "www.") {
+ realUrl = "http://" + realUrl;
+ }
+
+ return '<a href="' + realUrl + '">' + displayUrl + '</a>' + punctuation;
+ });
+ }
+
+ /**
+ * Creates or (if already cached) returns a temp element
+ * for the given document object
+ */
+ function _getTempElement(context) {
+ var tempElement = context._wysihtml5_tempElement;
+ if (!tempElement) {
+ tempElement = context._wysihtml5_tempElement = context.createElement("div");
+ }
+ return tempElement;
+ }
+
+ /**
+ * Replaces the original text nodes with the newly auto-linked dom tree
+ */
+ function _wrapMatchesInNode(textNode) {
+ var parentNode = textNode.parentNode,
+ tempElement = _getTempElement(parentNode.ownerDocument);
+
+ // We need to insert an empty/temporary <span /> to fix IE quirks
+ // Elsewise IE would strip white space in the beginning
+ tempElement.innerHTML = "<span></span>" + _convertUrlsToLinks(textNode.data);
+ tempElement.removeChild(tempElement.firstChild);
+
+ while (tempElement.firstChild) {
+ // inserts tempElement.firstChild before textNode
+ parentNode.insertBefore(tempElement.firstChild, textNode);
+ }
+ parentNode.removeChild(textNode);
+ }
+
+ function _hasParentThatShouldBeIgnored(node) {
+ var nodeName;
+ while (node.parentNode) {
+ node = node.parentNode;
+ nodeName = node.nodeName;
+ if (IGNORE_URLS_IN.contains(nodeName)) {
+ return true;
+ } else if (nodeName === "body") {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ function _parseNode(element) {
+ if (IGNORE_URLS_IN.contains(element.nodeName)) {
+ return;
+ }
+
+ if (element.nodeType === wysihtml5.TEXT_NODE && element.data.match(URL_REG_EXP)) {
+ _wrapMatchesInNode(element);
+ return;
+ }
+
+ var childNodes = wysihtml5.lang.array(element.childNodes).get(),
+ childNodesLength = childNodes.length,
+ i = 0;
+
+ for (; i<childNodesLength; i++) {
+ _parseNode(childNodes[i]);
+ }
+
+ return element;
+ }
+
+ wysihtml5.dom.autoLink = autoLink;
+
+ // Reveal url reg exp to the outside
+ wysihtml5.dom.autoLink.URL_REG_EXP = URL_REG_EXP;
+})(wysihtml5);(function(wysihtml5) {
+ var supportsClassList = wysihtml5.browser.supportsClassList(),
+ api = wysihtml5.dom;
+
+ api.addClass = function(element, className) {
+ if (supportsClassList) {
+ return element.classList.add(className);
+ }
+ if (api.hasClass(element, className)) {
+ return;
+ }
+ element.className += " " + className;
+ };
+
+ api.removeClass = function(element, className) {
+ if (supportsClassList) {
+ return element.classList.remove(className);
+ }
+
+ element.className = element.className.replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), " ");
+ };
+
+ api.hasClass = function(element, className) {
+ if (supportsClassList) {
+ return element.classList.contains(className);
+ }
+
+ var elementClassName = element.className;
+ return (elementClassName.length > 0 && (elementClassName == className || new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
+ };
+})(wysihtml5);
+wysihtml5.dom.contains = (function() {
+ var documentElement = document.documentElement;
+ if (documentElement.contains) {
+ return function(container, element) {
+ if (element.nodeType !== wysihtml5.ELEMENT_NODE) {
+ element = element.parentNode;
+ }
+ return container !== element && container.contains(element);
+ };
+ } else if (documentElement.compareDocumentPosition) {
+ return function(container, element) {
+ // https://developer.mozilla.org/en/DOM/Node.compareDocumentPosition
+ return !!(container.compareDocumentPosition(element) & 16);
+ };
+ }
+})();/**
+ * Converts an HTML fragment/element into a unordered/ordered list
+ *
+ * @param {Element} element The element which should be turned into a list
+ * @param {String} listType The list type in which to convert the tree (either "ul" or "ol")
+ * @return {Element} The created list
+ *
+ * @example
+ * <!-- Assume the following dom: -->
+ * <span id="pseudo-list">
+ * eminem<br>
+ * dr. dre
+ * <div>50 Cent</div>
+ * </span>
+ *
+ * <script>
+ * wysihtml5.dom.convertToList(document.getElementById("pseudo-list"), "ul");
+ * </script>
+ *
+ * <!-- Will result in: -->
+ * <ul>
+ * <li>eminem</li>
+ * <li>dr. dre</li>
+ * <li>50 Cent</li>
+ * </ul>
+ */
+wysihtml5.dom.convertToList = (function() {
+ function _createListItem(doc, list) {
+ var listItem = doc.createElement("li");
+ list.appendChild(listItem);
+ return listItem;
+ }
+
+ function _createList(doc, type) {
+ return doc.createElement(type);
+ }
+
+ function convertToList(element, listType) {
+ if (element.nodeName === "UL" || element.nodeName === "OL" || element.nodeName === "MENU") {
+ // Already a list
+ return element;
+ }
+
+ var doc = element.ownerDocument,
+ list = _createList(doc, listType),
+ lineBreaks = element.querySelectorAll("br"),
+ lineBreaksLength = lineBreaks.length,
+ childNodes,
+ childNodesLength,
+ childNode,
+ lineBreak,
+ parentNode,
+ isBlockElement,
+ isLineBreak,
+ currentListItem,
+ i;
+
+ // First find <br> at the end of inline elements and move them behind them
+ for (i=0; i<lineBreaksLength; i++) {
+ lineBreak = lineBreaks[i];
+ while ((parentNode = lineBreak.parentNode) && parentNode !== element && parentNode.lastChild === lineBreak) {
+ if (wysihtml5.dom.getStyle("display").from(parentNode) === "block") {
+ parentNode.removeChild(lineBreak);
+ break;
+ }
+ wysihtml5.dom.insert(lineBreak).after(lineBreak.parentNode);
+ }
+ }
+
+ childNodes = wysihtml5.lang.array(element.childNodes).get();
+ childNodesLength = childNodes.length;
+
+ for (i=0; i<childNodesLength; i++) {
+ currentListItem = currentListItem || _createListItem(doc, list);
+ childNode = childNodes[i];
+ isBlockElement = wysihtml5.dom.getStyle("display").from(childNode) === "block";
+ isLineBreak = childNode.nodeName === "BR";
+
+ if (isBlockElement) {
+ // Append blockElement to current <li> if empty, otherwise create a new one
+ currentListItem = currentListItem.firstChild ? _createListItem(doc, list) : currentListItem;
+ currentListItem.appendChild(childNode);
+ currentListItem = null;
+ continue;
+ }
+
+ if (isLineBreak) {
+ // Only create a new list item in the next iteration when the current one has already content
+ currentListItem = currentListItem.firstChild ? null : currentListItem;
+ continue;
+ }
+
+ currentListItem.appendChild(childNode);
+ }
+
+ element.parentNode.replaceChild(list, element);
+ return list;
+ }
+
+ return convertToList;
+})();/**
+ * Copy a set of attributes from one element to another
+ *
+ * @param {Array} attributesToCopy List of attributes which should be copied
+ * @return {Object} Returns an object which offers the "from" method which can be invoked with the element where to
+ * copy the attributes from., this again returns an object which provides a method named "to" which can be invoked
+ * with the element where to copy the attributes to (see example)
+ *
+ * @example
+ * var textarea = document.querySelector("textarea"),
+ * div = document.querySelector("div[contenteditable=true]"),
+ * anotherDiv = document.querySelector("div.preview");
+ * wysihtml5.dom.copyAttributes(["spellcheck", "value", "placeholder"]).from(textarea).to(div).andTo(anotherDiv);
+ *
+ */
+wysihtml5.dom.copyAttributes = function(attributesToCopy) {
+ return {
+ from: function(elementToCopyFrom) {
+ return {
+ to: function(elementToCopyTo) {
+ var attribute,
+ i = 0,
+ length = attributesToCopy.length;
+ for (; i<length; i++) {
+ attribute = attributesToCopy[i];
+ if (typeof(elementToCopyFrom[attribute]) !== "undefined" && elementToCopyFrom[attribute] !== "") {
+ elementToCopyTo[attribute] = elementToCopyFrom[attribute];
+ }
+ }
+ return { andTo: arguments.callee };
+ }
+ };
+ }
+ };
+};/**
+ * Copy a set of styles from one element to another
+ * Please note that this only works properly across browsers when the element from which to copy the styles
+ * is in the dom
+ *
+ * Interesting article on how to copy styles
+ *
+ * @param {Array} stylesToCopy List of styles which should be copied
+ * @return {Object} Returns an object which offers the "from" method which can be invoked with the element where to
+ * copy the styles from., this again returns an object which provides a method named "to" which can be invoked
+ * with the element where to copy the styles to (see example)
+ *
+ * @example
+ * var textarea = document.querySelector("textarea"),
+ * div = document.querySelector("div[contenteditable=true]"),
+ * anotherDiv = document.querySelector("div.preview");
+ * wysihtml5.dom.copyStyles(["overflow-y", "width", "height"]).from(textarea).to(div).andTo(anotherDiv);
+ *
+ */
+(function(dom) {
+
+ /**
+ * Mozilla, WebKit and Opera recalculate the computed width when box-sizing: boder-box; is set
+ * So if an element has "width: 200px; -moz-box-sizing: border-box; border: 1px;" then
+ * its computed css width will be 198px
+ */
+ var BOX_SIZING_PROPERTIES = ["-webkit-box-sizing", "-moz-box-sizing", "-ms-box-sizing", "box-sizing"];
+
+ var shouldIgnoreBoxSizingBorderBox = function(element) {
+ if (hasBoxSizingBorderBox(element)) {
+ return parseInt(dom.getStyle("width").from(element), 10) < element.offsetWidth;
+ }
+ return false;
+ };
+
+ var hasBoxSizingBorderBox = function(element) {
+ var i = 0,
+ length = BOX_SIZING_PROPERTIES.length;
+ for (; i<length; i++) {
+ if (dom.getStyle(BOX_SIZING_PROPERTIES[i]).from(element) === "border-box") {
+ return BOX_SIZING_PROPERTIES[i];
+ }
+ }
+ };
+
+ dom.copyStyles = function(stylesToCopy) {
+ return {
+ from: function(element) {
+ if (shouldIgnoreBoxSizingBorderBox(element)) {
+ stylesToCopy = wysihtml5.lang.array(stylesToCopy).without(BOX_SIZING_PROPERTIES);
+ }
+
+ var cssText = "",
+ length = stylesToCopy.length,
+ i = 0,
+ property;
+ for (; i<length; i++) {
+ property = stylesToCopy[i];
+ cssText += property + ":" + dom.getStyle(property).from(element) + ";";
+ }
+
+ return {
+ to: function(element) {
+ dom.setStyles(cssText).on(element);
+ return { andTo: arguments.callee };
+ }
+ };
+ }
+ };
+ };
+})(wysihtml5.dom);/**
+ * Event Delegation
+ *
+ * @example
+ * wysihtml5.dom.delegate(document.body, "a", "click", function() {
+ * // foo
+ * });
+ */
+(function(wysihtml5) {
+
+ wysihtml5.dom.delegate = function(container, selector, eventName, handler) {
+ return wysihtml5.dom.observe(container, eventName, function(event) {
+ var target = event.target,
+ match = wysihtml5.lang.array(container.querySelectorAll(selector));
+
+ while (target && target !== container) {
+ if (match.contains(target)) {
+ handler.call(target, event);
+ break;
+ }
+ target = target.parentNode;
+ }
+ });
+ };
+
+})(wysihtml5);/**
+ * Returns the given html wrapped in a div element
+ *
+ * Fixing IE's inability to treat unknown elements (HTML5 section, article, ...) correctly
+ * when inserted via innerHTML
+ *
+ * @param {String} html The html which should be wrapped in a dom element
+ * @param {Obejct} [context] Document object of the context the html belongs to
+ *
+ * @example
+ * wysihtml5.dom.getAsDom("<article>foo</article>");
+ */
+wysihtml5.dom.getAsDom = (function() {
+
+ var _innerHTMLShiv = function(html, context) {
+ var tempElement = context.createElement("div");
+ tempElement.style.display = "none";
+ context.body.appendChild(tempElement);
+ // IE throws an exception when trying to insert <frameset></frameset> via innerHTML
+ try { tempElement.innerHTML = html; } catch(e) {}
+ context.body.removeChild(tempElement);
+ return tempElement;
+ };
+
+ /**
+ * Make sure IE supports HTML5 tags, which is accomplished by simply creating one instance of each element
+ */
+ var _ensureHTML5Compatibility = function(context) {
+ if (context._wysihtml5_supportsHTML5Tags) {
+ return;
+ }
+ for (var i=0, length=HTML5_ELEMENTS.length; i<length; i++) {
+ context.createElement(HTML5_ELEMENTS[i]);
+ }
+ context._wysihtml5_supportsHTML5Tags = true;
+ };
+
+
+ /**
+ * List of html5 tags
+ * taken from http://simon.html5.org/html5-elements
+ */
+ var HTML5_ELEMENTS = [
+ "abbr", "article", "aside", "audio", "bdi", "canvas", "command", "datalist", "details", "figcaption",
+ "figure", "footer", "header", "hgroup", "keygen", "mark", "meter", "nav", "output", "progress",
+ "rp", "rt", "ruby", "svg", "section", "source", "summary", "time", "track", "video", "wbr"
+ ];
+
+ return function(html, context) {
+ context = context || document;
+ var tempElement;
+ if (typeof(html) === "object" && html.nodeType) {
+ tempElement = context.createElement("div");
+ tempElement.appendChild(html);
+ } else if (wysihtml5.browser.supportsHTML5Tags(context)) {
+ tempElement = context.createElement("div");
+ tempElement.innerHTML = html;
+ } else {
+ _ensureHTML5Compatibility(context);
+ tempElement = _innerHTMLShiv(html, context);
+ }
+ return tempElement;
+ };
+})();/**
+ * Walks the dom tree from the given node up until it finds a match
+ * Designed for optimal performance.
+ *
+ * @param {Element} node The from which to check the parent nodes
+ * @param {Object} matchingSet Object to match against (possible properties: nodeName, className, classRegExp)
+ * @param {Number} [levels] How many parents should the function check up from the current node (defaults to 50)
+ * @return {null|Element} Returns the first element that matched the desiredNodeName(s)
+ * @example
+ * var listElement = wysihtml5.dom.getParentElement(document.querySelector("li"), { nodeName: ["MENU", "UL", "OL"] });
+ * // ... or ...
+ * var unorderedListElement = wysihtml5.dom.getParentElement(document.querySelector("li"), { nodeName: "UL" });
+ * // ... or ...
+ * var coloredElement = wysihtml5.dom.getParentElement(myTextNode, { nodeName: "SPAN", className: "wysiwyg-color-red", classRegExp: /wysiwyg-color-[a-z]/g });
+ */
+wysihtml5.dom.getParentElement = (function() {
+
+ function _isSameNodeName(nodeName, desiredNodeNames) {
+ if (!desiredNodeNames || !desiredNodeNames.length) {
+ return true;
+ }
+
+ if (typeof(desiredNodeNames) === "string") {
+ return nodeName === desiredNodeNames;
+ } else {
+ return wysihtml5.lang.array(desiredNodeNames).contains(nodeName);
+ }
+ }
+
+ function _isElement(node) {
+ return node.nodeType === wysihtml5.ELEMENT_NODE;
+ }
+
+ function _hasClassName(element, className, classRegExp) {
+ var classNames = (element.className || "").match(classRegExp) || [];
+ if (!className) {
+ return !!classNames.length;
+ }
+ return classNames[classNames.length - 1] === className;
+ }
+
+ function _getParentElementWithNodeName(node, nodeName, levels) {
+ while (levels-- && node && node.nodeName !== "BODY") {
+ if (_isSameNodeName(node.nodeName, nodeName)) {
+ return node;
+ }
+ node = node.parentNode;
+ }
+ return null;
+ }
+
+ function _getParentElementWithNodeNameAndClassName(node, nodeName, className, classRegExp, levels) {
+ while (levels-- && node && node.nodeName !== "BODY") {
+ if (_isElement(node) &&
+ _isSameNodeName(node.nodeName, nodeName) &&
+ _hasClassName(node, className, classRegExp)) {
+ return node;
+ }
+ node = node.parentNode;
+ }
+ return null;
+ }
+
+ return function(node, matchingSet, levels) {
+ levels = levels || 50; // Go max 50 nodes upwards from current node
+ if (matchingSet.className || matchingSet.classRegExp) {
+ return _getParentElementWithNodeNameAndClassName(
+ node, matchingSet.nodeName, matchingSet.className, matchingSet.classRegExp, levels
+ );
+ } else {
+ return _getParentElementWithNodeName(
+ node, matchingSet.nodeName, levels
+ );
+ }
+ };
+})();
+/**
+ * Get element's style for a specific css property
+ *
+ * @param {Element} element The element on which to retrieve the style
+ * @param {String} property The CSS property to retrieve ("float", "display", "text-align", ...)
+ *
+ * @example
+ * wysihtml5.dom.getStyle("display").from(document.body);
+ * // => "block"
+ */
+wysihtml5.dom.getStyle = (function() {
+ var stylePropertyMapping = {
+ "float": ("styleFloat" in document.createElement("div").style) ? "styleFloat" : "cssFloat"
+ },
+ REG_EXP_CAMELIZE = /\-[a-z]/g;
+
+ function camelize(str) {
+ return str.replace(REG_EXP_CAMELIZE, function(match) {
+ return match.charAt(1).toUpperCase();
+ });
+ }
+
+ return function(property) {
+ return {
+ from: function(element) {
+ if (element.nodeType !== wysihtml5.ELEMENT_NODE) {
+ return;
+ }
+
+ var doc = element.ownerDocument,
+ camelizedProperty = stylePropertyMapping[property] || camelize(property),
+ style = element.style,
+ currentStyle = element.currentStyle,
+ styleValue = style[camelizedProperty];
+ if (styleValue) {
+ return styleValue;
+ }
+
+ // currentStyle is no standard and only supported by Opera and IE but it has one important advantage over the standard-compliant
+ // window.getComputedStyle, since it returns css property values in their original unit:
+ // If you set an elements width to "50%", window.getComputedStyle will give you it's current width in px while currentStyle
+ // gives you the original "50%".
+ // Opera supports both, currentStyle and window.getComputedStyle, that's why checking for currentStyle should have higher prio
+ if (currentStyle) {
+ try {
+ return currentStyle[camelizedProperty];
+ } catch(e) {
+ //ie will occasionally fail for unknown reasons. swallowing exception
+ }
+ }
+
+ var win = doc.defaultView || doc.parentWindow,
+ needsOverflowReset = (property === "height" || property === "width") && element.nodeName === "TEXTAREA",
+ originalOverflow,
+ returnValue;
+
+ if (win.getComputedStyle) {
+ // Chrome and Safari both calculate a wrong width and height for textareas when they have scroll bars
+ // therfore we remove and restore the scrollbar and calculate the value in between
+ if (needsOverflowReset) {
+ originalOverflow = style.overflow;
+ style.overflow = "hidden";
+ }
+ returnValue = win.getComputedStyle(element, null).getPropertyValue(property);
+ if (needsOverflowReset) {
+ style.overflow = originalOverflow || "";
+ }
+ return returnValue;
+ }
+ }
+ };
+ };
+})();/**
+ * High performant way to check whether an element with a specific tag name is in the given document
+ * Optimized for being heavily executed
+ * Unleashes the power of live node lists
+ *
+ * @param {Object} doc The document object of the context where to check
+ * @param {String} tagName Upper cased tag name
+ * @example
+ * wysihtml5.dom.hasElementWithTagName(document, "IMG");
+ */
+wysihtml5.dom.hasElementWithTagName = (function() {
+ var LIVE_CACHE = {},
+ DOCUMENT_IDENTIFIER = 1;
+
+ function _getDocumentIdentifier(doc) {
+ return doc._wysihtml5_identifier || (doc._wysihtml5_identifier = DOCUMENT_IDENTIFIER++);
+ }
+
+ return function(doc, tagName) {
+ var key = _getDocumentIdentifier(doc) + ":" + tagName,
+ cacheEntry = LIVE_CACHE[key];
+ if (!cacheEntry) {
+ cacheEntry = LIVE_CACHE[key] = doc.getElementsByTagName(tagName);
+ }
+
+ return cacheEntry.length > 0;
+ };
+})();/**
+ * High performant way to check whether an element with a specific class name is in the given document
+ * Optimized for being heavily executed
+ * Unleashes the power of live node lists
+ *
+ * @param {Object} doc The document object of the context where to check
+ * @param {String} tagName Upper cased tag name
+ * @example
+ * wysihtml5.dom.hasElementWithClassName(document, "foobar");
+ */
+(function(wysihtml5) {
+ var LIVE_CACHE = {},
+ DOCUMENT_IDENTIFIER = 1;
+
+ function _getDocumentIdentifier(doc) {
+ return doc._wysihtml5_identifier || (doc._wysihtml5_identifier = DOCUMENT_IDENTIFIER++);
+ }
+
+ wysihtml5.dom.hasElementWithClassName = function(doc, className) {
+ // getElementsByClassName is not supported by IE<9
+ // but is sometimes mocked via library code (which then doesn't return live node lists)
+ if (!wysihtml5.browser.supportsNativeGetElementsByClassName()) {
+ return !!doc.querySelector("." + className);
+ }
+
+ var key = _getDocumentIdentifier(doc) + ":" + className,
+ cacheEntry = LIVE_CACHE[key];
+ if (!cacheEntry) {
+ cacheEntry = LIVE_CACHE[key] = doc.getElementsByClassName(className);
+ }
+
+ return cacheEntry.length > 0;
+ };
+})(wysihtml5);
+wysihtml5.dom.insert = function(elementToInsert) {
+ return {
+ after: function(element) {
+ element.parentNode.insertBefore(elementToInsert, element.nextSibling);
+ },
+
+ before: function(element) {
+ element.parentNode.insertBefore(elementToInsert, element);
+ },
+
+ into: function(element) {
+ element.appendChild(elementToInsert);
+ }
+ };
+};wysihtml5.dom.insertCSS = function(rules) {
+ rules = rules.join("\n");
+
+ return {
+ into: function(doc) {
+ var head = doc.head || doc.getElementsByTagName("head")[0],
+ styleElement = doc.createElement("style");
+
+ styleElement.type = "text/css";
+
+ if (styleElement.styleSheet) {
+ styleElement.styleSheet.cssText = rules;
+ } else {
+ styleElement.appendChild(doc.createTextNode(rules));
+ }
+
+ if (head) {
+ head.appendChild(styleElement);
+ }
+ }
+ };
+};/**
+ * Method to set dom events
+ *
+ * @example
+ * wysihtml5.dom.observe(iframe.contentWindow.document.body, ["focus", "blur"], function() { ... });
+ */
+wysihtml5.dom.observe = function(element, eventNames, handler) {
+ eventNames = typeof(eventNames) === "string" ? [eventNames] : eventNames;
+
+ var handlerWrapper,
+ eventName,
+ i = 0,
+ length = eventNames.length;
+
+ for (; i<length; i++) {
+ eventName = eventNames[i];
+ if (element.addEventListener) {
+ element.addEventListener(eventName, handler, false);
+ } else {
+ handlerWrapper = function(event) {
+ if (!("target" in event)) {
+ event.target = event.srcElement;
+ }
+ event.preventDefault = event.preventDefault || function() {
+ this.returnValue = false;
+ };
+ event.stopPropagation = event.stopPropagation || function() {
+ this.cancelBubble = true;
+ };
+ handler.call(element, event);
+ };
+ element.attachEvent("on" + eventName, handlerWrapper);
+ }
+ }
+
+ return {
+ stop: function() {
+ var eventName,
+ i = 0,
+ length = eventNames.length;
+ for (; i<length; i++) {
+ eventName = eventNames[i];
+ if (element.removeEventListener) {
+ element.removeEventListener(eventName, handler, false);
+ } else {
+ element.detachEvent("on" + eventName, handlerWrapper);
+ }
+ }
+ }
+ };
+};
+/**
+ * HTML Sanitizer
+ * Rewrites the HTML based on given rules
+ *
+ * @param {Element|String} elementOrHtml HTML String to be sanitized OR element whose content should be sanitized
+ * @param {Object} [rules] List of rules for rewriting the HTML, if there's no rule for an element it will
+ * be converted to a "span". Each rule is a key/value pair where key is the tag to convert, and value the
+ * desired substitution.
+ * @param {Object} context Document object in which to parse the html, needed to sandbox the parsing
+ *
+ * @return {Element|String} Depends on the elementOrHtml parameter. When html then the sanitized html as string elsewise the element.
+ *
+ * @example
+ * var userHTML = '<div id="foo" onclick="alert(1);"><p><font color="red">foo</font><script>alert(1);</script></p></div>';
+ * wysihtml5.dom.parse(userHTML, {
+ * tags {
+ * p: "div", // Rename p tags to div tags
+ * font: "span" // Rename font tags to span tags
+ * div: true, // Keep them, also possible (same result when passing: "div" or true)
+ * script: undefined // Remove script elements
+ * }
+ * });
+ * // => <div><div><span>foo bar</span></div></div>
+ *
+ * var userHTML = '<table><tbody><tr><td>I'm a table!</td></tr></tbody></table>';
+ * wysihtml5.dom.parse(userHTML);
+ * // => '<span><span><span><span>I'm a table!</span></span></span></span>'
+ *
+ * var userHTML = '<div>foobar<br>foobar</div>';
+ * wysihtml5.dom.parse(userHTML, {
+ * tags: {
+ * div: undefined,
+ * br: true
+ * }
+ * });
+ * // => ''
+ *
+ * var userHTML = '<div class="red">foo</div><div class="pink">bar</div>';
+ * wysihtml5.dom.parse(userHTML, {
+ * classes: {
+ * red: 1,
+ * green: 1
+ * },
+ * tags: {
+ * div: {
+ * rename_tag: "p"
+ * }
+ * }
+ * });
+ * // => '<p class="red">foo</p><p>bar</p>'
+ */
+wysihtml5.dom.parse = (function() {
+
+ /**
+ * It's not possible to use a XMLParser/DOMParser as HTML5 is not always well-formed XML
+ * new DOMParser().parseFromString('<img src="foo.gif">') will cause a parseError since the
+ * node isn't closed
+ *
+ * Therefore we've to use the browser's ordinary HTML parser invoked by setting innerHTML.
+ */
+ var NODE_TYPE_MAPPING = {
+ "1": _handleElement,
+ "3": _handleText
+ },
+ // Rename unknown tags to this
+ DEFAULT_NODE_NAME = "span",
+ WHITE_SPACE_REG_EXP = /\s+/,
+ defaultRules = { tags: {}, classes: {} },
+ currentRules = {};
+
+ /**
+ * Iterates over all childs of the element, recreates them, appends them into a document fragment
+ * which later replaces the entire body content
+ */
+ function parse(elementOrHtml, rules, context, cleanUp) {
+ wysihtml5.lang.object(currentRules).merge(defaultRules).merge(rules).get();
+
+ context = context || elementOrHtml.ownerDocument || document;
+ var fragment = context.createDocumentFragment(),
+ isString = typeof(elementOrHtml) === "string",
+ element,
+ newNode,
+ firstChild;
+
+ if (isString) {
+ element = wysihtml5.dom.getAsDom(elementOrHtml, context);
+ } else {
+ element = elementOrHtml;
+ }
+
+ while (element.firstChild) {
+ firstChild = element.firstChild;
+ element.removeChild(firstChild);
+ newNode = _convert(firstChild, cleanUp);
+ if (newNode) {
+ fragment.appendChild(newNode);
+ }
+ }
+
+ // Clear element contents
+ element.innerHTML = "";
+
+ // Insert new DOM tree
+ element.appendChild(fragment);
+
+ return isString ? wysihtml5.quirks.getCorrectInnerHTML(element) : element;
+ }
+
+ function _convert(oldNode, cleanUp) {
+ var oldNodeType = oldNode.nodeType,
+ oldChilds = oldNode.childNodes,
+ oldChildsLength = oldChilds.length,
+ newNode,
+ method = NODE_TYPE_MAPPING[oldNodeType],
+ i = 0;
+
+ newNode = method && method(oldNode);
+
+ if (!newNode) {
+ return null;
+ }
+
+ for (i=0; i<oldChildsLength; i++) {
+ newChild = _convert(oldChilds[i], cleanUp);
+ if (newChild) {
+ newNode.appendChild(newChild);
+ }
+ }
+
+ // Cleanup senseless <span> elements
+ if (cleanUp &&
+ newNode.childNodes.length <= 1 &&
+ newNode.nodeName.toLowerCase() === DEFAULT_NODE_NAME &&
+ !newNode.attributes.length) {
+ return newNode.firstChild;
+ }
+
+ return newNode;
+ }
+
+ function _handleElement(oldNode) {
+ var rule,
+ newNode,
+ endTag,
+ tagRules = currentRules.tags,
+ nodeName = oldNode.nodeName.toLowerCase(),
+ scopeName = oldNode.scopeName;
+
+ /**
+ * We already parsed that element
+ * ignore it! (yes, this sometimes happens in IE8 when the html is invalid)
+ */
+ if (oldNode._wysihtml5) {
+ return null;
+ }
+ oldNode._wysihtml5 = 1;
+
+ if (oldNode.className === "wysihtml5-temp") {
+ return null;
+ }
+
+ /**
+ * IE is the only browser who doesn't include the namespace in the
+ * nodeName, that's why we have to prepend it by ourselves
+ * scopeName is a proprietary IE feature
+ * read more here http://msdn.microsoft.com/en-us/library/ms534388(v=vs.85).aspx
+ */
+ if (scopeName && scopeName != "HTML") {
+ nodeName = scopeName + ":" + nodeName;
+ }
+
+ /**
+ * Repair node
+ * IE is a bit bitchy when it comes to invalid nested markup which includes unclosed tags
+ * A <p> doesn't need to be closed according HTML4-5 spec, we simply replace it with a <div> to preserve its content and layout
+ */
+ if ("outerHTML" in oldNode) {
+ if (!wysihtml5.browser.autoClosesUnclosedTags() &&
+ oldNode.nodeName === "P" &&
+ oldNode.outerHTML.slice(-4).toLowerCase() !== "</p>") {
+ nodeName = "div";
+ }
+ }
+
+ if (nodeName in tagRules) {
+ rule = tagRules[nodeName];
+ if (!rule || rule.remove) {
+ return null;
+ }
+
+ rule = typeof(rule) === "string" ? { rename_tag: rule } : rule;
+ } else if (oldNode.firstChild) {
+ rule = { rename_tag: DEFAULT_NODE_NAME };
+ } else {
+ // Remove empty unknown elements
+ return null;
+ }
+
+ newNode = oldNode.ownerDocument.createElement(rule.rename_tag || nodeName);
+ _handleAttributes(oldNode, newNode, rule);
+
+ oldNode = null;
+ return newNode;
+ }
+
+ function _handleAttributes(oldNode, newNode, rule) {
+ var attributes = {}, // fresh new set of attributes to set on newNode
+ setClass = rule.set_class, // classes to set
+ addClass = rule.add_class, // add classes based on existing attributes
+ setAttributes = rule.set_attributes, // attributes to set on the current node
+ checkAttributes = rule.check_attributes, // check/convert values of attributes
+ allowedClasses = currentRules.classes,
+ i = 0,
+ classes = [],
+ newClasses = [],
+ newUniqueClasses = [],
+ oldClasses = [],
+ classesLength,
+ newClassesLength,
+ currentClass,
+ newClass,
+ attributeName,
+ newAttributeValue,
+ method;
+
+ if (setAttributes) {
+ attributes = wysihtml5.lang.object(setAttributes).clone();
+ }
+
+ if (checkAttributes) {
+ for (attributeName in checkAttributes) {
+ method = attributeCheckMethods[checkAttributes[attributeName]];
+ if (!method) {
+ continue;
+ }
+ newAttributeValue = method(_getAttribute(oldNode, attributeName));
+ if (typeof(newAttributeValue) === "string") {
+ attributes[attributeName] = newAttributeValue;
+ }
+ }
+ }
+
+ if (setClass) {
+ classes.push(setClass);
+ }
+
+ if (addClass) {
+ for (attributeName in addClass) {
+ method = addClassMethods[addClass[attributeName]];
+ if (!method) {
+ continue;
+ }
+ newClass = method(_getAttribute(oldNode, attributeName));
+ if (typeof(newClass) === "string") {
+ classes.push(newClass);
+ }
+ }
+ }
+
+ // make sure that wysihtml5 temp class doesn't get stripped out
+ allowedClasses["_wysihtml5-temp-placeholder"] = 1;
+
+ // add old classes last
+ oldClasses = oldNode.getAttribute("class");
+ if (oldClasses) {
+ classes = classes.concat(oldClasses.split(WHITE_SPACE_REG_EXP));
+ }
+ classesLength = classes.length;
+ for (; i<classesLength; i++) {
+ currentClass = classes[i];
+ if (allowedClasses[currentClass]) {
+ newClasses.push(currentClass);
+ }
+ }
+
+ // remove duplicate entries and preserve class specificity
+ newClassesLength = newClasses.length;
+ while (newClassesLength--) {
+ currentClass = newClasses[newClassesLength];
+ if (!wysihtml5.lang.array(newUniqueClasses).contains(currentClass)) {
+ newUniqueClasses.unshift(currentClass);
+ }
+ }
+
+ if (newUniqueClasses.length) {
+ attributes["class"] = newUniqueClasses.join(" ");
+ }
+
+ // set attributes on newNode
+ for (attributeName in attributes) {
+ // Setting attributes can cause a js error in IE under certain circumstances
+ // eg. on a <img> under https when it's new attribute value is non-https
+ // TODO: Investigate this further and check for smarter handling
+ try {
+ newNode.setAttribute(attributeName, attributes[attributeName]);
+ } catch(e) {}
+ }
+
+ // IE8 sometimes loses the width/height attributes when those are set before the "src"
+ // so we make sure to set them again
+ if (attributes.src) {
+ if (typeof(attributes.width) !== "undefined") {
+ newNode.setAttribute("width", attributes.width);
+ }
+ if (typeof(attributes.height) !== "undefined") {
+ newNode.setAttribute("height", attributes.height);
+ }
+ }
+ }
+
+ /**
+ * IE gives wrong results for hasAttribute/getAttribute, for example:
+ * var td = document.createElement("td");
+ * td.getAttribute("rowspan"); // => "1" in IE
+ *
+ * Therefore we have to check the element's outerHTML for the attribute
+ */
+ var HAS_GET_ATTRIBUTE_BUG = !wysihtml5.browser.supportsGetAttributeCorrectly();
+ function _getAttribute(node, attributeName) {
+ attributeName = attributeName.toLowerCase();
+ var nodeName = node.nodeName;
+ if (nodeName == "IMG" && attributeName == "src" && _isLoadedImage(node) === true) {
+ // Get 'src' attribute value via object property since this will always contain the
+ // full absolute url (http://...)
+ // this fixes a very annoying bug in firefox (ver 3.6 & 4) and IE 8 where images copied from the same host
+ // will have relative paths, which the sanitizer strips out (see attributeCheckMethods.url)
+ return node.src;
+ } else if (HAS_GET_ATTRIBUTE_BUG && "outerHTML" in node) {
+ // Don't trust getAttribute/hasAttribute in IE 6-8, instead check the element's outerHTML
+ var outerHTML = node.outerHTML.toLowerCase(),
+ // TODO: This might not work for attributes without value: <input disabled>
+ hasAttribute = outerHTML.indexOf(" " + attributeName + "=") != -1;
+
+ return hasAttribute ? node.getAttribute(attributeName) : null;
+ } else{
+ return node.getAttribute(attributeName);
+ }
+ }
+
+ /**
+ * Check whether the given node is a proper loaded image
+ * FIXME: Returns undefined when unknown (Chrome, Safari)
+ */
+ function _isLoadedImage(node) {
+ try {
+ return node.complete && !node.mozMatchesSelector(":-moz-broken");
+ } catch(e) {
+ if (node.complete && node.readyState === "complete") {
+ return true;
+ }
+ }
+ }
+
+ function _handleText(oldNode) {
+ return oldNode.ownerDocument.createTextNode(oldNode.data);
+ }
+
+
+ // ------------ attribute checks ------------ \\
+ var attributeCheckMethods = {
+ url: (function() {
+ var REG_EXP = /^https?:\/\//i;
+ return function(attributeValue) {
+ if (!attributeValue || !attributeValue.match(REG_EXP)) {
+ return null;
+ }
+ return attributeValue.replace(REG_EXP, function(match) {
+ return match.toLowerCase();
+ });
+ };
+ })(),
+
+ alt: (function() {
+ var REG_EXP = /[^ a-z0-9_\-]/gi;
+ return function(attributeValue) {
+ if (!attributeValue) {
+ return "";
+ }
+ return attributeValue.replace(REG_EXP, "");
+ };
+ })(),
+
+ numbers: (function() {
+ var REG_EXP = /\D/g;
+ return function(attributeValue) {
+ attributeValue = (attributeValue || "").replace(REG_EXP, "");
+ return attributeValue || null;
+ };
+ })()
+ };
+
+ // ------------ class converter (converts an html attribute to a class name) ------------ \\
+ var addClassMethods = {
+ align_img: (function() {
+ var mapping = {
+ left: "wysiwyg-float-left",
+ right: "wysiwyg-float-right"
+ };
+ return function(attributeValue) {
+ return mapping[String(attributeValue).toLowerCase()];
+ };
+ })(),
+
+ align_text: (function() {
+ var mapping = {
+ left: "wysiwyg-text-align-left",
+ right: "wysiwyg-text-align-right",
+ center: "wysiwyg-text-align-center",
+ justify: "wysiwyg-text-align-justify"
+ };
+ return function(attributeValue) {
+ return mapping[String(attributeValue).toLowerCase()];
+ };
+ })(),
+
+ clear_br: (function() {
+ var mapping = {
+ left: "wysiwyg-clear-left",
+ right: "wysiwyg-clear-right",
+ both: "wysiwyg-clear-both",
+ all: "wysiwyg-clear-both"
+ };
+ return function(attributeValue) {
+ return mapping[String(attributeValue).toLowerCase()];
+ };
+ })(),
+
+ size_font: (function() {
+ var mapping = {
+ "1": "wysiwyg-font-size-xx-small",
+ "2": "wysiwyg-font-size-small",
+ "3": "wysiwyg-font-size-medium",
+ "4": "wysiwyg-font-size-large",
+ "5": "wysiwyg-font-size-x-large",
+ "6": "wysiwyg-font-size-xx-large",
+ "7": "wysiwyg-font-size-xx-large",
+ "-": "wysiwyg-font-size-smaller",
+ "+": "wysiwyg-font-size-larger"
+ };
+ return function(attributeValue) {
+ return mapping[String(attributeValue).charAt(0)];
+ };
+ })()
+ };
+
+ return parse;
+})();/**
+ * Checks for empty text node childs and removes them
+ *
+ * @param {Element} node The element in which to cleanup
+ * @example
+ * wysihtml5.dom.removeEmptyTextNodes(element);
+ */
+wysihtml5.dom.removeEmptyTextNodes = function(node) {
+ var childNode,
+ childNodes = wysihtml5.lang.array(node.childNodes).get(),
+ childNodesLength = childNodes.length,
+ i = 0;
+ for (; i<childNodesLength; i++) {
+ childNode = childNodes[i];
+ if (childNode.nodeType === wysihtml5.TEXT_NODE && childNode.data === "") {
+ childNode.parentNode.removeChild(childNode);
+ }
+ }
+};
+/**
+ * Renames an element (eg. a <div> to a <p>) and keeps its childs
+ *
+ * @param {Element} element The list element which should be renamed
+ * @param {Element} newNodeName The desired tag name
+ *
+ * @example
+ * <!-- Assume the following dom: -->
+ * <ul id="list">
+ * <li>eminem</li>
+ * <li>dr. dre</li>
+ * <li>50 Cent</li>
+ * </ul>
+ *
+ * <script>
+ * wysihtml5.dom.renameElement(document.getElementById("list"), "ol");
+ * </script>
+ *
+ * <!-- Will result in: -->
+ * <ol>
+ * <li>eminem</li>
+ * <li>dr. dre</li>
+ * <li>50 Cent</li>
+ * </ol>
+ */
+wysihtml5.dom.renameElement = function(element, newNodeName) {
+ var newElement = element.ownerDocument.createElement(newNodeName),
+ firstChild;
+ while (firstChild = element.firstChild) {
+ newElement.appendChild(firstChild);
+ }
+ wysihtml5.dom.copyAttributes(["align", "className"]).from(element).to(newElement);
+ element.parentNode.replaceChild(newElement, element);
+ return newElement;
+};/**
+ * Takes an element, removes it and replaces it with it's childs
+ *
+ * @param {Object} node The node which to replace with it's child nodes
+ * @example
+ * <div id="foo">
+ * <span>hello</span>
+ * </div>
+ * <script>
+ * // Remove #foo and replace with it's children
+ * wysihtml5.dom.replaceWithChildNodes(document.getElementById("foo"));
+ * </script>
+ */
+wysihtml5.dom.replaceWithChildNodes = function(node) {
+ if (!node.parentNode) {
+ return;
+ }
+
+ if (!node.firstChild) {
+ node.parentNode.removeChild(node);
+ return;
+ }
+
+ var fragment = node.ownerDocument.createDocumentFragment();
+ while (node.firstChild) {
+ fragment.appendChild(node.firstChild);
+ }
+ node.parentNode.replaceChild(fragment, node);
+ node = fragment = null;
+};
+/**
+ * Unwraps an unordered/ordered list
+ *
+ * @param {Element} element The list element which should be unwrapped
+ *
+ * @example
+ * <!-- Assume the following dom: -->
+ * <ul id="list">
+ * <li>eminem</li>
+ * <li>dr. dre</li>
+ * <li>50 Cent</li>
+ * </ul>
+ *
+ * <script>
+ * wysihtml5.dom.resolveList(document.getElementById("list"));
+ * </script>
+ *
+ * <!-- Will result in: -->
+ * eminem<br>
+ * dr. dre<br>
+ * 50 Cent<br>
+ */
+(function(dom) {
+ function _isBlockElement(node) {
+ return dom.getStyle("display").from(node) === "block";
+ }
+
+ function _isLineBreak(node) {
+ return node.nodeName === "BR";
+ }
+
+ function _appendLineBreak(element) {
+ var lineBreak = element.ownerDocument.createElement("br");
+ element.appendChild(lineBreak);
+ }
+
+ function resolveList(list) {
+ if (list.nodeName !== "MENU" && list.nodeName !== "UL" && list.nodeName !== "OL") {
+ return;
+ }
+
+ var doc = list.ownerDocument,
+ fragment = doc.createDocumentFragment(),
+ previousSibling = list.previousElementSibling || list.previousSibling,
+ firstChild,
+ lastChild,
+ isLastChild,
+ shouldAppendLineBreak,
+ listItem;
+
+ if (previousSibling && !_isBlockElement(previousSibling)) {
+ _appendLineBreak(fragment);
+ }
+
+ while (listItem = list.firstChild) {
+ lastChild = listItem.lastChild;
+ while (firstChild = listItem.firstChild) {
+ isLastChild = firstChild === lastChild;
+ // This needs to be done before appending it to the fragment, as it otherwise will loose style information
+ shouldAppendLineBreak = isLastChild && !_isBlockElement(firstChild) && !_isLineBreak(firstChild);
+ fragment.appendChild(firstChild);
+ if (shouldAppendLineBreak) {
+ _appendLineBreak(fragment);
+ }
+ }
+
+ listItem.parentNode.removeChild(listItem);
+ }
+ list.parentNode.replaceChild(fragment, list);
+ }
+
+ dom.resolveList = resolveList;
+})(wysihtml5.dom);/**
+ * Sandbox for executing javascript, parsing css styles and doing dom operations in a secure way
+ *
+ * Browser Compatibility:
+ * - Secure in MSIE 6+, but only when the user hasn't made changes to his security level "restricted"
+ * - Partially secure in other browsers (Firefox, Opera, Safari, Chrome, ...)
+ *
+ * Please note that this class can't benefit from the HTML5 sandbox attribute for the following reasons:
+ * - sandboxing doesn't work correctly with inlined content (src="javascript:'<html>...</html>'")
+ * - sandboxing of physical documents causes that the dom isn't accessible anymore from the outside (iframe.contentWindow, ...)
+ * - setting the "allow-same-origin" flag would fix that, but then still javascript and dom events refuse to fire
+ * - therefore the "allow-scripts" flag is needed, which then would deactivate any security, as the js executed inside the iframe
+ * can do anything as if the sandbox attribute wasn't set
+ *
+ * @param {Function} [readyCallback] Method that gets invoked when the sandbox is ready
+ * @param {Object} [config] Optional parameters
+ *
+ * @example
+ * new wysihtml5.dom.Sandbox(function(sandbox) {
+ * sandbox.getWindow().document.body.innerHTML = '<img src=foo.gif onerror="alert(document.cookie)">';
+ * });
+ */
+(function(wysihtml5) {
+ var /**
+ * Default configuration
+ */
+ doc = document,
+ /**
+ * Properties to unset/protect on the window object
+ */
+ windowProperties = [
+ "parent", "top", "opener", "frameElement", "frames",
+ "localStorage", "globalStorage", "sessionStorage", "indexedDB"
+ ],
+ /**
+ * Properties on the window object which are set to an empty function
+ */
+ windowProperties2 = [
+ "open", "close", "openDialog", "showModalDialog",
+ "alert", "confirm", "prompt",
+ "openDatabase", "postMessage",
+ "XMLHttpRequest", "XDomainRequest"
+ ],
+ /**
+ * Properties to unset/protect on the document object
+ */
+ documentProperties = [
+ "referrer",
+ "write", "open", "close"
+ ];
+
+ wysihtml5.dom.Sandbox = Base.extend(
+ /** @scope wysihtml5.dom.Sandbox.prototype */ {
+
+ constructor: function(readyCallback, config) {
+ this.callback = readyCallback || wysihtml5.EMPTY_FUNCTION;
+ this.config = wysihtml5.lang.object({}).merge(config).get();
+ this.iframe = this._createIframe();
+ },
+
+ insertInto: function(element) {
+ if (typeof(element) === "string") {
+ element = doc.getElementById(element);
+ }
+
+ element.appendChild(this.iframe);
+ },
+
+ getIframe: function() {
+ return this.iframe;
+ },
+
+ getWindow: function() {
+ this._readyError();
+ },
+
+ getDocument: function() {
+ this._readyError();
+ },
+
+ destroy: function() {
+ var iframe = this.getIframe();
+ iframe.parentNode.removeChild(iframe);
+ },
+
+ _readyError: function() {
+ throw new Error("wysihtml5.Sandbox: Sandbox iframe isn't loaded yet");
+ },
+
+ /**
+ * Creates the sandbox iframe
+ *
+ * Some important notes:
+ * - We can't use HTML5 sandbox for now:
+ * setting it causes that the iframe's dom can't be accessed from the outside
+ * Therefore we need to set the "allow-same-origin" flag which enables accessing the iframe's dom
+ * But then there's another problem, DOM events (focus, blur, change, keypress, ...) aren't fired.
+ * In order to make this happen we need to set the "allow-scripts" flag.
+ * A combination of allow-scripts and allow-same-origin is almost the same as setting no sandbox attribute at all.
+ * - Chrome & Safari, doesn't seem to support sandboxing correctly when the iframe's html is inlined (no physical document)
+ * - IE needs to have the security="restricted" attribute set before the iframe is
+ * inserted into the dom tree
+ * - Believe it or not but in IE "security" in document.createElement("iframe") is false, even
+ * though it supports it
+ * - When an iframe has security="restricted", in IE eval() & execScript() don't work anymore
+ * - IE doesn't fire the onload event when the content is inlined in the src attribute, therefore we rely
+ * on the onreadystatechange event
+ */
+ _createIframe: function() {
+ var that = this,
+ iframe = doc.createElement("iframe");
+ iframe.className = "wysihtml5-sandbox";
+ wysihtml5.dom.setAttributes({
+ "security": "restricted",
+ "allowtransparency": "true",
+ "frameborder": 0,
+ "width": 0,
+ "height": 0,
+ "marginwidth": 0,
+ "marginheight": 0
+ }).on(iframe);
+
+ // Setting the src like this prevents ssl warnings in IE6
+ if (wysihtml5.browser.throwsMixedContentWarningWhenIframeSrcIsEmpty()) {
+ iframe.src = "javascript:'<html></html>'";
+ }
+
+ iframe.onload = function() {
+ iframe.onreadystatechange = iframe.onload = null;
+ that._onLoadIframe(iframe);
+ };
+
+ iframe.onreadystatechange = function() {
+ if (/loaded|complete/.test(iframe.readyState)) {
+ iframe.onreadystatechange = iframe.onload = null;
+ that._onLoadIframe(iframe);
+ }
+ };
+
+ return iframe;
+ },
+
+ /**
+ * Callback for when the iframe has finished loading
+ */
+ _onLoadIframe: function(iframe) {
+ // don't resume when the iframe got unloaded (eg. by removing it from the dom)
+ if (!wysihtml5.dom.contains(doc.documentElement, iframe)) {
+ return;
+ }
+
+ var that = this,
+ iframeWindow = iframe.contentWindow,
+ iframeDocument = iframe.contentWindow.document,
+ charset = doc.characterSet || doc.charset || "utf-8",
+ sandboxHtml = this._getHtml({
+ charset: charset,
+ stylesheets: this.config.stylesheets
+ });
+
+ // Create the basic dom tree including proper DOCTYPE and charset
+ iframeDocument.open("text/html", "replace");
+ iframeDocument.write(sandboxHtml);
+ iframeDocument.close();
+
+ this.getWindow = function() { return iframe.contentWindow; };
+ this.getDocument = function() { return iframe.contentWindow.document; };
+
+ // Catch js errors and pass them to the parent's onerror event
+ // addEventListener("error") doesn't work properly in some browsers
+ // TODO: apparently this doesn't work in IE9!
+ iframeWindow.onerror = function(errorMessage, fileName, lineNumber) {
+ throw new Error("wysihtml5.Sandbox: " + errorMessage, fileName, lineNumber);
+ };
+
+ if (!wysihtml5.browser.supportsSandboxedIframes()) {
+ // Unset a bunch of sensitive variables
+ // Please note: This isn't hack safe!
+ // It more or less just takes care of basic attacks and prevents accidental theft of sensitive information
+ // IE is secure though, which is the most important thing, since IE is the only browser, who
+ // takes over scripts & styles into contentEditable elements when copied from external websites
+ // or applications (Microsoft Word, ...)
+ var i, length;
+ for (i=0, length=windowProperties.length; i<length; i++) {
+ this._unset(iframeWindow, windowProperties[i]);
+ }
+ for (i=0, length=windowProperties2.length; i<length; i++) {
+ this._unset(iframeWindow, windowProperties2[i], wysihtml5.EMPTY_FUNCTION);
+ }
+ for (i=0, length=documentProperties.length; i<length; i++) {
+ this._unset(iframeDocument, documentProperties[i]);
+ }
+ // This doesn't work in Safari 5
+ // See http://stackoverflow.com/questions/992461/is-it-possible-to-override-docume…
+ this._unset(iframeDocument, "cookie", "", true);
+ }
+
+ this.loaded = true;
+
+ // Trigger the callback
+ setTimeout(function() { that.callback(that); }, 0);
+ },
+
+ _getHtml: function(templateVars) {
+ var stylesheets = templateVars.stylesheets,
+ html = "",
+ i = 0,
+ length;
+ stylesheets = typeof(stylesheets) === "string" ? [stylesheets] : stylesheets;
+ if (stylesheets) {
+ length = stylesheets.length;
+ for (; i<length; i++) {
+ html += '<link rel="stylesheet" href="' + stylesheets[i] + '">';
+ }
+ }
+ templateVars.stylesheets = html;
+
+ return wysihtml5.lang.string(
+ '<!DOCTYPE html><html><head>'
+ + '<meta charset="#{charset}">#{stylesheets}</head>'
+ + '<body></body></html>'
+ ).interpolate(templateVars);
+ },
+
+ /**
+ * Method to unset/override existing variables
+ * @example
+ * // Make cookie unreadable and unwritable
+ * this._unset(document, "cookie", "", true);
+ */
+ _unset: function(object, property, value, setter) {
+ try { object[property] = value; } catch(e) {}
+
+ try { object.__defineGetter__(property, function() { return value; }); } catch(e) {}
+ if (setter) {
+ try { object.__defineSetter__(property, function() {}); } catch(e) {}
+ }
+
+ if (!wysihtml5.browser.crashesWhenDefineProperty(property)) {
+ try {
+ var config = {
+ get: function() { return value; }
+ };
+ if (setter) {
+ config.set = function() {};
+ }
+ Object.defineProperty(object, property, config);
+ } catch(e) {}
+ }
+ }
+ });
+})(wysihtml5);
+(function() {
+ var mapping = {
+ "className": "class"
+ };
+ wysihtml5.dom.setAttributes = function(attributes) {
+ return {
+ on: function(element) {
+ for (var i in attributes) {
+ element.setAttribute(mapping[i] || i, attributes[i]);
+ }
+ }
+ }
+ };
+})();wysihtml5.dom.setStyles = function(styles) {
+ return {
+ on: function(element) {
+ var style = element.style;
+ if (typeof(styles) === "string") {
+ style.cssText += ";" + styles;
+ return;
+ }
+ for (var i in styles) {
+ if (i === "float") {
+ style.cssFloat = styles[i];
+ style.styleFloat = styles[i];
+ } else {
+ style[i] = styles[i];
+ }
+ }
+ }
+ };
+};/**
+ * Simulate HTML5 placeholder attribute
+ *
+ * Needed since
+ * - div[contentEditable] elements don't support it
+ * - older browsers (such as IE8 and Firefox 3.6) don't support it at all
+ *
+ * @param {Object} parent Instance of main wysihtml5.Editor class
+ * @param {Element} view Instance of wysihtml5.views.* class
+ * @param {String} placeholderText
+ *
+ * @example
+ * wysihtml.dom.simulatePlaceholder(this, composer, "Foobar");
+ */
+(function(dom) {
+ dom.simulatePlaceholder = function(editor, view, placeholderText) {
+ var CLASS_NAME = "placeholder",
+ unset = function() {
+ if (view.hasPlaceholderSet()) {
+ view.clear();
+ }
+ dom.removeClass(view.element, CLASS_NAME);
+ },
+ set = function() {
+ if (view.isEmpty()) {
+ view.setValue(placeholderText);
+ dom.addClass(view.element, CLASS_NAME);
+ }
+ };
+
+ editor
+ .observe("set_placeholder", set)
+ .observe("unset_placeholder", unset)
+ .observe("focus:composer", unset)
+ .observe("paste:composer", unset)
+ .observe("blur:composer", set);
+
+ set();
+ };
+})(wysihtml5.dom);
+(function(dom) {
+ var documentElement = document.documentElement;
+ if ("textContent" in documentElement) {
+ dom.setTextContent = function(element, text) {
+ element.textContent = text;
+ };
+
+ dom.getTextContent = function(element) {
+ return element.textContent;
+ };
+ } else if ("innerText" in documentElement) {
+ dom.setTextContent = function(element, text) {
+ element.innerText = text;
+ };
+
+ dom.getTextContent = function(element) {
+ return element.innerText;
+ };
+ } else {
+ dom.setTextContent = function(element, text) {
+ element.nodeValue = text;
+ };
+
+ dom.getTextContent = function(element) {
+ return element.nodeValue;
+ };
+ }
+})(wysihtml5.dom);
+
+/**
+ * Fix most common html formatting misbehaviors of browsers implementation when inserting
+ * content via copy & paste contentEditable
+ *
+ * @author Christopher Blum
+ */
+wysihtml5.quirks.cleanPastedHTML = (function() {
+ // TODO: We probably need more rules here
+ var defaultRules = {
+ // When pasting underlined links <a> into a contentEditable, IE thinks, it has to insert <u> to keep the styling
+ "a u": wysihtml5.dom.replaceWithChildNodes
+ };
+
+ function cleanPastedHTML(elementOrHtml, rules, context) {
+ rules = rules || defaultRules;
+ context = context || elementOrHtml.ownerDocument || document;
+
+ var element,
+ isString = typeof(elementOrHtml) === "string",
+ method,
+ matches,
+ matchesLength,
+ i,
+ j = 0;
+ if (isString) {
+ element = wysihtml5.dom.getAsDom(elementOrHtml, context);
+ } else {
+ element = elementOrHtml;
+ }
+
+ for (i in rules) {
+ matches = element.querySelectorAll(i);
+ method = rules[i];
+ matchesLength = matches.length;
+ for (; j<matchesLength; j++) {
+ method(matches[j]);
+ }
+ }
+
+ matches = elementOrHtml = rules = null;
+
+ return isString ? element.innerHTML : element;
+ }
+
+ return cleanPastedHTML;
+})();/**
+ * IE and Opera leave an empty paragraph in the contentEditable element after clearing it
+ *
+ * @param {Object} contentEditableElement The contentEditable element to observe for clearing events
+ * @exaple
+ * wysihtml5.quirks.ensureProperClearing(myContentEditableElement);
+ */
+(function(wysihtml5) {
+ var dom = wysihtml5.dom;
+
+ wysihtml5.quirks.ensureProperClearing = (function() {
+ var clearIfNecessary = function(event) {
+ var element = this;
+ setTimeout(function() {
+ var innerHTML = element.innerHTML.toLowerCase();
+ if (innerHTML == "<p> </p>" ||
+ innerHTML == "<p> </p><p> </p>") {
+ element.innerHTML = "";
+ }
+ }, 0);
+ };
+
+ return function(composer) {
+ dom.observe(composer.element, ["cut", "keydown"], clearIfNecessary);
+ };
+ })();
+
+
+
+ /**
+ * In Opera when the caret is in the first and only item of a list (<ul><li>|</li></ul>) and the list is the first child of the contentEditable element, it's impossible to delete the list by hitting backspace
+ *
+ * @param {Object} contentEditableElement The contentEditable element to observe for clearing events
+ * @exaple
+ * wysihtml5.quirks.ensureProperClearing(myContentEditableElement);
+ */
+ wysihtml5.quirks.ensureProperClearingOfLists = (function() {
+ var ELEMENTS_THAT_CONTAIN_LI = ["OL", "UL", "MENU"];
+
+ var clearIfNecessary = function(element, contentEditableElement) {
+ if (!contentEditableElement.firstChild || !wysihtml5.lang.array(ELEMENTS_THAT_CONTAIN_LI).contains(contentEditableElement.firstChild.nodeName)) {
+ return;
+ }
+
+ var list = dom.getParentElement(element, { nodeName: ELEMENTS_THAT_CONTAIN_LI });
+ if (!list) {
+ return;
+ }
+
+ var listIsFirstChildOfContentEditable = list == contentEditableElement.firstChild;
+ if (!listIsFirstChildOfContentEditable) {
+ return;
+ }
+
+ var hasOnlyOneListItem = list.childNodes.length <= 1;
+ if (!hasOnlyOneListItem) {
+ return;
+ }
+
+ var onlyListItemIsEmpty = list.firstChild ? list.firstChild.innerHTML === "" : true;
+ if (!onlyListItemIsEmpty) {
+ return;
+ }
+
+ list.parentNode.removeChild(list);
+ };
+
+ return function(composer) {
+ dom.observe(composer.element, "keydown", function(event) {
+ if (event.keyCode !== wysihtml5.BACKSPACE_KEY) {
+ return;
+ }
+
+ var element = composer.selection.getSelectedNode();
+ clearIfNecessary(element, composer.element);
+ });
+ };
+ })();
+
+})(wysihtml5);
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=664398
+//
+// In Firefox this:
+// var d = document.createElement("div");
+// d.innerHTML ='<a href="~"></a>';
+// d.innerHTML;
+// will result in:
+// <a href="%7E"></a>
+// which is wrong
+(function(wysihtml5) {
+ var TILDE_ESCAPED = "%7E";
+ wysihtml5.quirks.getCorrectInnerHTML = function(element) {
+ var innerHTML = element.innerHTML;
+ if (innerHTML.indexOf(TILDE_ESCAPED) === -1) {
+ return innerHTML;
+ }
+
+ var elementsWithTilde = element.querySelectorAll("[href*='~'], [src*='~']"),
+ url,
+ urlToSearch,
+ length,
+ i;
+ for (i=0, length=elementsWithTilde.length; i<length; i++) {
+ url = elementsWithTilde[i].href || elementsWithTilde[i].src;
+ urlToSearch = wysihtml5.lang.string(url).replace("~").by(TILDE_ESCAPED);
+ innerHTML = wysihtml5.lang.string(innerHTML).replace(urlToSearch).by(url);
+ }
+ return innerHTML;
+ };
+})(wysihtml5);/**
+ * Some browsers don't insert line breaks when hitting return in a contentEditable element
+ * - Opera & IE insert new <p> on return
+ * - Chrome & Safari insert new <div> on return
+ * - Firefox inserts <br> on return (yippie!)
+ *
+ * @param {Element} element
+ *
+ * @example
+ * wysihtml5.quirks.insertLineBreakOnReturn(element);
+ */
+(function(wysihtml5) {
+ var dom = wysihtml5.dom,
+ USE_NATIVE_LINE_BREAK_WHEN_CARET_INSIDE_TAGS = ["LI", "P", "H1", "H2", "H3", "H4", "H5", "H6"],
+ LIST_TAGS = ["UL", "OL", "MENU"];
+
+ wysihtml5.quirks.insertLineBreakOnReturn = function(composer) {
+ function unwrap(selectedNode) {
+ var parentElement = dom.getParentElement(selectedNode, { nodeName: ["P", "DIV"] }, 2);
+ if (!parentElement) {
+ return;
+ }
+
+ var invisibleSpace = document.createTextNode(wysihtml5.INVISIBLE_SPACE);
+ dom.insert(invisibleSpace).before(parentElement);
+ dom.replaceWithChildNodes(parentElement);
+ composer.selection.selectNode(invisibleSpace);
+ }
+
+ function keyDown(event) {
+ var keyCode = event.keyCode;
+ if (event.shiftKey || (keyCode !== wysihtml5.ENTER_KEY && keyCode !== wysihtml5.BACKSPACE_KEY)) {
+ return;
+ }
+
+ var element = event.target,
+ selectedNode = composer.selection.getSelectedNode(),
+ blockElement = dom.getParentElement(selectedNode, { nodeName: USE_NATIVE_LINE_BREAK_WHEN_CARET_INSIDE_TAGS }, 4);
+ if (blockElement) {
+ // Some browsers create <p> elements after leaving a list
+ // check after keydown of backspace and return whether a <p> got inserted and unwrap it
+ if (blockElement.nodeName === "LI" && (keyCode === wysihtml5.ENTER_KEY || keyCode === wysihtml5.BACKSPACE_KEY)) {
+ setTimeout(function() {
+ var selectedNode = composer.selection.getSelectedNode(),
+ list,
+ div;
+ if (!selectedNode) {
+ return;
+ }
+
+ list = dom.getParentElement(selectedNode, {
+ nodeName: LIST_TAGS
+ }, 2);
+
+ if (list) {
+ return;
+ }
+
+ unwrap(selectedNode);
+ }, 0);
+ } else if (blockElement.nodeName.match(/H[1-6]/) && keyCode === wysihtml5.ENTER_KEY) {
+ setTimeout(function() {
+ unwrap(composer.selection.getSelectedNode());
+ }, 0);
+ }
+ return;
+ }
+
+ if (keyCode === wysihtml5.ENTER_KEY && !wysihtml5.browser.insertsLineBreaksOnReturn()) {
+ composer.commands.exec("insertLineBreak");
+ event.preventDefault();
+ }
+ }
+
+ // keypress doesn't fire when you hit backspace
+ dom.observe(composer.element.ownerDocument, "keydown", keyDown);
+ };
+})(wysihtml5);/**
+ * Force rerendering of a given element
+ * Needed to fix display misbehaviors of IE
+ *
+ * @param {Element} element The element object which needs to be rerendered
+ * @example
+ * wysihtml5.quirks.redraw(document.body);
+ */
+(function(wysihtml5) {
+ var CLASS_NAME = "wysihtml5-quirks-redraw";
+
+ wysihtml5.quirks.redraw = function(element) {
+ wysihtml5.dom.addClass(element, CLASS_NAME);
+ wysihtml5.dom.removeClass(element, CLASS_NAME);
+
+ // Following hack is needed for firefox to make sure that image resize handles are properly removed
+ try {
+ var doc = element.ownerDocument;
+ doc.execCommand("italic", false, null);
+ doc.execCommand("italic", false, null);
+ } catch(e) {}
+ };
+})(wysihtml5);/**
+ * Selection API
+ *
+ * @example
+ * var selection = new wysihtml5.Selection(editor);
+ */
+(function(wysihtml5) {
+ var dom = wysihtml5.dom;
+
+ function _getCumulativeOffsetTop(element) {
+ var top = 0;
+ if (element.parentNode) {
+ do {
+ top += element.offsetTop || 0;
+ element = element.offsetParent;
+ } while (element);
+ }
+ return top;
+ }
+
+ wysihtml5.Selection = Base.extend(
+ /** @scope wysihtml5.Selection.prototype */ {
+ constructor: function(editor) {
+ // Make sure that our external range library is initialized
+ window.rangy.init();
+
+ this.editor = editor;
+ this.composer = editor.composer;
+ this.doc = this.composer.doc;
+ },
+
+ /**
+ * Get the current selection as a bookmark to be able to later restore it
+ *
+ * @return {Object} An object that represents the current selection
+ */
+ getBookmark: function() {
+ var range = this.getRange();
+ return range && range.cloneRange();
+ },
+
+ /**
+ * Restore a selection retrieved via wysihtml5.Selection.prototype.getBookmark
+ *
+ * @param {Object} bookmark An object that represents the current selection
+ */
+ setBookmark: function(bookmark) {
+ if (!bookmark) {
+ return;
+ }
+
+ this.setSelection(bookmark);
+ },
+
+ /**
+ * Set the caret in front of the given node
+ *
+ * @param {Object} node The element or text node where to position the caret in front of
+ * @example
+ * selection.setBefore(myElement);
+ */
+ setBefore: function(node) {
+ var range = rangy.createRange(this.doc);
+ range.setStartBefore(node);
+ range.setEndBefore(node);
+ return this.setSelection(range);
+ },
+
+ /**
+ * Set the caret after the given node
+ *
+ * @param {Object} node The element or text node where to position the caret in front of
+ * @example
+ * selection.setBefore(myElement);
+ */
+ setAfter: function(node) {
+ var range = rangy.createRange(this.doc);
+ range.setStartAfter(node);
+ range.setEndAfter(node);
+ return this.setSelection(range);
+ },
+
+ /**
+ * Ability to select/mark nodes
+ *
+ * @param {Element} node The node/element to select
+ * @example
+ * selection.selectNode(document.getElementById("my-image"));
+ */
+ selectNode: function(node) {
+ var range = rangy.createRange(this.doc),
+ isElement = node.nodeType === wysihtml5.ELEMENT_NODE,
+ canHaveHTML = "canHaveHTML" in node ? node.canHaveHTML : (node.nodeName !== "IMG"),
+ content = isElement ? node.innerHTML : node.data,
+ isEmpty = (content === "" || content === wysihtml5.INVISIBLE_SPACE),
+ displayStyle = dom.getStyle("display").from(node),
+ isBlockElement = (displayStyle === "block" || displayStyle === "list-item");
+
+ if (isEmpty && isElement && canHaveHTML) {
+ // Make sure that caret is visible in node by inserting a zero width no breaking space
+ try { node.innerHTML = wysihtml5.INVISIBLE_SPACE; } catch(e) {}
+ }
+
+ if (canHaveHTML) {
+ range.selectNodeContents(node);
+ } else {
+ range.selectNode(node);
+ }
+
+ if (canHaveHTML && isEmpty && isElement) {
+ range.collapse(isBlockElement);
+ } else if (canHaveHTML && isEmpty) {
+ range.setStartAfter(node);
+ range.setEndAfter(node);
+ }
+
+ this.setSelection(range);
+ },
+
+ /**
+ * Get the node which contains the selection
+ *
+ * @param {Boolean} [controlRange] (only IE) Whether it should return the selected ControlRange element when the selection type is a "ControlRange"
+ * @return {Object} The node that contains the caret
+ * @example
+ * var nodeThatContainsCaret = selection.getSelectedNode();
+ */
+ getSelectedNode: function(controlRange) {
+ var selection,
+ range;
+
+ if (controlRange && this.doc.selection && this.doc.selection.type === "Control") {
+ range = this.doc.selection.createRange();
+ if (range && range.length) {
+ return range.item(0);
+ }
+ }
+
+ selection = this.getSelection(this.doc);
+ if (selection.focusNode === selection.anchorNode) {
+ return selection.focusNode;
+ } else {
+ range = this.getRange(this.doc);
+ return range ? range.commonAncestorContainer : this.doc.body;
+ }
+ },
+
+ executeAndRestore: function(method, restoreScrollPosition) {
+ var body = this.doc.body,
+ oldScrollTop = restoreScrollPosition && body.scrollTop,
+ oldScrollLeft = restoreScrollPosition && body.scrollLeft,
+ className = "_wysihtml5-temp-placeholder",
+ placeholderHTML = '<span class="' + className + '">' + wysihtml5.INVISIBLE_SPACE + '</span>',
+ range = this.getRange(this.doc),
+ newRange;
+
+ // Nothing selected, execute and say goodbye
+ if (!range) {
+ method(body, body);
+ return;
+ }
+
+ var node = range.createContextualFragment(placeholderHTML);
+ range.insertNode(node);
+
+ // Make sure that a potential error doesn't cause our placeholder element to be left as a placeholder
+ try {
+ method(range.startContainer, range.endContainer);
+ } catch(e3) {
+ setTimeout(function() { throw e3; }, 0);
+ }
+
+ caretPlaceholder = this.doc.querySelector("." + className);
+ if (caretPlaceholder) {
+ newRange = rangy.createRange(this.doc);
+ newRange.selectNode(caretPlaceholder);
+ newRange.deleteContents();
+ this.setSelection(newRange);
+ } else {
+ // fallback for when all hell breaks loose
+ body.focus();
+ }
+
+ if (restoreScrollPosition) {
+ body.scrollTop = oldScrollTop;
+ body.scrollLeft = oldScrollLeft;
+ }
+
+ // Remove it again, just to make sure that the placeholder is definitely out of the dom tree
+ try {
+ caretPlaceholder.parentNode.removeChild(caretPlaceholder);
+ } catch(e4) {}
+ },
+
+ /**
+ * Different approach of preserving the selection (doesn't modify the dom)
+ * Takes all text nodes in the selection and saves the selection position in the first and last one
+ */
+ executeAndRestoreSimple: function(method) {
+ var range = this.getRange(),
+ body = this.doc.body,
+ newRange,
+ firstNode,
+ lastNode,
+ textNodes,
+ rangeBackup;
+
+ // Nothing selected, execute and say goodbye
+ if (!range) {
+ method(body, body);
+ return;
+ }
+
+ textNodes = range.getNodes([3]);
+ firstNode = textNodes[0] || range.startContainer;
+ lastNode = textNodes[textNodes.length - 1] || range.endContainer;
+
+ rangeBackup = {
+ collapsed: range.collapsed,
+ startContainer: firstNode,
+ startOffset: firstNode === range.startContainer ? range.startOffset : 0,
+ endContainer: lastNode,
+ endOffset: lastNode === range.endContainer ? range.endOffset : lastNode.length
+ };
+
+ try {
+ method(range.startContainer, range.endContainer);
+ } catch(e) {
+ setTimeout(function() { throw e; }, 0);
+ }
+
+ newRange = rangy.createRange(this.doc);
+ try { newRange.setStart(rangeBackup.startContainer, rangeBackup.startOffset); } catch(e1) {}
+ try { newRange.setEnd(rangeBackup.endContainer, rangeBackup.endOffset); } catch(e2) {}
+ try { this.setSelection(newRange); } catch(e3) {}
+ },
+
+ /**
+ * Insert html at the caret position and move the cursor after the inserted html
+ *
+ * @param {String} html HTML string to insert
+ * @example
+ * selection.insertHTML("<p>foobar</p>");
+ */
+ insertHTML: function(html) {
+ var range = rangy.createRange(this.doc),
+ node = range.createContextualFragment(html),
+ lastChild = node.lastChild;
+ this.insertNode(node);
+ if (lastChild) {
+ this.setAfter(lastChild);
+ }
+ },
+
+ /**
+ * Insert a node at the caret position and move the cursor behind it
+ *
+ * @param {Object} node HTML string to insert
+ * @example
+ * selection.insertNode(document.createTextNode("foobar"));
+ */
+ insertNode: function(node) {
+ var range = this.getRange();
+ if (range) {
+ range.insertNode(node);
+ }
+ },
+
+ /**
+ * Wraps current selection with the given node
+ *
+ * @param {Object} node The node to surround the selected elements with
+ */
+ surround: function(node) {
+ var range = this.getRange();
+ if (!range) {
+ return;
+ }
+
+ try {
+ // This only works when the range boundaries are not overlapping other elements
+ range.surroundContents(node);
+ this.selectNode(node);
+ } catch(e) {
+ // fallback
+ node.appendChild(range.extractContents());
+ range.insertNode(node);
+ }
+ },
+
+ /**
+ * Scroll the current caret position into the view
+ * FIXME: This is a bit hacky, there might be a smarter way of doing this
+ *
+ * @example
+ * selection.scrollIntoView();
+ */
+ scrollIntoView: function() {
+ var doc = this.doc,
+ hasScrollBars = doc.documentElement.scrollHeight > doc.documentElement.offsetHeight,
+ tempElement = doc._wysihtml5ScrollIntoViewElement = doc._wysihtml5ScrollIntoViewElement || (function() {
+ var element = doc.createElement("span");
+ // The element needs content in order to be able to calculate it's position properly
+ element.innerHTML = wysihtml5.INVISIBLE_SPACE;
+ return element;
+ })(),
+ offsetTop;
+
+ if (hasScrollBars) {
+ this.insertNode(tempElement);
+ offsetTop = _getCumulativeOffsetTop(tempElement);
+ tempElement.parentNode.removeChild(tempElement);
+ if (offsetTop > doc.body.scrollTop) {
+ doc.body.scrollTop = offsetTop;
+ }
+ }
+ },
+
+ /**
+ * Select line where the caret is in
+ */
+ selectLine: function() {
+ if (wysihtml5.browser.supportsSelectionModify()) {
+ this._selectLine_W3C();
+ } else if (this.doc.selection) {
+ this._selectLine_MSIE();
+ }
+ },
+
+ /**
+ * See https://developer.mozilla.org/en/DOM/Selection/modify
+ */
+ _selectLine_W3C: function() {
+ var win = this.doc.defaultView,
+ selection = win.getSelection();
+ selection.modify("extend", "left", "lineboundary");
+ selection.modify("extend", "right", "lineboundary");
+ },
+
+ _selectLine_MSIE: function() {
+ var range = this.doc.selection.createRange(),
+ rangeTop = range.boundingTop,
+ rangeHeight = range.boundingHeight,
+ scrollWidth = this.doc.body.scrollWidth,
+ rangeBottom,
+ rangeEnd,
+ measureNode,
+ i,
+ j;
+
+ if (!range.moveToPoint) {
+ return;
+ }
+
+ if (rangeTop === 0) {
+ // Don't know why, but when the selection ends at the end of a line
+ // range.boundingTop is 0
+ measureNode = this.doc.createElement("span");
+ this.insertNode(measureNode);
+ rangeTop = measureNode.offsetTop;
+ measureNode.parentNode.removeChild(measureNode);
+ }
+
+ rangeTop += 1;
+
+ for (i=-10; i<scrollWidth; i+=2) {
+ try {
+ range.moveToPoint(i, rangeTop);
+ break;
+ } catch(e1) {}
+ }
+
+ // Investigate the following in order to handle multi line selections
+ // rangeBottom = rangeTop + (rangeHeight ? (rangeHeight - 1) : 0);
+ rangeBottom = rangeTop;
+ rangeEnd = this.doc.selection.createRange();
+ for (j=scrollWidth; j>=0; j--) {
+ try {
+ rangeEnd.moveToPoint(j, rangeBottom);
+ break;
+ } catch(e2) {}
+ }
+
+ range.setEndPoint("EndToEnd", rangeEnd);
+ range.select();
+ },
+
+ getText: function() {
+ var selection = this.getSelection();
+ return selection ? selection.toString() : "";
+ },
+
+ getNodes: function(nodeType, filter) {
+ var range = this.getRange();
+ if (range) {
+ return range.getNodes([nodeType], filter);
+ } else {
+ return [];
+ }
+ },
+
+ getRange: function() {
+ var selection = this.getSelection();
+ return selection && selection.rangeCount && selection.getRangeAt(0);
+ },
+
+ getSelection: function() {
+ return rangy.getSelection(this.doc.defaultView || this.doc.parentWindow);
+ },
+
+ setSelection: function(range) {
+ var win = this.doc.defaultView || this.doc.parentWindow,
+ selection = rangy.getSelection(win);
+ return selection.setSingleRange(range);
+ }
+ });
+
+})(wysihtml5);
+/**
+ * Inspired by the rangy CSS Applier module written by Tim Down and licensed under the MIT license.
+ * http://code.google.com/p/rangy/
+ *
+ * changed in order to be able ...
+ * - to use custom tags
+ * - to detect and replace similar css classes via reg exp
+ */
+(function(wysihtml5, rangy) {
+ var defaultTagName = "span";
+
+ var REG_EXP_WHITE_SPACE = /\s+/g;
+
+ function hasClass(el, cssClass, regExp) {
+ if (!el.className) {
+ return false;
+ }
+
+ var matchingClassNames = el.className.match(regExp) || [];
+ return matchingClassNames[matchingClassNames.length - 1] === cssClass;
+ }
+
+ function addClass(el, cssClass, regExp) {
+ if (el.className) {
+ removeClass(el, regExp);
+ el.className += " " + cssClass;
+ } else {
+ el.className = cssClass;
+ }
+ }
+
+ function removeClass(el, regExp) {
+ if (el.className) {
+ el.className = el.className.replace(regExp, "");
+ }
+ }
+
+ function hasSameClasses(el1, el2) {
+ return el1.className.replace(REG_EXP_WHITE_SPACE, " ") == el2.className.replace(REG_EXP_WHITE_SPACE, " ");
+ }
+
+ function replaceWithOwnChildren(el) {
+ var parent = el.parentNode;
+ while (el.firstChild) {
+ parent.insertBefore(el.firstChild, el);
+ }
+ parent.removeChild(el);
+ }
+
+ function elementsHaveSameNonClassAttributes(el1, el2) {
+ if (el1.attributes.length != el2.attributes.length) {
+ return false;
+ }
+ for (var i = 0, len = el1.attributes.length, attr1, attr2, name; i < len; ++i) {
+ attr1 = el1.attributes[i];
+ name = attr1.name;
+ if (name != "class") {
+ attr2 = el2.attributes.getNamedItem(name);
+ if (attr1.specified != attr2.specified) {
+ return false;
+ }
+ if (attr1.specified && attr1.nodeValue !== attr2.nodeValue) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ function isSplitPoint(node, offset) {
+ if (rangy.dom.isCharacterDataNode(node)) {
+ if (offset == 0) {
+ return !!node.previousSibling;
+ } else if (offset == node.length) {
+ return !!node.nextSibling;
+ } else {
+ return true;
+ }
+ }
+
+ return offset > 0 && offset < node.childNodes.length;
+ }
+
+ function splitNodeAt(node, descendantNode, descendantOffset) {
+ var newNode;
+ if (rangy.dom.isCharacterDataNode(descendantNode)) {
+ if (descendantOffset == 0) {
+ descendantOffset = rangy.dom.getNodeIndex(descendantNode);
+ descendantNode = descendantNode.parentNode;
+ } else if (descendantOffset == descendantNode.length) {
+ descendantOffset = rangy.dom.getNodeIndex(descendantNode) + 1;
+ descendantNode = descendantNode.parentNode;
+ } else {
+ newNode = rangy.dom.splitDataNode(descendantNode, descendantOffset);
+ }
+ }
+ if (!newNode) {
+ newNode = descendantNode.cloneNode(false);
+ if (newNode.id) {
+ newNode.removeAttribute("id");
+ }
+ var child;
+ while ((child = descendantNode.childNodes[descendantOffset])) {
+ newNode.appendChild(child);
+ }
+ rangy.dom.insertAfter(newNode, descendantNode);
+ }
+ return (descendantNode == node) ? newNode : splitNodeAt(node, newNode.parentNode, rangy.dom.getNodeIndex(newNode));
+ }
+
+ function Merge(firstNode) {
+ this.isElementMerge = (firstNode.nodeType == wysihtml5.ELEMENT_NODE);
+ this.firstTextNode = this.isElementMerge ? firstNode.lastChild : firstNode;
+ this.textNodes = [this.firstTextNode];
+ }
+
+ Merge.prototype = {
+ doMerge: function() {
+ var textBits = [], textNode, parent, text;
+ for (var i = 0, len = this.textNodes.length; i < len; ++i) {
+ textNode = this.textNodes[i];
+ parent = textNode.parentNode;
+ textBits[i] = textNode.data;
+ if (i) {
+ parent.removeChild(textNode);
+ if (!parent.hasChildNodes()) {
+ parent.parentNode.removeChild(parent);
+ }
+ }
+ }
+ this.firstTextNode.data = text = textBits.join("");
+ return text;
+ },
+
+ getLength: function() {
+ var i = this.textNodes.length, len = 0;
+ while (i--) {
+ len += this.textNodes[i].length;
+ }
+ return len;
+ },
+
+ toString: function() {
+ var textBits = [];
+ for (var i = 0, len = this.textNodes.length; i < len; ++i) {
+ textBits[i] = "'" + this.textNodes[i].data + "'";
+ }
+ return "[Merge(" + textBits.join(",") + ")]";
+ }
+ };
+
+ function HTMLApplier(tagNames, cssClass, similarClassRegExp, normalize) {
+ this.tagNames = tagNames || [defaultTagName];
+ this.cssClass = cssClass || "";
+ this.similarClassRegExp = similarClassRegExp;
+ this.normalize = normalize;
+ this.applyToAnyTagName = false;
+ }
+
+ HTMLApplier.prototype = {
+ getAncestorWithClass: function(node) {
+ var cssClassMatch;
+ while (node) {
+ cssClassMatch = this.cssClass ? hasClass(node, this.cssClass, this.similarClassRegExp) : true;
+ if (node.nodeType == wysihtml5.ELEMENT_NODE && rangy.dom.arrayContains(this.tagNames, node.tagName.toLowerCase()) && cssClassMatch) {
+ return node;
+ }
+ node = node.parentNode;
+ }
+ return false;
+ },
+
+ // Normalizes nodes after applying a CSS class to a Range.
+ postApply: function(textNodes, range) {
+ var firstNode = textNodes[0], lastNode = textNodes[textNodes.length - 1];
+
+ var merges = [], currentMerge;
+
+ var rangeStartNode = firstNode, rangeEndNode = lastNode;
+ var rangeStartOffset = 0, rangeEndOffset = lastNode.length;
+
+ var textNode, precedingTextNode;
+
+ for (var i = 0, len = textNodes.length; i < len; ++i) {
+ textNode = textNodes[i];
+ precedingTextNode = this.getAdjacentMergeableTextNode(textNode.parentNode, false);
+ if (precedingTextNode) {
+ if (!currentMerge) {
+ currentMerge = new Merge(precedingTextNode);
+ merges.push(currentMerge);
+ }
+ currentMerge.textNodes.push(textNode);
+ if (textNode === firstNode) {
+ rangeStartNode = currentMerge.firstTextNode;
+ rangeStartOffset = rangeStartNode.length;
+ }
+ if (textNode === lastNode) {
+ rangeEndNode = currentMerge.firstTextNode;
+ rangeEndOffset = currentMerge.getLength();
+ }
+ } else {
+ currentMerge = null;
+ }
+ }
+
+ // Test whether the first node after the range needs merging
+ var nextTextNode = this.getAdjacentMergeableTextNode(lastNode.parentNode, true);
+ if (nextTextNode) {
+ if (!currentMerge) {
+ currentMerge = new Merge(lastNode);
+ merges.push(currentMerge);
+ }
+ currentMerge.textNodes.push(nextTextNode);
+ }
+
+ // Do the merges
+ if (merges.length) {
+ for (i = 0, len = merges.length; i < len; ++i) {
+ merges[i].doMerge();
+ }
+ // Set the range boundaries
+ range.setStart(rangeStartNode, rangeStartOffset);
+ range.setEnd(rangeEndNode, rangeEndOffset);
+ }
+ },
+
+ getAdjacentMergeableTextNode: function(node, forward) {
+ var isTextNode = (node.nodeType == wysihtml5.TEXT_NODE);
+ var el = isTextNode ? node.parentNode : node;
+ var adjacentNode;
+ var propName = forward ? "nextSibling" : "previousSibling";
+ if (isTextNode) {
+ // Can merge if the node's previous/next sibling is a text node
+ adjacentNode = node[propName];
+ if (adjacentNode && adjacentNode.nodeType == wysihtml5.TEXT_NODE) {
+ return adjacentNode;
+ }
+ } else {
+ // Compare element with its sibling
+ adjacentNode = el[propName];
+ if (adjacentNode && this.areElementsMergeable(node, adjacentNode)) {
+ return adjacentNode[forward ? "firstChild" : "lastChild"];
+ }
+ }
+ return null;
+ },
+
+ areElementsMergeable: function(el1, el2) {
+ return rangy.dom.arrayContains(this.tagNames, (el1.tagName || "").toLowerCase())
+ && rangy.dom.arrayContains(this.tagNames, (el2.tagName || "").toLowerCase())
+ && hasSameClasses(el1, el2)
+ && elementsHaveSameNonClassAttributes(el1, el2);
+ },
+
+ createContainer: function(doc) {
+ var el = doc.createElement(this.tagNames[0]);
+ if (this.cssClass) {
+ el.className = this.cssClass;
+ }
+ return el;
+ },
+
+ applyToTextNode: function(textNode) {
+ var parent = textNode.parentNode;
+ if (parent.childNodes.length == 1 && rangy.dom.arrayContains(this.tagNames, parent.tagName.toLowerCase())) {
+ if (this.cssClass) {
+ addClass(parent, this.cssClass, this.similarClassRegExp);
+ }
+ } else {
+ var el = this.createContainer(rangy.dom.getDocument(textNode));
+ textNode.parentNode.insertBefore(el, textNode);
+ el.appendChild(textNode);
+ }
+ },
+
+ isRemovable: function(el) {
+ return rangy.dom.arrayContains(this.tagNames, el.tagName.toLowerCase()) && wysihtml5.lang.string(el.className).trim() == this.cssClass;
+ },
+
+ undoToTextNode: function(textNode, range, ancestorWithClass) {
+ if (!range.containsNode(ancestorWithClass)) {
+ // Split out the portion of the ancestor from which we can remove the CSS class
+ var ancestorRange = range.cloneRange();
+ ancestorRange.selectNode(ancestorWithClass);
+
+ if (ancestorRange.isPointInRange(range.endContainer, range.endOffset) && isSplitPoint(range.endContainer, range.endOffset)) {
+ splitNodeAt(ancestorWithClass, range.endContainer, range.endOffset);
+ range.setEndAfter(ancestorWithClass);
+ }
+ if (ancestorRange.isPointInRange(range.startContainer, range.startOffset) && isSplitPoint(range.startContainer, range.startOffset)) {
+ ancestorWithClass = splitNodeAt(ancestorWithClass, range.startContainer, range.startOffset);
+ }
+ }
+
+ if (this.similarClassRegExp) {
+ removeClass(ancestorWithClass, this.similarClassRegExp);
+ }
+ if (this.isRemovable(ancestorWithClass)) {
+ replaceWithOwnChildren(ancestorWithClass);
+ }
+ },
+
+ applyToRange: function(range) {
+ var textNodes = range.getNodes([wysihtml5.TEXT_NODE]);
+ if (!textNodes.length) {
+ try {
+ var node = this.createContainer(range.endContainer.ownerDocument);
+ range.surroundContents(node);
+ this.selectNode(range, node);
+ return;
+ } catch(e) {}
+ }
+
+ range.splitBoundaries();
+ textNodes = range.getNodes([wysihtml5.TEXT_NODE]);
+
+ if (textNodes.length) {
+ var textNode;
+
+ for (var i = 0, len = textNodes.length; i < len; ++i) {
+ textNode = textNodes[i];
+ if (!this.getAncestorWithClass(textNode)) {
+ this.applyToTextNode(textNode);
+ }
+ }
+
+ range.setStart(textNodes[0], 0);
+ textNode = textNodes[textNodes.length - 1];
+ range.setEnd(textNode, textNode.length);
+
+ if (this.normalize) {
+ this.postApply(textNodes, range);
+ }
+ }
+ },
+
+ undoToRange: function(range) {
+ var textNodes = range.getNodes([wysihtml5.TEXT_NODE]), textNode, ancestorWithClass;
+ if (textNodes.length) {
+ range.splitBoundaries();
+ textNodes = range.getNodes([wysihtml5.TEXT_NODE]);
+ } else {
+ var doc = range.endContainer.ownerDocument,
+ node = doc.createTextNode(wysihtml5.INVISIBLE_SPACE);
+ range.insertNode(node);
+ range.selectNode(node);
+ textNodes = [node];
+ }
+
+ for (var i = 0, len = textNodes.length; i < len; ++i) {
+ textNode = textNodes[i];
+ ancestorWithClass = this.getAncestorWithClass(textNode);
+ if (ancestorWithClass) {
+ this.undoToTextNode(textNode, range, ancestorWithClass);
+ }
+ }
+
+ if (len == 1) {
+ this.selectNode(range, textNodes[0]);
+ } else {
+ range.setStart(textNodes[0], 0);
+ textNode = textNodes[textNodes.length - 1];
+ range.setEnd(textNode, textNode.length);
+
+ if (this.normalize) {
+ this.postApply(textNodes, range);
+ }
+ }
+ },
+
+ selectNode: function(range, node) {
+ var isElement = node.nodeType === wysihtml5.ELEMENT_NODE,
+ canHaveHTML = "canHaveHTML" in node ? node.canHaveHTML : true,
+ content = isElement ? node.innerHTML : node.data,
+ isEmpty = (content === "" || content === wysihtml5.INVISIBLE_SPACE);
+
+ if (isEmpty && isElement && canHaveHTML) {
+ // Make sure that caret is visible in node by inserting a zero width no breaking space
+ try { node.innerHTML = wysihtml5.INVISIBLE_SPACE; } catch(e) {}
+ }
+ range.selectNodeContents(node);
+ if (isEmpty && isElement) {
+ range.collapse(false);
+ } else if (isEmpty) {
+ range.setStartAfter(node);
+ range.setEndAfter(node);
+ }
+ },
+
+ getTextSelectedByRange: function(textNode, range) {
+ var textRange = range.cloneRange();
+ textRange.selectNodeContents(textNode);
+
+ var intersectionRange = textRange.intersection(range);
+ var text = intersectionRange ? intersectionRange.toString() : "";
+ textRange.detach();
+
+ return text;
+ },
+
+ isAppliedToRange: function(range) {
+ var ancestors = [],
+ ancestor,
+ textNodes = range.getNodes([wysihtml5.TEXT_NODE]);
+ if (!textNodes.length) {
+ ancestor = this.getAncestorWithClass(range.startContainer);
+ return ancestor ? [ancestor] : false;
+ }
+
+ for (var i = 0, len = textNodes.length, selectedText; i < len; ++i) {
+ selectedText = this.getTextSelectedByRange(textNodes[i], range);
+ ancestor = this.getAncestorWithClass(textNodes[i]);
+ if (selectedText != "" && !ancestor) {
+ return false;
+ } else {
+ ancestors.push(ancestor);
+ }
+ }
+ return ancestors;
+ },
+
+ toggleRange: function(range) {
+ if (this.isAppliedToRange(range)) {
+ this.undoToRange(range);
+ } else {
+ this.applyToRange(range);
+ }
+ }
+ };
+
+ wysihtml5.selection.HTMLApplier = HTMLApplier;
+
+})(wysihtml5, rangy);/**
+ * Rich Text Query/Formatting Commands
+ *
+ * @example
+ * var commands = new wysihtml5.Commands(editor);
+ */
+wysihtml5.Commands = Base.extend(
+ /** @scope wysihtml5.Commands.prototype */ {
+ constructor: function(editor) {
+ this.editor = editor;
+ this.composer = editor.composer;
+ this.doc = this.composer.doc;
+ },
+
+ /**
+ * Check whether the browser supports the given command
+ *
+ * @param {String} command The command string which to check (eg. "bold", "italic", "insertUnorderedList")
+ * @example
+ * commands.supports("createLink");
+ */
+ support: function(command) {
+ return wysihtml5.browser.supportsCommand(this.doc, command);
+ },
+
+ /**
+ * Check whether the browser supports the given command
+ *
+ * @param {String} command The command string which to execute (eg. "bold", "italic", "insertUnorderedList")
+ * @param {String} [value] The command value parameter, needed for some commands ("createLink", "insertImage", ...), optional for commands that don't require one ("bold", "underline", ...)
+ * @example
+ * commands.exec("insertImage", "http://a1.twimg.com/profile_images/113868655/schrei_twitter_reasonably_smal…");
+ */
+ exec: function(command, value) {
+ var obj = wysihtml5.commands[command],
+ args = wysihtml5.lang.array(arguments).get(),
+ method = obj && obj.exec,
+ result = null;
+
+ this.editor.fire("beforecommand:composer");
+
+ if (method) {
+ args.unshift(this.composer);
+ result = method.apply(obj, args);
+ } else {
+ try {
+ // try/catch for buggy firefox
+ result = this.doc.execCommand(command, false, value);
+ } catch(e) {}
+ }
+
+ this.editor.fire("aftercommand:composer");
+ return result;
+ },
+
+ /**
+ * Check whether the current command is active
+ * If the caret is within a bold text, then calling this with command "bold" should return true
+ *
+ * @param {String} command The command string which to check (eg. "bold", "italic", "insertUnorderedList")
+ * @param {String} [commandValue] The command value parameter (eg. for "insertImage" the image src)
+ * @return {Boolean} Whether the command is active
+ * @example
+ * var isCurrentSelectionBold = commands.state("bold");
+ */
+ state: function(command, commandValue) {
+ var obj = wysihtml5.commands[command],
+ args = wysihtml5.lang.array(arguments).get(),
+ method = obj && obj.state;
+ if (method) {
+ args.unshift(this.composer);
+ return method.apply(obj, args);
+ } else {
+ try {
+ // try/catch for buggy firefox
+ return this.doc.queryCommandState(command);
+ } catch(e) {
+ return false;
+ }
+ }
+ },
+
+ /**
+ * Get the current command's value
+ *
+ * @param {String} command The command string which to check (eg. "formatBlock")
+ * @return {String} The command value
+ * @example
+ * var currentBlockElement = commands.value("formatBlock");
+ */
+ value: function(command) {
+ var obj = wysihtml5.commands[command],
+ method = obj && obj.value;
+ if (method) {
+ return method.call(obj, this.composer, command);
+ } else {
+ try {
+ // try/catch for buggy firefox
+ return this.doc.queryCommandValue(command);
+ } catch(e) {
+ return null;
+ }
+ }
+ }
+});
+(function(wysihtml5) {
+ var undef;
+
+ wysihtml5.commands.bold = {
+ exec: function(composer, command) {
+ return wysihtml5.commands.formatInline.exec(composer, command, "b");
+ },
+
+ state: function(composer, command, color) {
+ // element.ownerDocument.queryCommandState("bold") results:
+ // firefox: only <b>
+ // chrome: <b>, <strong>, <h1>, <h2>, ...
+ // ie: <b>, <strong>
+ // opera: <b>, <strong>
+ return wysihtml5.commands.formatInline.state(composer, command, "b");
+ },
+
+ value: function() {
+ return undef;
+ }
+ };
+})(wysihtml5);
+
+(function(wysihtml5) {
+ var undef,
+ NODE_NAME = "A",
+ dom = wysihtml5.dom;
+
+ function _removeFormat(composer, anchors) {
+ var length = anchors.length,
+ i = 0,
+ anchor,
+ codeElement,
+ textContent;
+ for (; i<length; i++) {
+ anchor = anchors[i];
+ codeElement = dom.getParentElement(anchor, { nodeName: "code" });
+ textContent = dom.getTextContent(anchor);
+
+ // if <a> contains url-like text content, rename it to <code> to prevent re-autolinking
+ // else replace <a> with its childNodes
+ if (textContent.match(dom.autoLink.URL_REG_EXP) && !codeElement) {
+ // <code> element is used to prevent later auto-linking of the content
+ codeElement = dom.renameElement(anchor, "code");
+ } else {
+ dom.replaceWithChildNodes(anchor);
+ }
+ }
+ }
+
+ function _format(composer, attributes) {
+ var doc = composer.doc,
+ tempClass = "_wysihtml5-temp-" + (+new Date()),
+ tempClassRegExp = /non-matching-class/g,
+ i = 0,
+ length,
+ anchors,
+ anchor,
+ hasElementChild,
+ isEmpty,
+ elementToSetCaretAfter,
+ textContent,
+ whiteSpace,
+ j;
+ wysihtml5.commands.formatInline.exec(composer, undef, NODE_NAME, tempClass, tempClassRegExp);
+ anchors = doc.querySelectorAll(NODE_NAME + "." + tempClass);
+ length = anchors.length;
+ for (; i<length; i++) {
+ anchor = anchors[i];
+ anchor.removeAttribute("class");
+ for (j in attributes) {
+ anchor.setAttribute(j, attributes[j]);
+ }
+ }
+
+ elementToSetCaretAfter = anchor;
+ if (length === 1) {
+ textContent = dom.getTextContent(anchor);
+ hasElementChild = !!anchor.querySelector("*");
+ isEmpty = textContent === "" || textContent === wysihtml5.INVISIBLE_SPACE;
+ if (!hasElementChild && isEmpty) {
+ dom.setTextContent(anchor, attributes.text || anchor.href);
+ whiteSpace = doc.createTextNode(" ");
+ composer.selection.setAfter(anchor);
+ composer.selection.insertNode(whiteSpace);
+ elementToSetCaretAfter = whiteSpace;
+ }
+ }
+ composer.selection.setAfter(elementToSetCaretAfter);
+ }
+
+ wysihtml5.commands.createLink = {
+ /**
+ * TODO: Use HTMLApplier or formatInline here
+ *
+ * Turns selection into a link
+ * If selection is already a link, it removes the link and wraps it with a <code> element
+ * The <code> element is needed to avoid auto linking
+ *
+ * @example
+ * // either ...
+ * wysihtml5.commands.createLink.exec(composer, "createLink", "http://www.google.de");
+ * // ... or ...
+ * wysihtml5.commands.createLink.exec(composer, "createLink", { href: "http://www.google.de", target: "_blank" });
+ */
+ exec: function(composer, command, value) {
+ var anchors = this.state(composer, command);
+ if (anchors) {
+ // Selection contains links
+ composer.selection.executeAndRestore(function() {
+ _removeFormat(composer, anchors);
+ });
+ } else {
+ // Create links
+ value = typeof(value) === "object" ? value : { href: value };
+ _format(composer, value);
+ }
+ },
+
+ state: function(composer, command) {
+ return wysihtml5.commands.formatInline.state(composer, command, "A");
+ },
+
+ value: function() {
+ return undef;
+ }
+ };
+})(wysihtml5);/**
+ * document.execCommand("fontSize") will create either inline styles (firefox, chrome) or use font tags
+ * which we don't want
+ * Instead we set a css class
+ */
+(function(wysihtml5) {
+ var undef,
+ REG_EXP = /wysiwyg-font-size-[a-z\-]+/g;
+
+ wysihtml5.commands.fontSize = {
+ exec: function(composer, command, size) {
+ return wysihtml5.commands.formatInline.exec(composer, command, "span", "wysiwyg-font-size-" + size, REG_EXP);
+ },
+
+ state: function(composer, command, size) {
+ return wysihtml5.commands.formatInline.state(composer, command, "span", "wysiwyg-font-size-" + size, REG_EXP);
+ },
+
+ value: function() {
+ return undef;
+ }
+ };
+})(wysihtml5);
+/**
+ * document.execCommand("foreColor") will create either inline styles (firefox, chrome) or use font tags
+ * which we don't want
+ * Instead we set a css class
+ */
+(function(wysihtml5) {
+ var undef,
+ REG_EXP = /wysiwyg-color-[a-z]+/g;
+
+ wysihtml5.commands.foreColor = {
+ exec: function(composer, command, color) {
+ return wysihtml5.commands.formatInline.exec(composer, command, "span", "wysiwyg-color-" + color, REG_EXP);
+ },
+
+ state: function(composer, command, color) {
+ return wysihtml5.commands.formatInline.state(composer, command, "span", "wysiwyg-color-" + color, REG_EXP);
+ },
+
+ value: function() {
+ return undef;
+ }
+ };
+})(wysihtml5);(function(wysihtml5) {
+ var undef,
+ dom = wysihtml5.dom,
+ DEFAULT_NODE_NAME = "DIV",
+ // Following elements are grouped
+ // when the caret is within a H1 and the H4 is invoked, the H1 should turn into H4
+ // instead of creating a H4 within a H1 which would result in semantically invalid html
+ BLOCK_ELEMENTS_GROUP = ["H1", "H2", "H3", "H4", "H5", "H6", "P", "BLOCKQUOTE", DEFAULT_NODE_NAME];
+
+ /**
+ * Remove similiar classes (based on classRegExp)
+ * and add the desired class name
+ */
+ function _addClass(element, className, classRegExp) {
+ if (element.className) {
+ _removeClass(element, classRegExp);
+ element.className += " " + className;
+ } else {
+ element.className = className;
+ }
+ }
+
+ function _removeClass(element, classRegExp) {
+ element.className = element.className.replace(classRegExp, "");
+ }
+
+ /**
+ * Check whether given node is a text node and whether it's empty
+ */
+ function _isBlankTextNode(node) {
+ return node.nodeType === wysihtml5.TEXT_NODE && !wysihtml5.lang.string(node.data).trim();
+ }
+
+ /**
+ * Returns previous sibling node that is not a blank text node
+ */
+ function _getPreviousSiblingThatIsNotBlank(node) {
+ var previousSibling = node.previousSibling;
+ while (previousSibling && _isBlankTextNode(previousSibling)) {
+ previousSibling = previousSibling.previousSibling;
+ }
+ return previousSibling;
+ }
+
+ /**
+ * Returns next sibling node that is not a blank text node
+ */
+ function _getNextSiblingThatIsNotBlank(node) {
+ var nextSibling = node.nextSibling;
+ while (nextSibling && _isBlankTextNode(nextSibling)) {
+ nextSibling = nextSibling.nextSibling;
+ }
+ return nextSibling;
+ }
+
+ /**
+ * Adds line breaks before and after the given node if the previous and next siblings
+ * aren't already causing a visual line break (block element or <br>)
+ */
+ function _addLineBreakBeforeAndAfter(node) {
+ var doc = node.ownerDocument,
+ nextSibling = _getNextSiblingThatIsNotBlank(node),
+ previousSibling = _getPreviousSiblingThatIsNotBlank(node);
+
+ if (nextSibling && !_isLineBreakOrBlockElement(nextSibling)) {
+ node.parentNode.insertBefore(doc.createElement("br"), nextSibling);
+ }
+ if (previousSibling && !_isLineBreakOrBlockElement(previousSibling)) {
+ node.parentNode.insertBefore(doc.createElement("br"), node);
+ }
+ }
+
+ /**
+ * Removes line breaks before and after the given node
+ */
+ function _removeLineBreakBeforeAndAfter(node) {
+ var nextSibling = _getNextSiblingThatIsNotBlank(node),
+ previousSibling = _getPreviousSiblingThatIsNotBlank(node);
+
+ if (nextSibling && _isLineBreak(nextSibling)) {
+ nextSibling.parentNode.removeChild(nextSibling);
+ }
+ if (previousSibling && _isLineBreak(previousSibling)) {
+ previousSibling.parentNode.removeChild(previousSibling);
+ }
+ }
+
+ function _removeLastChildIfLineBreak(node) {
+ var lastChild = node.lastChild;
+ if (lastChild && _isLineBreak(lastChild)) {
+ lastChild.parentNode.removeChild(lastChild);
+ }
+ }
+
+ function _isLineBreak(node) {
+ return node.nodeName === "BR";
+ }
+
+ /**
+ * Checks whether the elment causes a visual line break
+ * (<br> or block elements)
+ */
+ function _isLineBreakOrBlockElement(element) {
+ if (_isLineBreak(element)) {
+ return true;
+ }
+
+ if (dom.getStyle("display").from(element) === "block") {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Execute native query command
+ * and if necessary modify the inserted node's className
+ */
+ function _execCommand(doc, command, nodeName, className) {
+ if (className) {
+ var eventListener = dom.observe(doc, "DOMNodeInserted", function(event) {
+ var target = event.target,
+ displayStyle;
+ if (target.nodeType !== wysihtml5.ELEMENT_NODE) {
+ return;
+ }
+ displayStyle = dom.getStyle("display").from(target);
+ if (displayStyle.substr(0, 6) !== "inline") {
+ // Make sure that only block elements receive the given class
+ target.className += " " + className;
+ }
+ });
+ }
+ doc.execCommand(command, false, nodeName);
+ if (eventListener) {
+ eventListener.stop();
+ }
+ }
+
+ function _selectLineAndWrap(composer, element) {
+ composer.selection.selectLine();
+ composer.selection.surround(element);
+ _removeLineBreakBeforeAndAfter(element);
+ _removeLastChildIfLineBreak(element);
+ composer.selection.selectNode(element);
+ }
+
+ function _hasClasses(element) {
+ return !!wysihtml5.lang.string(element.className).trim();
+ }
+
+ wysihtml5.commands.formatBlock = {
+ exec: function(composer, command, nodeName, className, classRegExp) {
+ var doc = composer.doc,
+ blockElement = this.state(composer, command, nodeName, className, classRegExp),
+ selectedNode;
+
+ nodeName = typeof(nodeName) === "string" ? nodeName.toUpperCase() : nodeName;
+
+ if (blockElement) {
+ composer.selection.executeAndRestoreSimple(function() {
+ if (classRegExp) {
+ _removeClass(blockElement, classRegExp);
+ }
+ var hasClasses = _hasClasses(blockElement);
+ if (!hasClasses && blockElement.nodeName === (nodeName || DEFAULT_NODE_NAME)) {
+ // Insert a line break afterwards and beforewards when there are siblings
+ // that are not of type line break or block element
+ _addLineBreakBeforeAndAfter(blockElement);
+ dom.replaceWithChildNodes(blockElement);
+ } else if (hasClasses) {
+ // Make sure that styling is kept by renaming the element to <div> and copying over the class name
+ dom.renameElement(blockElement, DEFAULT_NODE_NAME);
+ }
+ });
+ return;
+ }
+
+ // Find similiar block element and rename it (<h2 class="foo"></h2> => <h1 class="foo"></h1>)
+ if (nodeName === null || wysihtml5.lang.array(BLOCK_ELEMENTS_GROUP).contains(nodeName)) {
+ selectedNode = composer.selection.getSelectedNode();
+ blockElement = dom.getParentElement(selectedNode, {
+ nodeName: BLOCK_ELEMENTS_GROUP
+ });
+
+ if (blockElement) {
+ composer.selection.executeAndRestoreSimple(function() {
+ // Rename current block element to new block element and add class
+ if (nodeName) {
+ blockElement = dom.renameElement(blockElement, nodeName);
+ }
+ if (className) {
+ _addClass(blockElement, className, classRegExp);
+ }
+ });
+ return;
+ }
+ }
+
+ if (composer.commands.support(command)) {
+ _execCommand(doc, command, nodeName || DEFAULT_NODE_NAME, className);
+ return;
+ }
+
+ blockElement = doc.createElement(nodeName || DEFAULT_NODE_NAME);
+ if (className) {
+ blockElement.className = className;
+ }
+ _selectLineAndWrap(composer, blockElement);
+ },
+
+ state: function(composer, command, nodeName, className, classRegExp) {
+ nodeName = typeof(nodeName) === "string" ? nodeName.toUpperCase() : nodeName;
+ var selectedNode = composer.selection.getSelectedNode();
+ return dom.getParentElement(selectedNode, {
+ nodeName: nodeName,
+ className: className,
+ classRegExp: classRegExp
+ });
+ },
+
+ value: function() {
+ return undef;
+ }
+ };
+})(wysihtml5);/**
+ * formatInline scenarios for tag "B" (| = caret, |foo| = selected text)
+ *
+ * #1 caret in unformatted text:
+ * abcdefg|
+ * output:
+ * abcdefg<b>|</b>
+ *
+ * #2 unformatted text selected:
+ * abc|deg|h
+ * output:
+ * abc<b>|deg|</b>h
+ *
+ * #3 unformatted text selected across boundaries:
+ * ab|c <span>defg|h</span>
+ * output:
+ * ab<b>|c </b><span><b>defg</b>|h</span>
+ *
+ * #4 formatted text entirely selected
+ * <b>|abc|</b>
+ * output:
+ * |abc|
+ *
+ * #5 formatted text partially selected
+ * <b>ab|c|</b>
+ * output:
+ * <b>ab</b>|c|
+ *
+ * #6 formatted text selected across boundaries
+ * <span>ab|c</span> <b>de|fgh</b>
+ * output:
+ * <span>ab|c</span> de|<b>fgh</b>
+ */
+(function(wysihtml5) {
+ var undef,
+ // Treat <b> as <strong> and vice versa
+ ALIAS_MAPPING = {
+ "strong": "b",
+ "em": "i",
+ "b": "strong",
+ "i": "em"
+ },
+ htmlApplier = {};
+
+ function _getTagNames(tagName) {
+ var alias = ALIAS_MAPPING[tagName];
+ return alias ? [tagName.toLowerCase(), alias.toLowerCase()] : [tagName.toLowerCase()];
+ }
+
+ function _getApplier(tagName, className, classRegExp) {
+ var identifier = tagName + ":" + className;
+ if (!htmlApplier[identifier]) {
+ htmlApplier[identifier] = new wysihtml5.selection.HTMLApplier(_getTagNames(tagName), className, classRegExp, true);
+ }
+ return htmlApplier[identifier];
+ }
+
+ wysihtml5.commands.formatInline = {
+ exec: function(composer, command, tagName, className, classRegExp) {
+ var range = composer.selection.getRange();
+ if (!range) {
+ return false;
+ }
+ _getApplier(tagName, className, classRegExp).toggleRange(range);
+ composer.selection.setSelection(range);
+ },
+
+ state: function(composer, command, tagName, className, classRegExp) {
+ var doc = composer.doc,
+ aliasTagName = ALIAS_MAPPING[tagName] || tagName,
+ range;
+
+ // Check whether the document contains a node with the desired tagName
+ if (!wysihtml5.dom.hasElementWithTagName(doc, tagName) &&
+ !wysihtml5.dom.hasElementWithTagName(doc, aliasTagName)) {
+ return false;
+ }
+
+ // Check whether the document contains a node with the desired className
+ if (className && !wysihtml5.dom.hasElementWithClassName(doc, className)) {
+ return false;
+ }
+
+ range = composer.selection.getRange();
+ if (!range) {
+ return false;
+ }
+
+ return _getApplier(tagName, className, classRegExp).isAppliedToRange(range);
+ },
+
+ value: function() {
+ return undef;
+ }
+ };
+})(wysihtml5);(function(wysihtml5) {
+ var undef;
+
+ wysihtml5.commands.insertHTML = {
+ exec: function(composer, command, html) {
+ if (composer.commands.support(command)) {
+ composer.doc.execCommand(command, false, html);
+ } else {
+ composer.selection.insertHTML(html);
+ }
+ },
+
+ state: function() {
+ return false;
+ },
+
+ value: function() {
+ return undef;
+ }
+ };
+})(wysihtml5);(function(wysihtml5) {
+ var NODE_NAME = "IMG";
+
+ wysihtml5.commands.insertImage = {
+ /**
+ * Inserts an <img>
+ * If selection is already an image link, it removes it
+ *
+ * @example
+ * // either ...
+ * wysihtml5.commands.insertImage.exec(composer, "insertImage", "http://www.google.de/logo.jpg");
+ * // ... or ...
+ * wysihtml5.commands.insertImage.exec(composer, "insertImage", { src: "http://www.google.de/logo.jpg", title: "foo" });
+ */
+ exec: function(composer, command, value) {
+ value = typeof(value) === "object" ? value : { src: value };
+
+ var doc = composer.doc,
+ image = this.state(composer),
+ textNode,
+ i,
+ parent;
+
+ if (image) {
+ // Image already selected, set the caret before it and delete it
+ composer.selection.setBefore(image);
+ parent = image.parentNode;
+ parent.removeChild(image);
+
+ // and it's parent <a> too if it hasn't got any other relevant child nodes
+ wysihtml5.dom.removeEmptyTextNodes(parent);
+ if (parent.nodeName === "A" && !parent.firstChild) {
+ composer.selection.setAfter(parent);
+ parent.parentNode.removeChild(parent);
+ }
+
+ // firefox and ie sometimes don't remove the image handles, even though the image got removed
+ wysihtml5.quirks.redraw(composer.element);
+ return;
+ }
+
+ image = doc.createElement(NODE_NAME);
+
+ for (i in value) {
+ image[i] = value[i];
+ }
+
+ composer.selection.insertNode(image);
+ if (wysihtml5.browser.hasProblemsSettingCaretAfterImg()) {
+ textNode = doc.createTextNode(wysihtml5.INVISIBLE_SPACE);
+ composer.selection.insertNode(textNode);
+ composer.selection.setAfter(textNode);
+ } else {
+ composer.selection.setAfter(image);
+ }
+ },
+
+ state: function(composer) {
+ var doc = composer.doc,
+ selectedNode,
+ text,
+ imagesInSelection;
+
+ if (!wysihtml5.dom.hasElementWithTagName(doc, NODE_NAME)) {
+ return false;
+ }
+
+ selectedNode = composer.selection.getSelectedNode();
+ if (!selectedNode) {
+ return false;
+ }
+
+ if (selectedNode.nodeName === NODE_NAME) {
+ // This works perfectly in IE
+ return selectedNode;
+ }
+
+ if (selectedNode.nodeType !== wysihtml5.ELEMENT_NODE) {
+ return false;
+ }
+
+ text = composer.selection.getText();
+ text = wysihtml5.lang.string(text).trim();
+ if (text) {
+ return false;
+ }
+
+ imagesInSelection = composer.selection.getNodes(wysihtml5.ELEMENT_NODE, function(node) {
+ return node.nodeName === "IMG";
+ });
+
+ if (imagesInSelection.length !== 1) {
+ return false;
+ }
+
+ return imagesInSelection[0];
+ },
+
+ value: function(composer) {
+ var image = this.state(composer);
+ return image && image.src;
+ }
+ };
+})(wysihtml5);(function(wysihtml5) {
+ var undef,
+ LINE_BREAK = "<br>" + (wysihtml5.browser.needsSpaceAfterLineBreak() ? " " : "");
+
+ wysihtml5.commands.insertLineBreak = {
+ exec: function(composer, command) {
+ if (composer.commands.support(command)) {
+ composer.doc.execCommand(command, false, null);
+ if (!wysihtml5.browser.autoScrollsToCaret()) {
+ composer.selection.scrollIntoView();
+ }
+ } else {
+ composer.commands.exec("insertHTML", LINE_BREAK);
+ }
+ },
+
+ state: function() {
+ return false;
+ },
+
+ value: function() {
+ return undef;
+ }
+ };
+})(wysihtml5);(function(wysihtml5) {
+ var undef;
+
+ wysihtml5.commands.insertOrderedList = {
+ exec: function(composer, command) {
+ var doc = composer.doc,
+ selectedNode = composer.selection.getSelectedNode(),
+ list = wysihtml5.dom.getParentElement(selectedNode, { nodeName: "OL" }),
+ otherList = wysihtml5.dom.getParentElement(selectedNode, { nodeName: "UL" }),
+ tempClassName = "_wysihtml5-temp-" + new Date().getTime(),
+ isEmpty,
+ tempElement;
+
+ if (composer.commands.support(command)) {
+ doc.execCommand(command, false, null);
+ return;
+ }
+
+ if (list) {
+ // Unwrap list
+ // <ol><li>foo</li><li>bar</li></ol>
+ // becomes:
+ // foo<br>bar<br>
+ composer.selection.executeAndRestoreSimple(function() {
+ wysihtml5.dom.resolveList(list);
+ });
+ } else if (otherList) {
+ // Turn an unordered list into an ordered list
+ // <ul><li>foo</li><li>bar</li></ul>
+ // becomes:
+ // <ol><li>foo</li><li>bar</li></ol>
+ composer.selection.executeAndRestoreSimple(function() {
+ wysihtml5.dom.renameElement(otherList, "ol");
+ });
+ } else {
+ // Create list
+ composer.commands.exec("formatBlock", "div", tempClassName);
+ tempElement = doc.querySelector("." + tempClassName);
+ isEmpty = tempElement.innerHTML === "" || tempElement.innerHTML === wysihtml5.INVISIBLE_SPACE;
+ composer.selection.executeAndRestoreSimple(function() {
+ list = wysihtml5.dom.convertToList(tempElement, "ol");
+ });
+ if (isEmpty) {
+ composer.selection.selectNode(list.querySelector("li"));
+ }
+ }
+ },
+
+ state: function(composer) {
+ var selectedNode = composer.selection.getSelectedNode();
+ return wysihtml5.dom.getParentElement(selectedNode, { nodeName: "OL" });
+ },
+
+ value: function() {
+ return undef;
+ }
+ };
+})(wysihtml5);(function(wysihtml5) {
+ var undef;
+
+ wysihtml5.commands.insertUnorderedList = {
+ exec: function(composer, command) {
+ var doc = composer.doc,
+ selectedNode = composer.selection.getSelectedNode(),
+ list = wysihtml5.dom.getParentElement(selectedNode, { nodeName: "UL" }),
+ otherList = wysihtml5.dom.getParentElement(selectedNode, { nodeName: "OL" }),
+ tempClassName = "_wysihtml5-temp-" + new Date().getTime(),
+ isEmpty,
+ tempElement;
+
+ if (composer.commands.support(command)) {
+ doc.execCommand(command, false, null);
+ return;
+ }
+
+ if (list) {
+ // Unwrap list
+ // <ul><li>foo</li><li>bar</li></ul>
+ // becomes:
+ // foo<br>bar<br>
+ composer.selection.executeAndRestoreSimple(function() {
+ wysihtml5.dom.resolveList(list);
+ });
+ } else if (otherList) {
+ // Turn an ordered list into an unordered list
+ // <ol><li>foo</li><li>bar</li></ol>
+ // becomes:
+ // <ul><li>foo</li><li>bar</li></ul>
+ composer.selection.executeAndRestoreSimple(function() {
+ wysihtml5.dom.renameElement(otherList, "ul");
+ });
+ } else {
+ // Create list
+ composer.commands.exec("formatBlock", "div", tempClassName);
+ tempElement = doc.querySelector("." + tempClassName);
+ isEmpty = tempElement.innerHTML === "" || tempElement.innerHTML === wysihtml5.INVISIBLE_SPACE;
+ composer.selection.executeAndRestoreSimple(function() {
+ list = wysihtml5.dom.convertToList(tempElement, "ul");
+ });
+ if (isEmpty) {
+ composer.selection.selectNode(list.querySelector("li"));
+ }
+ }
+ },
+
+ state: function(composer) {
+ var selectedNode = composer.selection.getSelectedNode();
+ return wysihtml5.dom.getParentElement(selectedNode, { nodeName: "UL" });
+ },
+
+ value: function() {
+ return undef;
+ }
+ };
+})(wysihtml5);(function(wysihtml5) {
+ var undef;
+
+ wysihtml5.commands.italic = {
+ exec: function(composer, command) {
+ return wysihtml5.commands.formatInline.exec(composer, command, "i");
+ },
+
+ state: function(composer, command, color) {
+ // element.ownerDocument.queryCommandState("italic") results:
+ // firefox: only <i>
+ // chrome: <i>, <em>, <blockquote>, ...
+ // ie: <i>, <em>
+ // opera: only <i>
+ return wysihtml5.commands.formatInline.state(composer, command, "i");
+ },
+
+ value: function() {
+ return undef;
+ }
+ };
+})(wysihtml5);(function(wysihtml5) {
+ var undef,
+ CLASS_NAME = "wysiwyg-text-align-center",
+ REG_EXP = /wysiwyg-text-align-[a-z]+/g;
+
+ wysihtml5.commands.justifyCenter = {
+ exec: function(composer, command) {
+ return wysihtml5.commands.formatBlock.exec(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
+ },
+
+ state: function(composer, command) {
+ return wysihtml5.commands.formatBlock.state(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
+ },
+
+ value: function() {
+ return undef;
+ }
+ };
+})(wysihtml5);(function(wysihtml5) {
+ var undef,
+ CLASS_NAME = "wysiwyg-text-align-left",
+ REG_EXP = /wysiwyg-text-align-[a-z]+/g;
+
+ wysihtml5.commands.justifyLeft = {
+ exec: function(composer, command) {
+ return wysihtml5.commands.formatBlock.exec(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
+ },
+
+ state: function(composer, command) {
+ return wysihtml5.commands.formatBlock.state(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
+ },
+
+ value: function() {
+ return undef;
+ }
+ };
+})(wysihtml5);(function(wysihtml5) {
+ var undef,
+ CLASS_NAME = "wysiwyg-text-align-right",
+ REG_EXP = /wysiwyg-text-align-[a-z]+/g;
+
+ wysihtml5.commands.justifyRight = {
+ exec: function(composer, command) {
+ return wysihtml5.commands.formatBlock.exec(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
+ },
+
+ state: function(composer, command) {
+ return wysihtml5.commands.formatBlock.state(composer, "formatBlock", null, CLASS_NAME, REG_EXP);
+ },
+
+ value: function() {
+ return undef;
+ }
+ };
+})(wysihtml5);(function(wysihtml5) {
+ var undef;
+ wysihtml5.commands.underline = {
+ exec: function(composer, command) {
+ return wysihtml5.commands.formatInline.exec(composer, command, "u");
+ },
+
+ state: function(composer, command) {
+ return wysihtml5.commands.formatInline.state(composer, command, "u");
+ },
+
+ value: function() {
+ return undef;
+ }
+ };
+})(wysihtml5);/**
+ * Undo Manager for wysihtml5
+ * slightly inspired by http://rniwa.com/editing/undomanager.html#the-undomanager-interface
+ */
+(function(wysihtml5) {
+ var Z_KEY = 90,
+ Y_KEY = 89,
+ BACKSPACE_KEY = 8,
+ DELETE_KEY = 46,
+ MAX_HISTORY_ENTRIES = 40,
+ UNDO_HTML = '<span id="_wysihtml5-undo" class="_wysihtml5-temp">' + wysihtml5.INVISIBLE_SPACE + '</span>',
+ REDO_HTML = '<span id="_wysihtml5-redo" class="_wysihtml5-temp">' + wysihtml5.INVISIBLE_SPACE + '</span>',
+ dom = wysihtml5.dom;
+
+ function cleanTempElements(doc) {
+ var tempElement;
+ while (tempElement = doc.querySelector("._wysihtml5-temp")) {
+ tempElement.parentNode.removeChild(tempElement);
+ }
+ }
+
+ wysihtml5.UndoManager = wysihtml5.lang.Dispatcher.extend(
+ /** @scope wysihtml5.UndoManager.prototype */ {
+ constructor: function(editor) {
+ this.editor = editor;
+ this.composer = editor.composer;
+ this.element = this.composer.element;
+ this.history = [this.composer.getValue()];
+ this.position = 1;
+
+ // Undo manager currently only supported in browsers who have the insertHTML command (not IE)
+ if (this.composer.commands.support("insertHTML")) {
+ this._observe();
+ }
+ },
+
+ _observe: function() {
+ var that = this,
+ doc = this.composer.sandbox.getDocument(),
+ lastKey;
+
+ // Catch CTRL+Z and CTRL+Y
+ dom.observe(this.element, "keydown", function(event) {
+ if (event.altKey || (!event.ctrlKey && !event.metaKey)) {
+ return;
+ }
+
+ var keyCode = event.keyCode,
+ isUndo = keyCode === Z_KEY && !event.shiftKey,
+ isRedo = (keyCode === Z_KEY && event.shiftKey) || (keyCode === Y_KEY);
+
+ if (isUndo) {
+ that.undo();
+ event.preventDefault();
+ } else if (isRedo) {
+ that.redo();
+ event.preventDefault();
+ }
+ });
+
+ // Catch delete and backspace
+ dom.observe(this.element, "keydown", function(event) {
+ var keyCode = event.keyCode;
+ if (keyCode === lastKey) {
+ return;
+ }
+
+ lastKey = keyCode;
+
+ if (keyCode === BACKSPACE_KEY || keyCode === DELETE_KEY) {
+ that.transact();
+ }
+ });
+
+ // Now this is very hacky:
+ // These days browsers don't offer a undo/redo event which we could hook into
+ // to be notified when the user hits undo/redo in the contextmenu.
+ // Therefore we simply insert two elements as soon as the contextmenu gets opened.
+ // The last element being inserted will be immediately be removed again by a exexCommand("undo")
+ // => When the second element appears in the dom tree then we know the user clicked "redo" in the context menu
+ // => When the first element disappears from the dom tree then we know the user clicked "undo" in the context menu
+ if (wysihtml5.browser.hasUndoInContextMenu()) {
+ var interval, observed, cleanUp = function() {
+ cleanTempElements(doc);
+ clearInterval(interval);
+ };
+
+ dom.observe(this.element, "contextmenu", function() {
+ cleanUp();
+ that.composer.selection.executeAndRestoreSimple(function() {
+ if (that.element.lastChild) {
+ that.composer.selection.setAfter(that.element.lastChild);
+ }
+
+ // enable undo button in context menu
+ doc.execCommand("insertHTML", false, UNDO_HTML);
+ // enable redo button in context menu
+ doc.execCommand("insertHTML", false, REDO_HTML);
+ doc.execCommand("undo", false, null);
+ });
+
+ interval = setInterval(function() {
+ if (doc.getElementById("_wysihtml5-redo")) {
+ cleanUp();
+ that.redo();
+ } else if (!doc.getElementById("_wysihtml5-undo")) {
+ cleanUp();
+ that.undo();
+ }
+ }, 400);
+
+ if (!observed) {
+ observed = true;
+ dom.observe(document, "mousedown", cleanUp);
+ dom.observe(doc, ["mousedown", "paste", "cut", "copy"], cleanUp);
+ }
+ });
+ }
+
+ this.editor
+ .observe("newword:composer", function() {
+ that.transact();
+ })
+
+ .observe("beforecommand:composer", function() {
+ that.transact();
+ });
+ },
+
+ transact: function() {
+ var previousHtml = this.history[this.position - 1],
+ currentHtml = this.composer.getValue();
+
+ if (currentHtml == previousHtml) {
+ return;
+ }
+
+ var length = this.history.length = this.position;
+ if (length > MAX_HISTORY_ENTRIES) {
+ this.history.shift();
+ this.position--;
+ }
+
+ this.position++;
+ this.history.push(currentHtml);
+ },
+
+ undo: function() {
+ this.transact();
+
+ if (this.position <= 1) {
+ return;
+ }
+
+ this.set(this.history[--this.position - 1]);
+ this.editor.fire("undo:composer");
+ },
+
+ redo: function() {
+ if (this.position >= this.history.length) {
+ return;
+ }
+
+ this.set(this.history[++this.position - 1]);
+ this.editor.fire("redo:composer");
+ },
+
+ set: function(html) {
+ this.composer.setValue(html);
+ this.editor.focus(true);
+ }
+ });
+})(wysihtml5);
+/**
+ * TODO: the following methods still need unit test coverage
+ */
+wysihtml5.views.View = Base.extend(
+ /** @scope wysihtml5.views.View.prototype */ {
+ constructor: function(parent, textareaElement, config) {
+ this.parent = parent;
+ this.element = textareaElement;
+ this.config = config;
+
+ this._observeViewChange();
+ },
+
+ _observeViewChange: function() {
+ var that = this;
+ this.parent.observe("beforeload", function() {
+ that.parent.observe("change_view", function(view) {
+ if (view === that.name) {
+ that.parent.currentView = that;
+ that.show();
+ // Using tiny delay here to make sure that the placeholder is set before focusing
+ setTimeout(function() { that.focus(); }, 0);
+ } else {
+ that.hide();
+ }
+ });
+ });
+ },
+
+ focus: function() {
+ if (this.element.ownerDocument.querySelector(":focus") === this.element) {
+ return;
+ }
+
+ try { this.element.focus(); } catch(e) {}
+ },
+
+ hide: function() {
+ this.element.style.display = "none";
+ },
+
+ show: function() {
+ this.element.style.display = "";
+ },
+
+ disable: function() {
+ this.element.setAttribute("disabled", "disabled");
+ },
+
+ enable: function() {
+ this.element.removeAttribute("disabled");
+ }
+});(function(wysihtml5) {
+ var dom = wysihtml5.dom,
+ browser = wysihtml5.browser;
+
+ wysihtml5.views.Composer = wysihtml5.views.View.extend(
+ /** @scope wysihtml5.views.Composer.prototype */ {
+ name: "composer",
+
+ // Needed for firefox in order to display a proper caret in an empty contentEditable
+ CARET_HACK: "<br>",
+
+ constructor: function(parent, textareaElement, config) {
+ this.base(parent, textareaElement, config);
+ this.textarea = this.parent.textarea;
+ this._initSandbox();
+ },
+
+ clear: function() {
+ this.element.innerHTML = browser.displaysCaretInEmptyContentEditableCorrectly() ? "" : this.CARET_HACK;
+ },
+
+ getValue: function(parse) {
+ var value = this.isEmpty() ? "" : wysihtml5.quirks.getCorrectInnerHTML(this.element);
+
+ if (parse) {
+ value = this.parent.parse(value);
+ }
+
+ // Replace all "zero width no breaking space" chars
+ // which are used as hacks to enable some functionalities
+ // Also remove all CARET hacks that somehow got left
+ value = wysihtml5.lang.string(value).replace(wysihtml5.INVISIBLE_SPACE).by("");
+
+ return value;
+ },
+
+ setValue: function(html, parse) {
+ if (parse) {
+ html = this.parent.parse(html);
+ }
+ this.element.innerHTML = html;
+ },
+
+ show: function() {
+ this.iframe.style.display = this._displayStyle || "";
+
+ // Firefox needs this, otherwise contentEditable becomes uneditable
+ this.disable();
+ this.enable();
+ },
+
+ hide: function() {
+ this._displayStyle = dom.getStyle("display").from(this.iframe);
+ if (this._displayStyle === "none") {
+ this._displayStyle = null;
+ }
+ this.iframe.style.display = "none";
+ },
+
+ disable: function() {
+ this.element.removeAttribute("contentEditable");
+ this.base();
+ },
+
+ enable: function() {
+ this.element.setAttribute("contentEditable", "true");
+ this.base();
+ },
+
+ focus: function(setToEnd) {
+ // IE 8 fires the focus event after .focus()
+ // This is needed by our simulate_placeholder.js to work
+ // therefore we clear it ourselves this time
+ if (wysihtml5.browser.doesAsyncFocus() && this.hasPlaceholderSet()) {
+ this.clear();
+ }
+
+ this.base();
+
+ var lastChild = this.element.lastChild;
+ if (setToEnd && lastChild) {
+ if (lastChild.nodeName === "BR") {
+ this.selection.setBefore(this.element.lastChild);
+ } else {
+ this.selection.setAfter(this.element.lastChild);
+ }
+ }
+ },
+
+ getTextContent: function() {
+ return dom.getTextContent(this.element);
+ },
+
+ hasPlaceholderSet: function() {
+ return this.getTextContent() == this.textarea.element.getAttribute("placeholder");
+ },
+
+ isEmpty: function() {
+ var innerHTML = this.element.innerHTML,
+ elementsWithVisualValue = "blockquote, ul, ol, img, embed, object, table, iframe, svg, video, audio, button, input, select, textarea";
+ return innerHTML === "" ||
+ innerHTML === this.CARET_HACK ||
+ this.hasPlaceholderSet() ||
+ (this.getTextContent() === "" && !this.element.querySelector(elementsWithVisualValue));
+ },
+
+ _initSandbox: function() {
+ var that = this;
+
+ this.sandbox = new dom.Sandbox(function() {
+ that._create();
+ }, {
+ stylesheets: this.config.stylesheets
+ });
+ this.iframe = this.sandbox.getIframe();
+
+ // Create hidden field which tells the server after submit, that the user used an wysiwyg editor
+ var hiddenField = document.createElement("input");
+ hiddenField.type = "hidden";
+ hiddenField.name = "_wysihtml5_mode";
+ hiddenField.value = 1;
+
+ // Store reference to current wysihtml5 instance on the textarea element
+ var textareaElement = this.textarea.element;
+ dom.insert(this.iframe).after(textareaElement);
+ dom.insert(hiddenField).after(textareaElement);
+ },
+
+ _create: function() {
+ var that = this;
+
+ this.doc = this.sandbox.getDocument();
+ this.element = this.doc.body;
+ this.textarea = this.parent.textarea;
+ this.element.innerHTML = this.textarea.getValue(true);
+ this.enable();
+
+ // Make sure our selection handler is ready
+ this.selection = new wysihtml5.Selection(this.parent);
+
+ // Make sure commands dispatcher is ready
+ this.commands = new wysihtml5.Commands(this.parent);
+
+ dom.copyAttributes([
+ "className", "spellcheck", "title", "lang", "dir", "accessKey"
+ ]).from(this.textarea.element).to(this.element);
+
+ dom.addClass(this.element, this.config.composerClassName);
+
+ // Make the editor look like the original textarea, by syncing styles
+ if (this.config.style) {
+ this.style();
+ }
+
+ this.observe();
+
+ var name = this.config.name;
+ if (name) {
+ dom.addClass(this.element, name);
+ dom.addClass(this.iframe, name);
+ }
+
+ // Simulate html5 placeholder attribute on contentEditable element
+ var placeholderText = typeof(this.config.placeholder) === "string"
+ ? this.config.placeholder
+ : this.textarea.element.getAttribute("placeholder");
+ if (placeholderText) {
+ dom.simulatePlaceholder(this.parent, this, placeholderText);
+ }
+
+ // Make sure that the browser avoids using inline styles whenever possible
+ this.commands.exec("styleWithCSS", false);
+
+ this._initAutoLinking();
+ this._initObjectResizing();
+ this._initUndoManager();
+
+ // Simulate html5 autofocus on contentEditable element
+ if (this.textarea.element.hasAttribute("autofocus") || document.querySelector(":focus") == this.textarea.element) {
+ setTimeout(function() { that.focus(); }, 100);
+ }
+
+ wysihtml5.quirks.insertLineBreakOnReturn(this);
+
+ // IE sometimes leaves a single paragraph, which can't be removed by the user
+ if (!browser.clearsContentEditableCorrectly()) {
+ wysihtml5.quirks.ensureProperClearing(this);
+ }
+
+ if (!browser.clearsListsInContentEditableCorrectly()) {
+ wysihtml5.quirks.ensureProperClearingOfLists(this);
+ }
+
+ // Set up a sync that makes sure that textarea and editor have the same content
+ if (this.initSync && this.config.sync) {
+ this.initSync();
+ }
+
+ // Okay hide the textarea, we are ready to go
+ this.textarea.hide();
+
+ // Fire global (before-)load event
+ this.parent.fire("beforeload").fire("load");
+ },
+
+ _initAutoLinking: function() {
+ var that = this,
+ supportsDisablingOfAutoLinking = browser.canDisableAutoLinking(),
+ supportsAutoLinking = browser.doesAutoLinkingInContentEditable();
+ if (supportsDisablingOfAutoLinking) {
+ this.commands.exec("autoUrlDetect", false);
+ }
+
+ if (!this.config.autoLink) {
+ return;
+ }
+
+ // Only do the auto linking by ourselves when the browser doesn't support auto linking
+ // OR when he supports auto linking but we were able to turn it off (IE9+)
+ if (!supportsAutoLinking || (supportsAutoLinking && supportsDisablingOfAutoLinking)) {
+ this.parent.observe("newword:composer", function() {
+ that.selection.executeAndRestore(function(startContainer, endContainer) {
+ dom.autoLink(endContainer.parentNode);
+ });
+ });
+ }
+
+ // Assuming we have the following:
+ // <a href="http://www.google.de">http://www.google.de</a>
+ // If a user now changes the url in the innerHTML we want to make sure that
+ // it's synchronized with the href attribute (as long as the innerHTML is still a url)
+ var // Use a live NodeList to check whether there are any links in the document
+ links = this.sandbox.getDocument().getElementsByTagName("a"),
+ // The autoLink helper method reveals a reg exp to detect correct urls
+ urlRegExp = dom.autoLink.URL_REG_EXP,
+ getTextContent = function(element) {
+ var textContent = wysihtml5.lang.string(dom.getTextContent(element)).trim();
+ if (textContent.substr(0, 4) === "www.") {
+ textContent = "http://" + textContent;
+ }
+ return textContent;
+ };
+
+ dom.observe(this.element, "keydown", function(event) {
+ if (!links.length) {
+ return;
+ }
+
+ var selectedNode = that.selection.getSelectedNode(event.target.ownerDocument),
+ link = dom.getParentElement(selectedNode, { nodeName: "A" }, 4),
+ textContent;
+
+ if (!link) {
+ return;
+ }
+
+ textContent = getTextContent(link);
+ // keydown is fired before the actual content is changed
+ // therefore we set a timeout to change the href
+ setTimeout(function() {
+ var newTextContent = getTextContent(link);
+ if (newTextContent === textContent) {
+ return;
+ }
+
+ // Only set href when new href looks like a valid url
+ if (newTextContent.match(urlRegExp)) {
+ link.setAttribute("href", newTextContent);
+ }
+ }, 0);
+ });
+ },
+
+ _initObjectResizing: function() {
+ var properties = ["width", "height"],
+ propertiesLength = properties.length,
+ element = this.element;
+
+ this.commands.exec("enableObjectResizing", this.config.allowObjectResizing);
+
+ if (this.config.allowObjectResizing) {
+ // IE sets inline styles after resizing objects
+ // The following lines make sure that the width/height css properties
+ // are copied over to the width/height attributes
+ if (browser.supportsEvent("resizeend")) {
+ dom.observe(element, "resizeend", function(event) {
+ var target = event.target || event.srcElement,
+ style = target.style,
+ i = 0,
+ property;
+ for(; i<propertiesLength; i++) {
+ property = properties[i];
+ if (style[property]) {
+ target.setAttribute(property, parseInt(style[property], 10));
+ style[property] = "";
+ }
+ }
+ // After resizing IE sometimes forgets to remove the old resize handles
+ wysihtml5.quirks.redraw(element);
+ });
+ }
+ } else {
+ if (browser.supportsEvent("resizestart")) {
+ dom.observe(element, "resizestart", function(event) { event.preventDefault(); });
+ }
+ }
+ },
+
+ _initUndoManager: function() {
+ new wysihtml5.UndoManager(this.parent);
+ }
+ });
+})(wysihtml5);(function(wysihtml5) {
+ var dom = wysihtml5.dom,
+ doc = document,
+ win = window,
+ HOST_TEMPLATE = doc.createElement("div"),
+ /**
+ * Styles to copy from textarea to the composer element
+ */
+ TEXT_FORMATTING = [
+ "background-color",
+ "color", "cursor",
+ "font-family", "font-size", "font-style", "font-variant", "font-weight",
+ "line-height", "letter-spacing",
+ "text-align", "text-decoration", "text-indent", "text-rendering",
+ "word-break", "word-wrap", "word-spacing"
+ ],
+ /**
+ * Styles to copy from textarea to the iframe
+ */
+ BOX_FORMATTING = [
+ "background-color",
+ "border-collapse",
+ "border-bottom-color", "border-bottom-style", "border-bottom-width",
+ "border-left-color", "border-left-style", "border-left-width",
+ "border-right-color", "border-right-style", "border-right-width",
+ "border-top-color", "border-top-style", "border-top-width",
+ "clear", "display", "float",
+ "margin-bottom", "margin-left", "margin-right", "margin-top",
+ "outline-color", "outline-offset", "outline-width", "outline-style",
+ "padding-left", "padding-right", "padding-top", "padding-bottom",
+ "position", "top", "left", "right", "bottom", "z-index",
+ "vertical-align", "text-align",
+ "-webkit-box-sizing", "-moz-box-sizing", "-ms-box-sizing", "box-sizing",
+ "-webkit-box-shadow", "-moz-box-shadow", "-ms-box-shadow","box-shadow",
+ "-webkit-border-top-right-radius", "-moz-border-radius-topright", "border-top-right-radius",
+ "-webkit-border-bottom-right-radius", "-moz-border-radius-bottomright", "border-bottom-right-radius",
+ "-webkit-border-bottom-left-radius", "-moz-border-radius-bottomleft", "border-bottom-left-radius",
+ "-webkit-border-top-left-radius", "-moz-border-radius-topleft", "border-top-left-radius",
+ "width", "height"
+ ],
+ /**
+ * Styles to sync while the window gets resized
+ */
+ RESIZE_STYLE = [
+ "width", "height",
+ "top", "left", "right", "bottom"
+ ],
+ ADDITIONAL_CSS_RULES = [
+ "html { height: 100%; }",
+ "body { min-height: 100%; padding: 0; margin: 0; margin-top: -1px; padding-top: 1px; }",
+ "._wysihtml5-temp { display: none; }",
+ wysihtml5.browser.isGecko ?
+ "body.placeholder { color: graytext !important; }" :
+ "body.placeholder { color: #a9a9a9 !important; }",
+ "body[disabled] { background-color: #eee !important; color: #999 !important; cursor: default !important; }",
+ // Ensure that user see's broken images and can delete them
+ "img:-moz-broken { -moz-force-broken-image-icon: 1; height: 24px; width: 24px; }"
+ ];
+
+ /**
+ * With "setActive" IE offers a smart way of focusing elements without scrolling them into view:
+ * http://msdn.microsoft.com/en-us/library/ms536738(v=vs.85).aspx
+ *
+ * Other browsers need a more hacky way: (pssst don't tell my mama)
+ * In order to prevent the element being scrolled into view when focusing it, we simply
+ * move it out of the scrollable area, focus it, and reset it's position
+ */
+ var focusWithoutScrolling = function(element) {
+ if (element.setActive) {
+ // Following line could cause a js error when the textarea is invisible
+ // See https://github.com/xing/wysihtml5/issues/9
+ try { element.setActive(); } catch(e) {}
+ } else {
+ var elementStyle = element.style,
+ originalScrollTop = doc.documentElement.scrollTop || doc.body.scrollTop,
+ originalScrollLeft = doc.documentElement.scrollLeft || doc.body.scrollLeft,
+ originalStyles = {
+ position: elementStyle.position,
+ top: elementStyle.top,
+ left: elementStyle.left,
+ WebkitUserSelect: elementStyle.WebkitUserSelect
+ };
+
+ dom.setStyles({
+ position: "absolute",
+ top: "-99999px",
+ left: "-99999px",
+ // Don't ask why but temporarily setting -webkit-user-select to none makes the whole thing performing smoother
+ WebkitUserSelect: "none"
+ }).on(element);
+
+ element.focus();
+
+ dom.setStyles(originalStyles).on(element);
+
+ if (win.scrollTo) {
+ // Some browser extensions unset this method to prevent annoyances
+ // "Better PopUp Blocker" for Chrome http://code.google.com/p/betterpopupblocker/source/browse/trunk/blockStart.…
+ // Issue: http://code.google.com/p/betterpopupblocker/issues/detail?id=1
+ win.scrollTo(originalScrollLeft, originalScrollTop);
+ }
+ }
+ };
+
+
+ wysihtml5.views.Composer.prototype.style = function() {
+ var that = this,
+ originalActiveElement = doc.querySelector(":focus"),
+ textareaElement = this.textarea.element,
+ hasPlaceholder = textareaElement.hasAttribute("placeholder"),
+ originalPlaceholder = hasPlaceholder && textareaElement.getAttribute("placeholder");
+ this.focusStylesHost = this.focusStylesHost || HOST_TEMPLATE.cloneNode(false);
+ this.blurStylesHost = this.blurStylesHost || HOST_TEMPLATE.cloneNode(false);
+
+ // Remove placeholder before copying (as the placeholder has an affect on the computed style)
+ if (hasPlaceholder) {
+ textareaElement.removeAttribute("placeholder");
+ }
+
+ if (textareaElement === originalActiveElement) {
+ textareaElement.blur();
+ }
+
+ // --------- iframe styles (has to be set before editor styles, otherwise IE9 sets wrong fontFamily on blurStylesHost) ---------
+ dom.copyStyles(BOX_FORMATTING).from(textareaElement).to(this.iframe).andTo(this.blurStylesHost);
+
+ // --------- editor styles ---------
+ dom.copyStyles(TEXT_FORMATTING).from(textareaElement).to(this.element).andTo(this.blurStylesHost);
+
+ // --------- apply standard rules ---------
+ dom.insertCSS(ADDITIONAL_CSS_RULES).into(this.element.ownerDocument);
+
+ // --------- :focus styles ---------
+ focusWithoutScrolling(textareaElement);
+ dom.copyStyles(BOX_FORMATTING).from(textareaElement).to(this.focusStylesHost);
+ dom.copyStyles(TEXT_FORMATTING).from(textareaElement).to(this.focusStylesHost);
+
+ // Make sure that we don't change the display style of the iframe when copying styles oblur/onfocus
+ // this is needed for when the change_view event is fired where the iframe is hidden and then
+ // the blur event fires and re-displays it
+ var boxFormattingStyles = wysihtml5.lang.array(BOX_FORMATTING).without(["display"]);
+
+ // --------- restore focus ---------
+ if (originalActiveElement) {
+ originalActiveElement.focus();
+ } else {
+ textareaElement.blur();
+ }
+
+ // --------- restore placeholder ---------
+ if (hasPlaceholder) {
+ textareaElement.setAttribute("placeholder", originalPlaceholder);
+ }
+
+ // When copying styles, we only get the computed style which is never returned in percent unit
+ // Therefore we've to recalculate style onresize
+ if (!wysihtml5.browser.hasCurrentStyleProperty()) {
+ var winObserver = dom.observe(win, "resize", function() {
+ // Remove event listener if composer doesn't exist anymore
+ if (!dom.contains(document.documentElement, that.iframe)) {
+ winObserver.stop();
+ return;
+ }
+ var originalTextareaDisplayStyle = dom.getStyle("display").from(textareaElement),
+ originalComposerDisplayStyle = dom.getStyle("display").from(that.iframe);
+ textareaElement.style.display = "";
+ that.iframe.style.display = "none";
+ dom.copyStyles(RESIZE_STYLE)
+ .from(textareaElement)
+ .to(that.iframe)
+ .andTo(that.focusStylesHost)
+ .andTo(that.blurStylesHost);
+ that.iframe.style.display = originalComposerDisplayStyle;
+ textareaElement.style.display = originalTextareaDisplayStyle;
+ });
+ }
+
+ // --------- Sync focus/blur styles ---------
+ this.parent.observe("focus:composer", function() {
+ dom.copyStyles(boxFormattingStyles) .from(that.focusStylesHost).to(that.iframe);
+ dom.copyStyles(TEXT_FORMATTING) .from(that.focusStylesHost).to(that.element);
+ });
+
+ this.parent.observe("blur:composer", function() {
+ dom.copyStyles(boxFormattingStyles) .from(that.blurStylesHost).to(that.iframe);
+ dom.copyStyles(TEXT_FORMATTING) .from(that.blurStylesHost).to(that.element);
+ });
+
+ return this;
+ };
+})(wysihtml5);/**
+ * Taking care of events
+ * - Simulating 'change' event on contentEditable element
+ * - Handling drag & drop logic
+ * - Catch paste events
+ * - Dispatch proprietary newword:composer event
+ * - Keyboard shortcuts
+ */
+(function(wysihtml5) {
+ var dom = wysihtml5.dom,
+ browser = wysihtml5.browser,
+ /**
+ * Map keyCodes to query commands
+ */
+ shortcuts = {
+ "66": "bold", // B
+ "73": "italic", // I
+ "85": "underline" // U
+ };
+
+ wysihtml5.views.Composer.prototype.observe = function() {
+ var that = this,
+ state = this.getValue(),
+ iframe = this.sandbox.getIframe(),
+ element = this.element,
+ focusBlurElement = browser.supportsEventsInIframeCorrectly() ? element : this.sandbox.getWindow(),
+ // Firefox < 3.5 doesn't support the drop event, instead it supports a so called "dragdrop" event which behaves almost the same
+ pasteEvents = browser.supportsEvent("drop") ? ["drop", "paste"] : ["dragdrop", "paste"];
+
+ // --------- destroy:composer event ---------
+ dom.observe(iframe, "DOMNodeRemoved", function() {
+ clearInterval(domNodeRemovedInterval);
+ that.parent.fire("destroy:composer");
+ });
+
+ // DOMNodeRemoved event is not supported in IE 8
+ var domNodeRemovedInterval = setInterval(function() {
+ if (!dom.contains(document.documentElement, iframe)) {
+ clearInterval(domNodeRemovedInterval);
+ that.parent.fire("destroy:composer");
+ }
+ }, 250);
+
+
+ // --------- Focus & blur logic ---------
+ dom.observe(focusBlurElement, "focus", function() {
+ that.parent.fire("focus").fire("focus:composer");
+
+ // Delay storing of state until all focus handler are fired
+ // especially the one which resets the placeholder
+ setTimeout(function() { state = that.getValue(); }, 0);
+ });
+
+ dom.observe(focusBlurElement, "blur", function() {
+ if (state !== that.getValue()) {
+ that.parent.fire("change").fire("change:composer");
+ }
+ that.parent.fire("blur").fire("blur:composer");
+ });
+
+ if (wysihtml5.browser.isIos()) {
+ // When on iPad/iPhone/IPod after clicking outside of editor, the editor loses focus
+ // but the UI still acts as if the editor has focus (blinking caret and onscreen keyboard visible)
+ // We prevent that by focusing a temporary input element which immediately loses focus
+ dom.observe(element, "blur", function() {
+ var input = element.ownerDocument.createElement("input"),
+ originalScrollTop = document.documentElement.scrollTop || document.body.scrollTop,
+ originalScrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
+ try {
+ that.selection.insertNode(input);
+ } catch(e) {
+ element.appendChild(input);
+ }
+ input.focus();
+ input.parentNode.removeChild(input);
+
+ window.scrollTo(originalScrollLeft, originalScrollTop);
+ });
+ }
+
+ // --------- Drag & Drop logic ---------
+ dom.observe(element, "dragenter", function() {
+ that.parent.fire("unset_placeholder");
+ });
+
+ if (browser.firesOnDropOnlyWhenOnDragOverIsCancelled()) {
+ dom.observe(element, ["dragover", "dragenter"], function(event) {
+ event.preventDefault();
+ });
+ }
+
+ dom.observe(element, pasteEvents, function(event) {
+ var dataTransfer = event.dataTransfer,
+ data;
+
+ if (dataTransfer && browser.supportsDataTransfer()) {
+ data = dataTransfer.getData("text/html") || dataTransfer.getData("text/plain");
+ }
+ if (data) {
+ element.focus();
+ that.commands.exec("insertHTML", data);
+ that.parent.fire("paste").fire("paste:composer");
+ event.stopPropagation();
+ event.preventDefault();
+ } else {
+ setTimeout(function() {
+ that.parent.fire("paste").fire("paste:composer");
+ }, 0);
+ }
+ });
+
+ // --------- neword event ---------
+ dom.observe(element, "keyup", function(event) {
+ var keyCode = event.keyCode;
+ if (keyCode === wysihtml5.SPACE_KEY || keyCode === wysihtml5.ENTER_KEY) {
+ that.parent.fire("newword:composer");
+ }
+ });
+
+ this.parent.observe("paste:composer", function() {
+ setTimeout(function() { that.parent.fire("newword:composer"); }, 0);
+ });
+
+ // --------- Make sure that images are selected when clicking on them ---------
+ if (!browser.canSelectImagesInContentEditable()) {
+ dom.observe(element, "mousedown", function(event) {
+ var target = event.target;
+ if (target.nodeName === "IMG") {
+ that.selection.selectNode(target);
+ event.preventDefault();
+ }
+ });
+ }
+
+ // --------- Shortcut logic ---------
+ dom.observe(element, "keydown", function(event) {
+ var keyCode = event.keyCode,
+ command = shortcuts[keyCode];
+ if ((event.ctrlKey || event.metaKey) && !event.altKey && command) {
+ that.commands.exec(command);
+ event.preventDefault();
+ }
+ });
+
+ // --------- Make sure that when pressing backspace/delete on selected images deletes the image and it's anchor ---------
+ dom.observe(element, "keydown", function(event) {
+ var target = that.selection.getSelectedNode(true),
+ keyCode = event.keyCode,
+ parent;
+ if (target && target.nodeName === "IMG" && (keyCode === wysihtml5.BACKSPACE_KEY || keyCode === wysihtml5.DELETE_KEY)) { // 8 => backspace, 46 => delete
+ parent = target.parentNode;
+ // delete the <img>
+ parent.removeChild(target);
+ // and it's parent <a> too if it hasn't got any other child nodes
+ if (parent.nodeName === "A" && !parent.firstChild) {
+ parent.parentNode.removeChild(parent);
+ }
+
+ setTimeout(function() { wysihtml5.quirks.redraw(element); }, 0);
+ event.preventDefault();
+ }
+ });
+
+ // --------- Show url in tooltip when hovering links or images ---------
+ var titlePrefixes = {
+ IMG: "Image: ",
+ A: "Link: "
+ };
+
+ dom.observe(element, "mouseover", function(event) {
+ var target = event.target,
+ nodeName = target.nodeName,
+ title;
+ if (nodeName !== "A" && nodeName !== "IMG") {
+ return;
+ }
+ var hasTitle = target.hasAttribute("title");
+ if(!hasTitle){
+ title = titlePrefixes[nodeName] + (target.getAttribute("href") || target.getAttribute("src"));
+ target.setAttribute("title", title);
+ }
+ });
+ };
+})(wysihtml5);/**
+ * Class that takes care that the value of the composer and the textarea is always in sync
+ */
+(function(wysihtml5) {
+ var INTERVAL = 400;
+
+ wysihtml5.views.Synchronizer = Base.extend(
+ /** @scope wysihtml5.views.Synchronizer.prototype */ {
+
+ constructor: function(editor, textarea, composer) {
+ this.editor = editor;
+ this.textarea = textarea;
+ this.composer = composer;
+
+ this._observe();
+ },
+
+ /**
+ * Sync html from composer to textarea
+ * Takes care of placeholders
+ * @param {Boolean} shouldParseHtml Whether the html should be sanitized before inserting it into the textarea
+ */
+ fromComposerToTextarea: function(shouldParseHtml) {
+ this.textarea.setValue(wysihtml5.lang.string(this.composer.getValue()).trim(), shouldParseHtml);
+ },
+
+ /**
+ * Sync value of textarea to composer
+ * Takes care of placeholders
+ * @param {Boolean} shouldParseHtml Whether the html should be sanitized before inserting it into the composer
+ */
+ fromTextareaToComposer: function(shouldParseHtml) {
+ var textareaValue = this.textarea.getValue();
+ if (textareaValue) {
+ this.composer.setValue(textareaValue, shouldParseHtml);
+ } else {
+ this.composer.clear();
+ this.editor.fire("set_placeholder");
+ }
+ },
+
+ /**
+ * Invoke syncing based on view state
+ * @param {Boolean} shouldParseHtml Whether the html should be sanitized before inserting it into the composer/textarea
+ */
+ sync: function(shouldParseHtml) {
+ if (this.editor.currentView.name === "textarea") {
+ this.fromTextareaToComposer(shouldParseHtml);
+ } else {
+ this.fromComposerToTextarea(shouldParseHtml);
+ }
+ },
+
+ /**
+ * Initializes interval-based syncing
+ * also makes sure that on-submit the composer's content is synced with the textarea
+ * immediately when the form gets submitted
+ */
+ _observe: function() {
+ var interval,
+ that = this,
+ form = this.textarea.element.form,
+ startInterval = function() {
+ interval = setInterval(function() { that.fromComposerToTextarea(); }, INTERVAL);
+ },
+ stopInterval = function() {
+ clearInterval(interval);
+ interval = null;
+ };
+
+ startInterval();
+
+ if (form) {
+ // If the textarea is in a form make sure that after onreset and onsubmit the composer
+ // has the correct state
+ wysihtml5.dom.observe(form, "submit", function() {
+ that.sync(true);
+ });
+ wysihtml5.dom.observe(form, "reset", function() {
+ setTimeout(function() { that.fromTextareaToComposer(); }, 0);
+ });
+ }
+
+ this.editor.observe("change_view", function(view) {
+ if (view === "composer" && !interval) {
+ that.fromTextareaToComposer(true);
+ startInterval();
+ } else if (view === "textarea") {
+ that.fromComposerToTextarea(true);
+ stopInterval();
+ }
+ });
+
+ this.editor.observe("destroy:composer", stopInterval);
+ }
+ });
+})(wysihtml5);
+wysihtml5.views.Textarea = wysihtml5.views.View.extend(
+ /** @scope wysihtml5.views.Textarea.prototype */ {
+ name: "textarea",
+
+ constructor: function(parent, textareaElement, config) {
+ this.base(parent, textareaElement, config);
+
+ this._observe();
+ },
+
+ clear: function() {
+ this.element.value = "";
+ },
+
+ getValue: function(parse) {
+ var value = this.isEmpty() ? "" : this.element.value;
+ if (parse) {
+ value = this.parent.parse(value);
+ }
+ return value;
+ },
+
+ setValue: function(html, parse) {
+ if (parse) {
+ html = this.parent.parse(html);
+ }
+ this.element.value = html;
+ },
+
+ hasPlaceholderSet: function() {
+ var supportsPlaceholder = wysihtml5.browser.supportsPlaceholderAttributeOn(this.element),
+ placeholderText = this.element.getAttribute("placeholder") || null,
+ value = this.element.value,
+ isEmpty = !value;
+ return (supportsPlaceholder && isEmpty) || (value === placeholderText);
+ },
+
+ isEmpty: function() {
+ return !wysihtml5.lang.string(this.element.value).trim() || this.hasPlaceholderSet();
+ },
+
+ _observe: function() {
+ var element = this.element,
+ parent = this.parent,
+ eventMapping = {
+ focusin: "focus",
+ focusout: "blur"
+ },
+ /**
+ * Calling focus() or blur() on an element doesn't synchronously trigger the attached focus/blur events
+ * This is the case for focusin and focusout, so let's use them whenever possible, kkthxbai
+ */
+ events = wysihtml5.browser.supportsEvent("focusin") ? ["focusin", "focusout", "change"] : ["focus", "blur", "change"];
+
+ parent.observe("beforeload", function() {
+ wysihtml5.dom.observe(element, events, function(event) {
+ var eventName = eventMapping[event.type] || event.type;
+ parent.fire(eventName).fire(eventName + ":textarea");
+ });
+
+ wysihtml5.dom.observe(element, ["paste", "drop"], function() {
+ setTimeout(function() { parent.fire("paste").fire("paste:textarea"); }, 0);
+ });
+ });
+ }
+});/**
+ * Toolbar Dialog
+ *
+ * @param {Element} link The toolbar link which causes the dialog to show up
+ * @param {Element} container The dialog container
+ *
+ * @example
+ * <!-- Toolbar link -->
+ * <a data-wysihtml5-command="insertImage">insert an image</a>
+ *
+ * <!-- Dialog -->
+ * <div data-wysihtml5-dialog="insertImage" style="display: none;">
+ * <label>
+ * URL: <input data-wysihtml5-dialog-field="src" value="http://">
+ * </label>
+ * <label>
+ * Alternative text: <input data-wysihtml5-dialog-field="alt" value="">
+ * </label>
+ * </div>
+ *
+ * <script>
+ * var dialog = new wysihtml5.toolbar.Dialog(
+ * document.querySelector("[data-wysihtml5-command='insertImage']"),
+ * document.querySelector("[data-wysihtml5-dialog='insertImage']")
+ * );
+ * dialog.observe("save", function(attributes) {
+ * // do something
+ * });
+ * </script>
+ */
+(function(wysihtml5) {
+ var dom = wysihtml5.dom,
+ CLASS_NAME_OPENED = "wysihtml5-command-dialog-opened",
+ SELECTOR_FORM_ELEMENTS = "input, select, textarea",
+ SELECTOR_FIELDS = "[data-wysihtml5-dialog-field]",
+ ATTRIBUTE_FIELDS = "data-wysihtml5-dialog-field";
+
+
+ wysihtml5.toolbar.Dialog = wysihtml5.lang.Dispatcher.extend(
+ /** @scope wysihtml5.toolbar.Dialog.prototype */ {
+ constructor: function(link, container) {
+ this.link = link;
+ this.container = container;
+ },
+
+ _observe: function() {
+ if (this._observed) {
+ return;
+ }
+
+ var that = this,
+ callbackWrapper = function(event) {
+ var attributes = that._serialize();
+ if (attributes == that.elementToChange) {
+ that.fire("edit", attributes);
+ } else {
+ that.fire("save", attributes);
+ }
+ that.hide();
+ event.preventDefault();
+ event.stopPropagation();
+ };
+
+ dom.observe(that.link, "click", function(event) {
+ if (dom.hasClass(that.link, CLASS_NAME_OPENED)) {
+ setTimeout(function() { that.hide(); }, 0);
+ }
+ });
+
+ dom.observe(this.container, "keydown", function(event) {
+ var keyCode = event.keyCode;
+ if (keyCode === wysihtml5.ENTER_KEY) {
+ callbackWrapper(event);
+ }
+ if (keyCode === wysihtml5.ESCAPE_KEY) {
+ that.hide();
+ }
+ });
+
+ dom.delegate(this.container, "[data-wysihtml5-dialog-action=save]", "click", callbackWrapper);
+
+ dom.delegate(this.container, "[data-wysihtml5-dialog-action=cancel]", "click", function(event) {
+ that.fire("cancel");
+ that.hide();
+ event.preventDefault();
+ event.stopPropagation();
+ });
+
+ var formElements = this.container.querySelectorAll(SELECTOR_FORM_ELEMENTS),
+ i = 0,
+ length = formElements.length,
+ _clearInterval = function() { clearInterval(that.interval); };
+ for (; i<length; i++) {
+ dom.observe(formElements[i], "change", _clearInterval);
+ }
+
+ this._observed = true;
+ },
+
+ /**
+ * Grabs all fields in the dialog and puts them in key=>value style in an object which
+ * then gets returned
+ */
+ _serialize: function() {
+ var data = this.elementToChange || {},
+ fields = this.container.querySelectorAll(SELECTOR_FIELDS),
+ length = fields.length,
+ i = 0;
+ for (; i<length; i++) {
+ data[fields[i].getAttribute(ATTRIBUTE_FIELDS)] = fields[i].value;
+ }
+ return data;
+ },
+
+ /**
+ * Takes the attributes of the "elementToChange"
+ * and inserts them in their corresponding dialog input fields
+ *
+ * Assume the "elementToChange" looks like this:
+ * <a href="http://www.google.com" target="_blank">foo</a>
+ *
+ * and we have the following dialog:
+ * <input type="text" data-wysihtml5-dialog-field="href" value="">
+ * <input type="text" data-wysihtml5-dialog-field="target" value="">
+ *
+ * after calling _interpolate() the dialog will look like this
+ * <input type="text" data-wysihtml5-dialog-field="href" value="http://www.google.com">
+ * <input type="text" data-wysihtml5-dialog-field="target" value="_blank">
+ *
+ * Basically it adopted the attribute values into the corresponding input fields
+ *
+ */
+ _interpolate: function(avoidHiddenFields) {
+ var field,
+ fieldName,
+ newValue,
+ focusedElement = document.querySelector(":focus"),
+ fields = this.container.querySelectorAll(SELECTOR_FIELDS),
+ length = fields.length,
+ i = 0;
+ for (; i<length; i++) {
+ field = fields[i];
+
+ // Never change elements where the user is currently typing in
+ if (field === focusedElement) {
+ continue;
+ }
+
+ // Don't update hidden fields
+ // See https://github.com/xing/wysihtml5/pull/14
+ if (avoidHiddenFields && field.type === "hidden") {
+ continue;
+ }
+
+ fieldName = field.getAttribute(ATTRIBUTE_FIELDS);
+ newValue = this.elementToChange ? (this.elementToChange[fieldName] || "") : field.defaultValue;
+ field.value = newValue;
+ }
+ },
+
+ /**
+ * Show the dialog element
+ */
+ show: function(elementToChange) {
+ var that = this,
+ firstField = this.container.querySelector(SELECTOR_FORM_ELEMENTS);
+ this.elementToChange = elementToChange;
+ this._observe();
+ this._interpolate();
+ if (elementToChange) {
+ this.interval = setInterval(function() { that._interpolate(true); }, 500);
+ }
+ dom.addClass(this.link, CLASS_NAME_OPENED);
+ this.container.style.display = "";
+ this.fire("show");
+ if (firstField && !elementToChange) {
+ try {
+ firstField.focus();
+ } catch(e) {}
+ }
+ },
+
+ /**
+ * Hide the dialog element
+ */
+ hide: function() {
+ clearInterval(this.interval);
+ this.elementToChange = null;
+ dom.removeClass(this.link, CLASS_NAME_OPENED);
+ this.container.style.display = "none";
+ this.fire("hide");
+ }
+ });
+})(wysihtml5);
+/**
+ * Converts speech-to-text and inserts this into the editor
+ * As of now (2011/03/25) this only is supported in Chrome >= 11
+ *
+ * Note that it sends the recorded audio to the google speech recognition api:
+ * http://stackoverflow.com/questions/4361826/does-chrome-have-buil-in-speech-…
+ *
+ * Current HTML5 draft can be found here
+ * http://lists.w3.org/Archives/Public/public-xg-htmlspeech/2011Feb/att-0020/a…
+ *
+ * "Accessing Google Speech API Chrome 11"
+ * http://mikepultz.com/2011/03/accessing-google-speech-api-chrome-11/
+ */
+(function(wysihtml5) {
+ var dom = wysihtml5.dom;
+
+ var linkStyles = {
+ position: "relative"
+ };
+
+ var wrapperStyles = {
+ left: 0,
+ margin: 0,
+ opacity: 0,
+ overflow: "hidden",
+ padding: 0,
+ position: "absolute",
+ top: 0,
+ zIndex: 1
+ };
+
+ var inputStyles = {
+ cursor: "inherit",
+ fontSize: "50px",
+ height: "50px",
+ marginTop: "-25px",
+ outline: 0,
+ padding: 0,
+ position: "absolute",
+ right: "-4px",
+ top: "50%"
+ };
+
+ var inputAttributes = {
+ "x-webkit-speech": "",
+ "speech": ""
+ };
+
+ wysihtml5.toolbar.Speech = function(parent, link) {
+ var input = document.createElement("input");
+ if (!wysihtml5.browser.supportsSpeechApiOn(input)) {
+ link.style.display = "none";
+ return;
+ }
+
+ var wrapper = document.createElement("div");
+
+ wysihtml5.lang.object(wrapperStyles).merge({
+ width: link.offsetWidth + "px",
+ height: link.offsetHeight + "px"
+ });
+
+ dom.insert(input).into(wrapper);
+ dom.insert(wrapper).into(link);
+
+ dom.setStyles(inputStyles).on(input);
+ dom.setAttributes(inputAttributes).on(input)
+
+ dom.setStyles(wrapperStyles).on(wrapper);
+ dom.setStyles(linkStyles).on(link);
+
+ var eventName = "onwebkitspeechchange" in input ? "webkitspeechchange" : "speechchange";
+ dom.observe(input, eventName, function() {
+ parent.execCommand("insertText", input.value);
+ input.value = "";
+ });
+
+ dom.observe(input, "click", function(event) {
+ if (dom.hasClass(link, "wysihtml5-command-disabled")) {
+ event.preventDefault();
+ }
+
+ event.stopPropagation();
+ });
+ };
+})(wysihtml5);/**
+ * Toolbar
+ *
+ * @param {Object} parent Reference to instance of Editor instance
+ * @param {Element} container Reference to the toolbar container element
+ *
+ * @example
+ * <div id="toolbar">
+ * <a data-wysihtml5-command="createLink">insert link</a>
+ * <a data-wysihtml5-command="formatBlock" data-wysihtml5-command-value="h1">insert h1</a>
+ * </div>
+ *
+ * <script>
+ * var toolbar = new wysihtml5.toolbar.Toolbar(editor, document.getElementById("toolbar"));
+ * </script>
+ */
+(function(wysihtml5) {
+ var CLASS_NAME_COMMAND_DISABLED = "wysihtml5-command-disabled",
+ CLASS_NAME_COMMANDS_DISABLED = "wysihtml5-commands-disabled",
+ CLASS_NAME_COMMAND_ACTIVE = "wysihtml5-command-active",
+ CLASS_NAME_ACTION_ACTIVE = "wysihtml5-action-active",
+ dom = wysihtml5.dom;
+
+ wysihtml5.toolbar.Toolbar = Base.extend(
+ /** @scope wysihtml5.toolbar.Toolbar.prototype */ {
+ constructor: function(editor, container) {
+ this.editor = editor;
+ this.container = typeof(container) === "string" ? document.getElementById(container) : container;
+ this.composer = editor.composer;
+
+ this._getLinks("command");
+ this._getLinks("action");
+
+ this._observe();
+ this.show();
+
+ var speechInputLinks = this.container.querySelectorAll("[data-wysihtml5-command=insertSpeech]"),
+ length = speechInputLinks.length,
+ i = 0;
+ for (; i<length; i++) {
+ new wysihtml5.toolbar.Speech(this, speechInputLinks[i]);
+ }
+ },
+
+ _getLinks: function(type) {
+ var links = this[type + "Links"] = wysihtml5.lang.array(this.container.querySelectorAll("[data-wysihtml5-" + type + "]")).get(),
+ length = links.length,
+ i = 0,
+ mapping = this[type + "Mapping"] = {},
+ link,
+ group,
+ name,
+ value,
+ dialog;
+ for (; i<length; i++) {
+ link = links[i];
+ name = link.getAttribute("data-wysihtml5-" + type);
+ value = link.getAttribute("data-wysihtml5-" + type + "-value");
+ group = this.container.querySelector("[data-wysihtml5-" + type + "-group='" + name + "']");
+ dialog = this._getDialog(link, name);
+
+ mapping[name + ":" + value] = {
+ link: link,
+ group: group,
+ name: name,
+ value: value,
+ dialog: dialog,
+ state: false
+ };
+ }
+ },
+
+ _getDialog: function(link, command) {
+ var that = this,
+ dialogElement = this.container.querySelector("[data-wysihtml5-dialog='" + command + "']"),
+ dialog,
+ caretBookmark;
+
+ if (dialogElement) {
+ dialog = new wysihtml5.toolbar.Dialog(link, dialogElement);
+
+ dialog.observe("show", function() {
+ caretBookmark = that.composer.selection.getBookmark();
+
+ that.editor.fire("show:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
+ });
+
+ dialog.observe("save", function(attributes) {
+ if (caretBookmark) {
+ that.composer.selection.setBookmark(caretBookmark);
+ }
+ that._execCommand(command, attributes);
+
+ that.editor.fire("save:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
+ });
+
+ dialog.observe("cancel", function() {
+ that.editor.focus(false);
+ that.editor.fire("cancel:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
+ });
+ }
+ return dialog;
+ },
+
+ /**
+ * @example
+ * var toolbar = new wysihtml5.Toolbar();
+ * // Insert a <blockquote> element or wrap current selection in <blockquote>
+ * toolbar.execCommand("formatBlock", "blockquote");
+ */
+ execCommand: function(command, commandValue) {
+ if (this.commandsDisabled) {
+ return;
+ }
+
+ var commandObj = this.commandMapping[command + ":" + commandValue];
+
+ // Show dialog when available
+ if (commandObj && commandObj.dialog && !commandObj.state) {
+ commandObj.dialog.show();
+ } else {
+ this._execCommand(command, commandValue);
+ }
+ },
+
+ _execCommand: function(command, commandValue) {
+ // Make sure that composer is focussed (false => don't move caret to the end)
+ this.editor.focus(false);
+
+ this.composer.commands.exec(command, commandValue);
+ this._updateLinkStates();
+ },
+
+ execAction: function(action) {
+ var editor = this.editor;
+ switch(action) {
+ case "change_view":
+ if (editor.currentView === editor.textarea) {
+ editor.fire("change_view", "composer");
+ } else {
+ editor.fire("change_view", "textarea");
+ }
+ break;
+ }
+ },
+
+ _observe: function() {
+ var that = this,
+ editor = this.editor,
+ container = this.container,
+ links = this.commandLinks.concat(this.actionLinks),
+ length = links.length,
+ i = 0;
+
+ for (; i<length; i++) {
+ // 'javascript:;' and unselectable=on Needed for IE, but done in all browsers to make sure that all get the same css applied
+ // (you know, a:link { ... } doesn't match anchors with missing href attribute)
+ dom.setAttributes({
+ href: "javascript:;",
+ unselectable: "on"
+ }).on(links[i]);
+ }
+
+ // Needed for opera
+ dom.delegate(container, "[data-wysihtml5-command]", "mousedown", function(event) { event.preventDefault(); });
+
+ dom.delegate(container, "[data-wysihtml5-command]", "click", function(event) {
+ var link = this,
+ command = link.getAttribute("data-wysihtml5-command"),
+ commandValue = link.getAttribute("data-wysihtml5-command-value");
+ that.execCommand(command, commandValue);
+ event.preventDefault();
+ });
+
+ dom.delegate(container, "[data-wysihtml5-action]", "click", function(event) {
+ var action = this.getAttribute("data-wysihtml5-action");
+ that.execAction(action);
+ event.preventDefault();
+ });
+
+ editor.observe("focus:composer", function() {
+ that.bookmark = null;
+ clearInterval(that.interval);
+ that.interval = setInterval(function() { that._updateLinkStates(); }, 500);
+ });
+
+ editor.observe("blur:composer", function() {
+ clearInterval(that.interval);
+ });
+
+ editor.observe("destroy:composer", function() {
+ clearInterval(that.interval);
+ });
+
+ editor.observe("change_view", function(currentView) {
+ // Set timeout needed in order to let the blur event fire first
+ setTimeout(function() {
+ that.commandsDisabled = (currentView !== "composer");
+ that._updateLinkStates();
+ if (that.commandsDisabled) {
+ dom.addClass(container, CLASS_NAME_COMMANDS_DISABLED);
+ } else {
+ dom.removeClass(container, CLASS_NAME_COMMANDS_DISABLED);
+ }
+ }, 0);
+ });
+ },
+
+ _updateLinkStates: function() {
+ var element = this.composer.element,
+ commandMapping = this.commandMapping,
+ actionMapping = this.actionMapping,
+ i,
+ state,
+ action,
+ command;
+ // every millisecond counts... this is executed quite often
+ for (i in commandMapping) {
+ command = commandMapping[i];
+ if (this.commandsDisabled) {
+ state = false;
+ dom.removeClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
+ if (command.group) {
+ dom.removeClass(command.group, CLASS_NAME_COMMAND_ACTIVE);
+ }
+ if (command.dialog) {
+ command.dialog.hide();
+ }
+ } else {
+ state = this.composer.commands.state(command.name, command.value);
+ if (wysihtml5.lang.object(state).isArray()) {
+ // Grab first and only object/element in state array, otherwise convert state into boolean
+ // to avoid showing a dialog for multiple selected elements which may have different attributes
+ // eg. when two links with different href are selected, the state will be an array consisting of both link elements
+ // but the dialog interface can only update one
+ state = state.length === 1 ? state[0] : true;
+ }
+ dom.removeClass(command.link, CLASS_NAME_COMMAND_DISABLED);
+ if (command.group) {
+ dom.removeClass(command.group, CLASS_NAME_COMMAND_DISABLED);
+ }
+ }
+
+ if (command.state === state) {
+ continue;
+ }
+
+ command.state = state;
+ if (state) {
+ dom.addClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
+ if (command.group) {
+ dom.addClass(command.group, CLASS_NAME_COMMAND_ACTIVE);
+ }
+ if (command.dialog) {
+ if (typeof(state) === "object") {
+ command.dialog.show(state);
+ } else {
+ command.dialog.hide();
+ }
+ }
+ } else {
+ dom.removeClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
+ if (command.group) {
+ dom.removeClass(command.group, CLASS_NAME_COMMAND_ACTIVE);
+ }
+ if (command.dialog) {
+ command.dialog.hide();
+ }
+ }
+ }
+
+ for (i in actionMapping) {
+ action = actionMapping[i];
+
+ if (action.name === "change_view") {
+ action.state = this.editor.currentView === this.editor.textarea;
+ if (action.state) {
+ dom.addClass(action.link, CLASS_NAME_ACTION_ACTIVE);
+ } else {
+ dom.removeClass(action.link, CLASS_NAME_ACTION_ACTIVE);
+ }
+ }
+ }
+ },
+
+ show: function() {
+ this.container.style.display = "";
+ },
+
+ hide: function() {
+ this.container.style.display = "none";
+ }
+ });
+
+})(wysihtml5);
+/**
+ * WYSIHTML5 Editor
+ *
+ * @param {Element} textareaElement Reference to the textarea which should be turned into a rich text interface
+ * @param {Object} [config] See defaultConfig object below for explanation of each individual config option
+ *
+ * @events
+ * load
+ * beforeload (for internal use only)
+ * focus
+ * focus:composer
+ * focus:textarea
+ * blur
+ * blur:composer
+ * blur:textarea
+ * change
+ * change:composer
+ * change:textarea
+ * paste
+ * paste:composer
+ * paste:textarea
+ * newword:composer
+ * destroy:composer
+ * undo:composer
+ * redo:composer
+ * beforecommand:composer
+ * aftercommand:composer
+ * change_view
+ */
+(function(wysihtml5) {
+ var undef;
+
+ var defaultConfig = {
+ // Give the editor a name, the name will also be set as class name on the iframe and on the iframe's body
+ name: undef,
+ // Whether the editor should look like the textarea (by adopting styles)
+ style: true,
+ // Id of the toolbar element, pass falsey value if you don't want any toolbar logic
+ toolbar: undef,
+ // Whether urls, entered by the user should automatically become clickable-links
+ autoLink: true,
+ // Object which includes parser rules to apply when html gets inserted via copy & paste
+ // See parser_rules/*.js for examples
+ parserRules: { tags: { br: {}, span: {}, div: {}, p: {} }, classes: {} },
+ // Parser method to use when the user inserts content via copy & paste
+ parser: wysihtml5.dom.parse,
+ // Class name which should be set on the contentEditable element in the created sandbox iframe, can be styled via the 'stylesheets' option
+ composerClassName: "wysihtml5-editor",
+ // Class name to add to the body when the wysihtml5 editor is supported
+ bodyClassName: "wysihtml5-supported",
+ // Array (or single string) of stylesheet urls to be loaded in the editor's iframe
+ stylesheets: [],
+ // Placeholder text to use, defaults to the placeholder attribute on the textarea element
+ placeholderText: undef,
+ // Whether the composer should allow the user to manually resize images, tables etc.
+ allowObjectResizing: true,
+ // Whether the rich text editor should be rendered on touch devices (wysihtml5 >= 0.3.0 comes with basic support for iOS 5)
+ supportTouchDevices: true
+ };
+
+ wysihtml5.Editor = wysihtml5.lang.Dispatcher.extend(
+ /** @scope wysihtml5.Editor.prototype */ {
+ constructor: function(textareaElement, config) {
+ this.textareaElement = typeof(textareaElement) === "string" ? document.getElementById(textareaElement) : textareaElement;
+ this.config = wysihtml5.lang.object({}).merge(defaultConfig).merge(config).get();
+ this.textarea = new wysihtml5.views.Textarea(this, this.textareaElement, this.config);
+ this.currentView = this.textarea;
+ this._isCompatible = wysihtml5.browser.supported();
+
+ // Sort out unsupported/unwanted browsers here
+ if (!this._isCompatible || (!this.config.supportTouchDevices && wysihtml5.browser.isTouchDevice())) {
+ var that = this;
+ setTimeout(function() { that.fire("beforeload").fire("load"); }, 0);
+ return;
+ }
+
+ // Add class name to body, to indicate that the editor is supported
+ wysihtml5.dom.addClass(document.body, this.config.bodyClassName);
+
+ this.composer = new wysihtml5.views.Composer(this, this.textareaElement, this.config);
+ this.currentView = this.composer;
+
+ if (typeof(this.config.parser) === "function") {
+ this._initParser();
+ }
+
+ this.observe("beforeload", function() {
+ this.synchronizer = new wysihtml5.views.Synchronizer(this, this.textarea, this.composer);
+ if (this.config.toolbar) {
+ this.toolbar = new wysihtml5.toolbar.Toolbar(this, this.config.toolbar);
+ }
+ });
+
+ try {
+ console.log("Heya! This page is using wysihtml5 for rich text editing. Check out https://github.com/xing/wysihtml5");
+ } catch(e) {}
+ },
+
+ isCompatible: function() {
+ return this._isCompatible;
+ },
+
+ clear: function() {
+ this.currentView.clear();
+ return this;
+ },
+
+ getValue: function(parse) {
+ return this.currentView.getValue(parse);
+ },
+
+ setValue: function(html, parse) {
+ if (!html) {
+ return this.clear();
+ }
+ this.currentView.setValue(html, parse);
+ return this;
+ },
+
+ focus: function(setToEnd) {
+ this.currentView.focus(setToEnd);
+ return this;
+ },
+
+ /**
+ * Deactivate editor (make it readonly)
+ */
+ disable: function() {
+ this.currentView.disable();
+ return this;
+ },
+
+ /**
+ * Activate editor
+ */
+ enable: function() {
+ this.currentView.enable();
+ return this;
+ },
+
+ isEmpty: function() {
+ return this.currentView.isEmpty();
+ },
+
+ hasPlaceholderSet: function() {
+ return this.currentView.hasPlaceholderSet();
+ },
+
+ parse: function(htmlOrElement) {
+ var returnValue = this.config.parser(htmlOrElement, this.config.parserRules, this.composer.sandbox.getDocument(), true);
+ if (typeof(htmlOrElement) === "object") {
+ wysihtml5.quirks.redraw(htmlOrElement);
+ }
+ return returnValue;
+ },
+
+ /**
+ * Prepare html parser logic
+ * - Observes for paste and drop
+ */
+ _initParser: function() {
+ this.observe("paste:composer", function() {
+ var keepScrollPosition = true,
+ that = this;
+ that.composer.selection.executeAndRestore(function() {
+ wysihtml5.quirks.cleanPastedHTML(that.composer.element);
+ that.parse(that.composer.element);
+ }, keepScrollPosition);
+ });
+
+ this.observe("paste:textarea", function() {
+ var value = this.textarea.getValue(),
+ newValue;
+ newValue = this.parse(value);
+ this.textarea.setValue(newValue);
+ });
+ }
+ });
+})(wysihtml5);
Added: trunk/wao-web/src/main/webapp/wysihtml5-0.3.0/wysihtml5.min.js
===================================================================
--- trunk/wao-web/src/main/webapp/wysihtml5-0.3.0/wysihtml5.min.js (rev 0)
+++ trunk/wao-web/src/main/webapp/wysihtml5-0.3.0/wysihtml5.min.js 2014-04-15 08:01:22 UTC (rev 1885)
@@ -0,0 +1,261 @@
+/*
+ wysihtml5 v0.3.0
+ https://github.com/xing/wysihtml5
+
+ Author: Christopher Blum (https://github.com/tiff)
+
+ Copyright (C) 2012 XING AG
+ Licensed under the MIT license (MIT)
+
+ Rangy, a cross-browser JavaScript range and selection library
+ http://code.google.com/p/rangy/
+
+ Copyright 2011, Tim Down
+ Licensed under the MIT license.
+ Version: 1.2.2
+ Build date: 13 November 2011
+*/
+var wysihtml5={version:"0.3.0",commands:{},dom:{},quirks:{},toolbar:{},lang:{},selection:{},views:{},INVISIBLE_SPACE:"\ufeff",EMPTY_FUNCTION:function(){},ELEMENT_NODE:1,TEXT_NODE:3,BACKSPACE_KEY:8,ENTER_KEY:13,ESCAPE_KEY:27,SPACE_KEY:32,DELETE_KEY:46};
+window.rangy=function(){function b(a,b){var c=typeof a[b];return c==k||!!(c==h&&a[b])||"unknown"==c}function c(a,b){return!!(typeof a[b]==h&&a[b])}function a(a,b){return typeof a[b]!=j}function d(a){return function(b,c){for(var d=c.length;d--;)if(!a(b,c[d]))return!1;return!0}}function e(a){return a&&m(a,r)&&x(a,q)}function f(a){window.alert("Rangy not supported in your browser. Reason: "+a);o.initialized=!0;o.supported=!1}function g(){if(!o.initialized){var a,d=!1,h=!1;b(document,"createRange")&&
+(a=document.createRange(),m(a,p)&&x(a,n)&&(d=!0),a.detach());if((a=c(document,"body")?document.body:document.getElementsByTagName("body")[0])&&b(a,"createTextRange"))a=a.createTextRange(),e(a)&&(h=!0);!d&&!h&&f("Neither Range nor TextRange are implemented");o.initialized=!0;o.features={implementsDomRange:d,implementsTextRange:h};d=w.concat(z);h=0;for(a=d.length;h<a;++h)try{d[h](o)}catch(j){c(window,"console")&&b(window.console,"log")&&window.console.log("Init listener threw an exception. Continuing.",
+j)}}}function i(a){this.name=a;this.supported=this.initialized=!1}var h="object",k="function",j="undefined",n="startContainer startOffset endContainer endOffset collapsed commonAncestorContainer START_TO_START START_TO_END END_TO_START END_TO_END".split(" "),p="setStart setStartBefore setStartAfter setEnd setEndBefore setEndAfter collapse selectNode selectNodeContents compareBoundaryPoints deleteContents extractContents cloneContents insertNode surroundContents cloneRange toString detach".split(" "),
+q="boundingHeight boundingLeft boundingTop boundingWidth htmlText text".split(" "),r="collapse compareEndPoints duplicate getBookmark moveToBookmark moveToElementText parentElement pasteHTML select setEndPoint getBoundingClientRect".split(" "),m=d(b),s=d(c),x=d(a),o={version:"1.2.2",initialized:!1,supported:!0,util:{isHostMethod:b,isHostObject:c,isHostProperty:a,areHostMethods:m,areHostObjects:s,areHostProperties:x,isTextRange:e},features:{},modules:{},config:{alertOnWarn:!1,preferTextRange:!1}};
+o.fail=f;o.warn=function(a){a="Rangy warning: "+a;o.config.alertOnWarn?window.alert(a):typeof window.console!=j&&typeof window.console.log!=j&&window.console.log(a)};({}).hasOwnProperty?o.util.extend=function(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c])}:f("hasOwnProperty not supported");var z=[],w=[];o.init=g;o.addInitListener=function(a){o.initialized?a(o):z.push(a)};var y=[];o.addCreateMissingNativeApiListener=function(a){y.push(a)};o.createMissingNativeApi=function(a){a=a||window;g();
+for(var b=0,c=y.length;b<c;++b)y[b](a)};i.prototype.fail=function(a){this.initialized=!0;this.supported=!1;throw Error("Module '"+this.name+"' failed to load: "+a);};i.prototype.warn=function(a){o.warn("Module "+this.name+": "+a)};i.prototype.createError=function(a){return Error("Error in Rangy "+this.name+" module: "+a)};o.createModule=function(a,b){var c=new i(a);o.modules[a]=c;w.push(function(a){b(a,c);c.initialized=!0;c.supported=!0})};o.requireModules=function(a){for(var b=0,c=a.length,d,h;b<
+c;++b){h=a[b];d=o.modules[h];if(!d||!(d instanceof i))throw Error("Module '"+h+"' not found");if(!d.supported)throw Error("Module '"+h+"' not supported");}};var A=!1,s=function(){A||(A=!0,o.initialized||g())};if(typeof window==j)f("No window found");else if(typeof document==j)f("No document found");else return b(document,"addEventListener")&&document.addEventListener("DOMContentLoaded",s,!1),b(window,"addEventListener")?window.addEventListener("load",s,!1):b(window,"attachEvent")?window.attachEvent("onload",
+s):f("Window does not have required addEventListener or attachEvent method"),o}();
+rangy.createModule("DomUtil",function(b,c){function a(a){for(var b=0;a=a.previousSibling;)b++;return b}function d(a,b){var c=[],d;for(d=a;d;d=d.parentNode)c.push(d);for(d=b;d;d=d.parentNode)if(m(c,d))return d;return null}function e(a,b,c){for(c=c?a:a.parentNode;c;){a=c.parentNode;if(a===b)return c;c=a}return null}function f(a){a=a.nodeType;return 3==a||4==a||8==a}function g(a,b){var c=b.nextSibling,d=b.parentNode;c?d.insertBefore(a,c):d.appendChild(a);return a}function i(a){if(9==a.nodeType)return a;
+if(typeof a.ownerDocument!=p)return a.ownerDocument;if(typeof a.document!=p)return a.document;if(a.parentNode)return i(a.parentNode);throw Error("getDocument: no document found for node");}function h(a){return!a?"[No node]":f(a)?'"'+a.data+'"':1==a.nodeType?"<"+a.nodeName+(a.id?' id="'+a.id+'"':"")+">["+a.childNodes.length+"]":a.nodeName}function k(a){this._next=this.root=a}function j(a,b){this.node=a;this.offset=b}function n(a){this.code=this[a];this.codeName=a;this.message="DOMException: "+this.codeName}
+var p="undefined",q=b.util;q.areHostMethods(document,["createDocumentFragment","createElement","createTextNode"])||c.fail("document missing a Node creation method");q.isHostMethod(document,"getElementsByTagName")||c.fail("document missing getElementsByTagName method");var r=document.createElement("div");q.areHostMethods(r,["insertBefore","appendChild","cloneNode"])||c.fail("Incomplete Element implementation");q.isHostProperty(r,"innerHTML")||c.fail("Element is missing innerHTML property");r=document.createTextNode("test");
+q.areHostMethods(r,["splitText","deleteData","insertData","appendData","cloneNode"])||c.fail("Incomplete Text Node implementation");var m=function(a,b){for(var c=a.length;c--;)if(a[c]===b)return!0;return!1};k.prototype={_current:null,hasNext:function(){return!!this._next},next:function(){var a=this._current=this._next,b;if(this._current){b=a.firstChild;if(!b)for(b=null;a!==this.root&&!(b=a.nextSibling);)a=a.parentNode;this._next=b}return this._current},detach:function(){this._current=this._next=this.root=
+null}};j.prototype={equals:function(a){return this.node===a.node&this.offset==a.offset},inspect:function(){return"[DomPosition("+h(this.node)+":"+this.offset+")]"}};n.prototype={INDEX_SIZE_ERR:1,HIERARCHY_REQUEST_ERR:3,WRONG_DOCUMENT_ERR:4,NO_MODIFICATION_ALLOWED_ERR:7,NOT_FOUND_ERR:8,NOT_SUPPORTED_ERR:9,INVALID_STATE_ERR:11};n.prototype.toString=function(){return this.message};b.dom={arrayContains:m,isHtmlNamespace:function(a){var b;return typeof a.namespaceURI==p||null===(b=a.namespaceURI)||"http://www.w3.org/1999/xhtml"==
+b},parentElement:function(a){a=a.parentNode;return 1==a.nodeType?a:null},getNodeIndex:a,getNodeLength:function(a){var b;return f(a)?a.length:(b=a.childNodes)?b.length:0},getCommonAncestor:d,isAncestorOf:function(a,b,c){for(b=c?b:b.parentNode;b;){if(b===a)return!0;b=b.parentNode}return!1},getClosestAncestorIn:e,isCharacterDataNode:f,insertAfter:g,splitDataNode:function(a,b){var c=a.cloneNode(!1);c.deleteData(0,b);a.deleteData(b,a.length-b);g(c,a);return c},getDocument:i,getWindow:function(a){a=i(a);
+if(typeof a.defaultView!=p)return a.defaultView;if(typeof a.parentWindow!=p)return a.parentWindow;throw Error("Cannot get a window object for node");},getIframeWindow:function(a){if(typeof a.contentWindow!=p)return a.contentWindow;if(typeof a.contentDocument!=p)return a.contentDocument.defaultView;throw Error("getIframeWindow: No Window object found for iframe element");},getIframeDocument:function(a){if(typeof a.contentDocument!=p)return a.contentDocument;if(typeof a.contentWindow!=p)return a.contentWindow.document;
+throw Error("getIframeWindow: No Document object found for iframe element");},getBody:function(a){return q.isHostObject(a,"body")?a.body:a.getElementsByTagName("body")[0]},getRootContainer:function(a){for(var b;b=a.parentNode;)a=b;return a},comparePoints:function(b,c,h,j){var k;if(b==h)return c===j?0:c<j?-1:1;if(k=e(h,b,!0))return c<=a(k)?-1:1;if(k=e(b,h,!0))return a(k)<j?-1:1;c=d(b,h);b=b===c?c:e(b,c,!0);h=h===c?c:e(h,c,!0);if(b===h)throw Error("comparePoints got to case 4 and childA and childB are the same!");
+for(c=c.firstChild;c;){if(c===b)return-1;if(c===h)return 1;c=c.nextSibling}throw Error("Should not be here!");},inspectNode:h,fragmentFromNodeChildren:function(a){for(var b=i(a).createDocumentFragment(),c;c=a.firstChild;)b.appendChild(c);return b},createIterator:function(a){return new k(a)},DomPosition:j};b.DOMException=n});
+rangy.createModule("DomRange",function(b){function c(a,b){return 3!=a.nodeType&&(l.isAncestorOf(a,b.startContainer,!0)||l.isAncestorOf(a,b.endContainer,!0))}function a(a){return l.getDocument(a.startContainer)}function d(a,b,c){if(b=a._listeners[b])for(var d=0,h=b.length;d<h;++d)b[d].call(a,{target:a,args:c})}function e(a){return new u(a.parentNode,l.getNodeIndex(a))}function f(a){return new u(a.parentNode,l.getNodeIndex(a)+1)}function g(a,b,c){var d=11==a.nodeType?a.firstChild:a;l.isCharacterDataNode(b)?
+c==b.length?l.insertAfter(a,b):b.parentNode.insertBefore(a,0==c?b:l.splitDataNode(b,c)):c>=b.childNodes.length?b.appendChild(a):b.insertBefore(a,b.childNodes[c]);return d}function i(b){for(var c,d,h=a(b.range).createDocumentFragment();d=b.next();){c=b.isPartiallySelectedSubtree();d=d.cloneNode(!c);c&&(c=b.getSubtreeIterator(),d.appendChild(i(c)),c.detach(!0));if(10==d.nodeType)throw new B("HIERARCHY_REQUEST_ERR");h.appendChild(d)}return h}function h(a,b,c){for(var d,e,c=c||{stop:!1};d=a.next();)if(a.isPartiallySelectedSubtree())if(!1===
+b(d)){c.stop=!0;break}else{if(d=a.getSubtreeIterator(),h(d,b,c),d.detach(!0),c.stop)break}else for(d=l.createIterator(d);e=d.next();)if(!1===b(e)){c.stop=!0;return}}function k(a){for(var b;a.next();)a.isPartiallySelectedSubtree()?(b=a.getSubtreeIterator(),k(b),b.detach(!0)):a.remove()}function j(b){for(var c,d=a(b.range).createDocumentFragment(),h;c=b.next();){b.isPartiallySelectedSubtree()?(c=c.cloneNode(!1),h=b.getSubtreeIterator(),c.appendChild(j(h)),h.detach(!0)):b.remove();if(10==c.nodeType)throw new B("HIERARCHY_REQUEST_ERR");
+d.appendChild(c)}return d}function n(a,b,c){var d=!(!b||!b.length),e,j=!!c;d&&(e=RegExp("^("+b.join("|")+")$"));var k=[];h(new q(a,!1),function(a){(!d||e.test(a.nodeType))&&(!j||c(a))&&k.push(a)});return k}function p(a){return"["+("undefined"==typeof a.getName?"Range":a.getName())+"("+l.inspectNode(a.startContainer)+":"+a.startOffset+", "+l.inspectNode(a.endContainer)+":"+a.endOffset+")]"}function q(a,b){this.range=a;this.clonePartiallySelectedTextNodes=b;if(!a.collapsed){this.sc=a.startContainer;
+this.so=a.startOffset;this.ec=a.endContainer;this.eo=a.endOffset;var c=a.commonAncestorContainer;this.sc===this.ec&&l.isCharacterDataNode(this.sc)?(this.isSingleCharacterDataNode=!0,this._first=this._last=this._next=this.sc):(this._first=this._next=this.sc===c&&!l.isCharacterDataNode(this.sc)?this.sc.childNodes[this.so]:l.getClosestAncestorIn(this.sc,c,!0),this._last=this.ec===c&&!l.isCharacterDataNode(this.ec)?this.ec.childNodes[this.eo-1]:l.getClosestAncestorIn(this.ec,c,!0))}}function r(a){this.code=
+this[a];this.codeName=a;this.message="RangeException: "+this.codeName}function m(a,b,c){this.nodes=n(a,b,c);this._next=this.nodes[0];this._position=0}function s(a){return function(b,c){for(var d,h=c?b:b.parentNode;h;){d=h.nodeType;if(l.arrayContains(a,d))return h;h=h.parentNode}return null}}function x(a,b){if($(a,b))throw new r("INVALID_NODE_TYPE_ERR");}function o(a){if(!a.startContainer)throw new B("INVALID_STATE_ERR");}function z(a,b){if(!l.arrayContains(b,a.nodeType))throw new r("INVALID_NODE_TYPE_ERR");
+}function w(a,b){if(0>b||b>(l.isCharacterDataNode(a)?a.length:a.childNodes.length))throw new B("INDEX_SIZE_ERR");}function y(a,b){if(O(a,!0)!==O(b,!0))throw new B("WRONG_DOCUMENT_ERR");}function A(a){if(aa(a,!0))throw new B("NO_MODIFICATION_ALLOWED_ERR");}function t(a,b){if(!a)throw new B(b);}function v(a){o(a);if(!l.arrayContains(G,a.startContainer.nodeType)&&!O(a.startContainer,!0)||!l.arrayContains(G,a.endContainer.nodeType)&&!O(a.endContainer,!0)||!(a.startOffset<=(l.isCharacterDataNode(a.startContainer)?
+a.startContainer.length:a.startContainer.childNodes.length))||!(a.endOffset<=(l.isCharacterDataNode(a.endContainer)?a.endContainer.length:a.endContainer.childNodes.length)))throw Error("Range error: Range is no longer valid after DOM mutation ("+a.inspect()+")");}function D(){}function K(a){a.START_TO_START=Q;a.START_TO_END=U;a.END_TO_END=ba;a.END_TO_START=V;a.NODE_BEFORE=W;a.NODE_AFTER=X;a.NODE_BEFORE_AND_AFTER=Y;a.NODE_INSIDE=R}function F(a){K(a);K(a.prototype)}function E(a,b){return function(){v(this);
+var c=this.startContainer,d=this.startOffset,e=this.commonAncestorContainer,j=new q(this,!0);c!==e&&(c=l.getClosestAncestorIn(c,e,!0),d=f(c),c=d.node,d=d.offset);h(j,A);j.reset();e=a(j);j.detach();b(this,c,d,c,d);return e}}function I(a,d,h){function g(a,b){return function(c){o(this);z(c,L);z(M(c),G);c=(a?e:f)(c);(b?i:n)(this,c.node,c.offset)}}function i(a,b,c){var h=a.endContainer,e=a.endOffset;if(b!==a.startContainer||c!==a.startOffset){if(M(b)!=M(h)||1==l.comparePoints(b,c,h,e))h=b,e=c;d(a,b,c,
+h,e)}}function n(a,b,c){var h=a.startContainer,e=a.startOffset;if(b!==a.endContainer||c!==a.endOffset){if(M(b)!=M(h)||-1==l.comparePoints(b,c,h,e))h=b,e=c;d(a,h,e,b,c)}}a.prototype=new D;b.util.extend(a.prototype,{setStart:function(a,b){o(this);x(a,!0);w(a,b);i(this,a,b)},setEnd:function(a,b){o(this);x(a,!0);w(a,b);n(this,a,b)},setStartBefore:g(!0,!0),setStartAfter:g(!1,!0),setEndBefore:g(!0,!1),setEndAfter:g(!1,!1),collapse:function(a){v(this);a?d(this,this.startContainer,this.startOffset,this.startContainer,
+this.startOffset):d(this,this.endContainer,this.endOffset,this.endContainer,this.endOffset)},selectNodeContents:function(a){o(this);x(a,!0);d(this,a,0,a,l.getNodeLength(a))},selectNode:function(a){o(this);x(a,!1);z(a,L);var b=e(a),a=f(a);d(this,b.node,b.offset,a.node,a.offset)},extractContents:E(j,d),deleteContents:E(k,d),canSurroundContents:function(){v(this);A(this.startContainer);A(this.endContainer);var a=new q(this,!0),b=a._first&&c(a._first,this)||a._last&&c(a._last,this);a.detach();return!b},
+detach:function(){h(this)},splitBoundaries:function(){v(this);var a=this.startContainer,b=this.startOffset,c=this.endContainer,h=this.endOffset,e=a===c;l.isCharacterDataNode(c)&&(0<h&&h<c.length)&&l.splitDataNode(c,h);l.isCharacterDataNode(a)&&(0<b&&b<a.length)&&(a=l.splitDataNode(a,b),e?(h-=b,c=a):c==a.parentNode&&h>=l.getNodeIndex(a)&&h++,b=0);d(this,a,b,c,h)},normalizeBoundaries:function(){v(this);var a=this.startContainer,b=this.startOffset,c=this.endContainer,h=this.endOffset,e=function(a){var b=
+a.nextSibling;b&&b.nodeType==a.nodeType&&(c=a,h=a.length,a.appendData(b.data),b.parentNode.removeChild(b))},j=function(d){var e=d.previousSibling;if(e&&e.nodeType==d.nodeType){a=d;var j=d.length;b=e.length;d.insertData(0,e.data);e.parentNode.removeChild(e);a==c?(h+=b,c=a):c==d.parentNode&&(e=l.getNodeIndex(d),h==e?(c=d,h=j):h>e&&h--)}},k=!0;l.isCharacterDataNode(c)?c.length==h&&e(c):(0<h&&(k=c.childNodes[h-1])&&l.isCharacterDataNode(k)&&e(k),k=!this.collapsed);k?l.isCharacterDataNode(a)?0==b&&j(a):
+b<a.childNodes.length&&(e=a.childNodes[b])&&l.isCharacterDataNode(e)&&j(e):(a=c,b=h);d(this,a,b,c,h)},collapseToPoint:function(a,b){o(this);x(a,!0);w(a,b);(a!==this.startContainer||b!==this.startOffset||a!==this.endContainer||b!==this.endOffset)&&d(this,a,b,a,b)}});F(a)}function N(a){a.collapsed=a.startContainer===a.endContainer&&a.startOffset===a.endOffset;a.commonAncestorContainer=a.collapsed?a.startContainer:l.getCommonAncestor(a.startContainer,a.endContainer)}function J(a,b,c,h,e){var j=a.startContainer!==
+b||a.startOffset!==c,k=a.endContainer!==h||a.endOffset!==e;a.startContainer=b;a.startOffset=c;a.endContainer=h;a.endOffset=e;N(a);d(a,"boundarychange",{startMoved:j,endMoved:k})}function C(a){this.startContainer=a;this.startOffset=0;this.endContainer=a;this.endOffset=0;this._listeners={boundarychange:[],detach:[]};N(this)}b.requireModules(["DomUtil"]);var l=b.dom,u=l.DomPosition,B=b.DOMException;q.prototype={_current:null,_next:null,_first:null,_last:null,isSingleCharacterDataNode:!1,reset:function(){this._current=
+null;this._next=this._first},hasNext:function(){return!!this._next},next:function(){var a=this._current=this._next;a&&(this._next=a!==this._last?a.nextSibling:null,l.isCharacterDataNode(a)&&this.clonePartiallySelectedTextNodes&&(a===this.ec&&(a=a.cloneNode(!0)).deleteData(this.eo,a.length-this.eo),this._current===this.sc&&(a=a.cloneNode(!0)).deleteData(0,this.so)));return a},remove:function(){var a=this._current,b,c;l.isCharacterDataNode(a)&&(a===this.sc||a===this.ec)?(b=a===this.sc?this.so:0,c=a===
+this.ec?this.eo:a.length,b!=c&&a.deleteData(b,c-b)):a.parentNode&&a.parentNode.removeChild(a)},isPartiallySelectedSubtree:function(){return c(this._current,this.range)},getSubtreeIterator:function(){var b;if(this.isSingleCharacterDataNode)b=this.range.cloneRange(),b.collapse();else{b=new C(a(this.range));var c=this._current,d=c,h=0,e=c,j=l.getNodeLength(c);l.isAncestorOf(c,this.sc,!0)&&(d=this.sc,h=this.so);l.isAncestorOf(c,this.ec,!0)&&(e=this.ec,j=this.eo);J(b,d,h,e,j)}return new q(b,this.clonePartiallySelectedTextNodes)},
+detach:function(a){a&&this.range.detach();this.range=this._current=this._next=this._first=this._last=this.sc=this.so=this.ec=this.eo=null}};r.prototype={BAD_BOUNDARYPOINTS_ERR:1,INVALID_NODE_TYPE_ERR:2};r.prototype.toString=function(){return this.message};m.prototype={_current:null,hasNext:function(){return!!this._next},next:function(){this._current=this._next;this._next=this.nodes[++this._position];return this._current},detach:function(){this._current=this._next=this.nodes=null}};var L=[1,3,4,5,
+7,8,10],G=[2,9,11],P=[1,3,4,5,7,8,10,11],H=[1,3,4,5,7,8],M=l.getRootContainer,O=s([9,11]),aa=s([5,6,10,12]),$=s([6,10,12]),Z=document.createElement("style"),S=!1;try{Z.innerHTML="<b>x</b>",S=3==Z.firstChild.nodeType}catch(ca){}b.features.htmlParsingConforms=S;var T="startContainer startOffset endContainer endOffset collapsed commonAncestorContainer".split(" "),Q=0,U=1,ba=2,V=3,W=0,X=1,Y=2,R=3;D.prototype={attachListener:function(a,b){this._listeners[a].push(b)},compareBoundaryPoints:function(a,b){v(this);
+y(this.startContainer,b.startContainer);var c=a==V||a==Q?"start":"end",d=a==U||a==Q?"start":"end";return l.comparePoints(this[c+"Container"],this[c+"Offset"],b[d+"Container"],b[d+"Offset"])},insertNode:function(a){v(this);z(a,P);A(this.startContainer);if(l.isAncestorOf(a,this.startContainer,!0))throw new B("HIERARCHY_REQUEST_ERR");this.setStartBefore(g(a,this.startContainer,this.startOffset))},cloneContents:function(){v(this);var b,c;if(this.collapsed)return a(this).createDocumentFragment();if(this.startContainer===
+this.endContainer&&l.isCharacterDataNode(this.startContainer))return b=this.startContainer.cloneNode(!0),b.data=b.data.slice(this.startOffset,this.endOffset),c=a(this).createDocumentFragment(),c.appendChild(b),c;c=new q(this,!0);b=i(c);c.detach();return b},canSurroundContents:function(){v(this);A(this.startContainer);A(this.endContainer);var a=new q(this,!0),b=a._first&&c(a._first,this)||a._last&&c(a._last,this);a.detach();return!b},surroundContents:function(a){z(a,H);if(!this.canSurroundContents())throw new r("BAD_BOUNDARYPOINTS_ERR");
+var b=this.extractContents();if(a.hasChildNodes())for(;a.lastChild;)a.removeChild(a.lastChild);g(a,this.startContainer,this.startOffset);a.appendChild(b);this.selectNode(a)},cloneRange:function(){v(this);for(var b=new C(a(this)),c=T.length,d;c--;)d=T[c],b[d]=this[d];return b},toString:function(){v(this);var a=this.startContainer;if(a===this.endContainer&&l.isCharacterDataNode(a))return 3==a.nodeType||4==a.nodeType?a.data.slice(this.startOffset,this.endOffset):"";var b=[],a=new q(this,!0);h(a,function(a){(3==
+a.nodeType||4==a.nodeType)&&b.push(a.data)});a.detach();return b.join("")},compareNode:function(a){v(this);var b=a.parentNode,c=l.getNodeIndex(a);if(!b)throw new B("NOT_FOUND_ERR");a=this.comparePoint(b,c);b=this.comparePoint(b,c+1);return 0>a?0<b?Y:W:0<b?X:R},comparePoint:function(a,b){v(this);t(a,"HIERARCHY_REQUEST_ERR");y(a,this.startContainer);return 0>l.comparePoints(a,b,this.startContainer,this.startOffset)?-1:0<l.comparePoints(a,b,this.endContainer,this.endOffset)?1:0},createContextualFragment:S?
+function(a){var b=this.startContainer,c=l.getDocument(b);if(!b)throw new B("INVALID_STATE_ERR");var d=null;1==b.nodeType?d=b:l.isCharacterDataNode(b)&&(d=l.parentElement(b));d=null===d||"HTML"==d.nodeName&&l.isHtmlNamespace(l.getDocument(d).documentElement)&&l.isHtmlNamespace(d)?c.createElement("body"):d.cloneNode(!1);d.innerHTML=a;return l.fragmentFromNodeChildren(d)}:function(b){o(this);var c=a(this).createElement("body");c.innerHTML=b;return l.fragmentFromNodeChildren(c)},toHtml:function(){v(this);
+var b=a(this).createElement("div");b.appendChild(this.cloneContents());return b.innerHTML},intersectsNode:function(b,c){v(this);t(b,"NOT_FOUND_ERR");if(l.getDocument(b)!==a(this))return!1;var d=b.parentNode,h=l.getNodeIndex(b);t(d,"NOT_FOUND_ERR");var e=l.comparePoints(d,h,this.endContainer,this.endOffset),d=l.comparePoints(d,h+1,this.startContainer,this.startOffset);return c?0>=e&&0<=d:0>e&&0<d},isPointInRange:function(a,b){v(this);t(a,"HIERARCHY_REQUEST_ERR");y(a,this.startContainer);return 0<=
+l.comparePoints(a,b,this.startContainer,this.startOffset)&&0>=l.comparePoints(a,b,this.endContainer,this.endOffset)},intersectsRange:function(b,c){v(this);if(a(b)!=a(this))throw new B("WRONG_DOCUMENT_ERR");var d=l.comparePoints(this.startContainer,this.startOffset,b.endContainer,b.endOffset),h=l.comparePoints(this.endContainer,this.endOffset,b.startContainer,b.startOffset);return c?0>=d&&0<=h:0>d&&0<h},intersection:function(a){if(this.intersectsRange(a)){var b=l.comparePoints(this.startContainer,
+this.startOffset,a.startContainer,a.startOffset),c=l.comparePoints(this.endContainer,this.endOffset,a.endContainer,a.endOffset),d=this.cloneRange();-1==b&&d.setStart(a.startContainer,a.startOffset);1==c&&d.setEnd(a.endContainer,a.endOffset);return d}return null},union:function(a){if(this.intersectsRange(a,!0)){var b=this.cloneRange();-1==l.comparePoints(a.startContainer,a.startOffset,this.startContainer,this.startOffset)&&b.setStart(a.startContainer,a.startOffset);1==l.comparePoints(a.endContainer,
+a.endOffset,this.endContainer,this.endOffset)&&b.setEnd(a.endContainer,a.endOffset);return b}throw new r("Ranges do not intersect");},containsNode:function(a,b){return b?this.intersectsNode(a,!1):this.compareNode(a)==R},containsNodeContents:function(a){return 0<=this.comparePoint(a,0)&&0>=this.comparePoint(a,l.getNodeLength(a))},containsRange:function(a){return this.intersection(a).equals(a)},containsNodeText:function(a){var b=this.cloneRange();b.selectNode(a);var c=b.getNodes([3]);return 0<c.length?
+(b.setStart(c[0],0),a=c.pop(),b.setEnd(a,a.length),a=this.containsRange(b),b.detach(),a):this.containsNodeContents(a)},createNodeIterator:function(a,b){v(this);return new m(this,a,b)},getNodes:function(a,b){v(this);return n(this,a,b)},getDocument:function(){return a(this)},collapseBefore:function(a){o(this);this.setEndBefore(a);this.collapse(!1)},collapseAfter:function(a){o(this);this.setStartAfter(a);this.collapse(!0)},getName:function(){return"DomRange"},equals:function(a){return C.rangesEqual(this,
+a)},inspect:function(){return p(this)}};I(C,J,function(a){o(a);a.startContainer=a.startOffset=a.endContainer=a.endOffset=null;a.collapsed=a.commonAncestorContainer=null;d(a,"detach",null);a._listeners=null});b.rangePrototype=D.prototype;C.rangeProperties=T;C.RangeIterator=q;C.copyComparisonConstants=F;C.createPrototypeRange=I;C.inspect=p;C.getRangeDocument=a;C.rangesEqual=function(a,b){return a.startContainer===b.startContainer&&a.startOffset===b.startOffset&&a.endContainer===b.endContainer&&a.endOffset===
+b.endOffset};b.DomRange=C;b.RangeException=r});
+rangy.createModule("WrappedRange",function(b){function c(a,b,c,d){var g=a.duplicate();g.collapse(c);var i=g.parentElement();e.isAncestorOf(b,i,!0)||(i=b);if(!i.canHaveHTML)return new f(i.parentNode,e.getNodeIndex(i));var b=e.getDocument(i).createElement("span"),r,m=c?"StartToStart":"StartToEnd";do i.insertBefore(b,b.previousSibling),g.moveToElementText(b);while(0<(r=g.compareEndPoints(m,a))&&b.previousSibling);m=b.nextSibling;if(-1==r&&m&&e.isCharacterDataNode(m)){g.setEndPoint(c?"EndToStart":"EndToEnd",
+a);if(/[\r\n]/.test(m.data)){i=g.duplicate();c=i.text.replace(/\r\n/g,"\r").length;for(c=i.moveStart("character",c);-1==i.compareEndPoints("StartToEnd",i);)c++,i.moveStart("character",1)}else c=g.text.length;i=new f(m,c)}else m=(d||!c)&&b.previousSibling,i=(c=(d||c)&&b.nextSibling)&&e.isCharacterDataNode(c)?new f(c,0):m&&e.isCharacterDataNode(m)?new f(m,m.length):new f(i,e.getNodeIndex(b));b.parentNode.removeChild(b);return i}function a(a,b){var c,d,f=a.offset,g=e.getDocument(a.node),i=g.body.createTextRange(),
+m=e.isCharacterDataNode(a.node);m?(c=a.node,d=c.parentNode):(c=a.node.childNodes,c=f<c.length?c[f]:null,d=a.node);g=g.createElement("span");g.innerHTML="&#feff;";c?d.insertBefore(g,c):d.appendChild(g);i.moveToElementText(g);i.collapse(!b);d.removeChild(g);if(m)i[b?"moveStart":"moveEnd"]("character",f);return i}b.requireModules(["DomUtil","DomRange"]);var d,e=b.dom,f=e.DomPosition,g=b.DomRange;if(b.features.implementsDomRange&&(!b.features.implementsTextRange||!b.config.preferTextRange))(function(){function a(b){for(var c=
+j.length,d;c--;)d=j[c],b[d]=b.nativeRange[d]}var c,j=g.rangeProperties,f;d=function(b){if(!b)throw Error("Range must be specified");this.nativeRange=b;a(this)};g.createPrototypeRange(d,function(a,b,c,d,h){var e=a.endContainer!==d||a.endOffset!=h;if(a.startContainer!==b||a.startOffset!=c||e)a.setEnd(d,h),a.setStart(b,c)},function(a){a.nativeRange.detach();a.detached=!0;for(var b=j.length,c;b--;)c=j[b],a[c]=null});c=d.prototype;c.selectNode=function(b){this.nativeRange.selectNode(b);a(this)};c.deleteContents=
+function(){this.nativeRange.deleteContents();a(this)};c.extractContents=function(){var b=this.nativeRange.extractContents();a(this);return b};c.cloneContents=function(){return this.nativeRange.cloneContents()};c.surroundContents=function(b){this.nativeRange.surroundContents(b);a(this)};c.collapse=function(b){this.nativeRange.collapse(b);a(this)};c.cloneRange=function(){return new d(this.nativeRange.cloneRange())};c.refresh=function(){a(this)};c.toString=function(){return this.nativeRange.toString()};
+var i=document.createTextNode("test");e.getBody(document).appendChild(i);var q=document.createRange();q.setStart(i,0);q.setEnd(i,0);try{q.setStart(i,1),c.setStart=function(b,c){this.nativeRange.setStart(b,c);a(this)},c.setEnd=function(b,c){this.nativeRange.setEnd(b,c);a(this)},f=function(b){return function(c){this.nativeRange[b](c);a(this)}}}catch(r){c.setStart=function(b,c){try{this.nativeRange.setStart(b,c)}catch(d){this.nativeRange.setEnd(b,c),this.nativeRange.setStart(b,c)}a(this)},c.setEnd=function(b,
+c){try{this.nativeRange.setEnd(b,c)}catch(d){this.nativeRange.setStart(b,c),this.nativeRange.setEnd(b,c)}a(this)},f=function(b,c){return function(d){try{this.nativeRange[b](d)}catch(e){this.nativeRange[c](d),this.nativeRange[b](d)}a(this)}}}c.setStartBefore=f("setStartBefore","setEndBefore");c.setStartAfter=f("setStartAfter","setEndAfter");c.setEndBefore=f("setEndBefore","setStartBefore");c.setEndAfter=f("setEndAfter","setStartAfter");q.selectNodeContents(i);c.selectNodeContents=q.startContainer==
+i&&q.endContainer==i&&0==q.startOffset&&q.endOffset==i.length?function(b){this.nativeRange.selectNodeContents(b);a(this)}:function(a){this.setStart(a,0);this.setEnd(a,g.getEndOffset(a))};q.selectNodeContents(i);q.setEnd(i,3);f=document.createRange();f.selectNodeContents(i);f.setEnd(i,4);f.setStart(i,2);c.compareBoundaryPoints=-1==q.compareBoundaryPoints(q.START_TO_END,f)&1==q.compareBoundaryPoints(q.END_TO_START,f)?function(a,b){b=b.nativeRange||b;a==b.START_TO_END?a=b.END_TO_START:a==b.END_TO_START&&
+(a=b.START_TO_END);return this.nativeRange.compareBoundaryPoints(a,b)}:function(a,b){return this.nativeRange.compareBoundaryPoints(a,b.nativeRange||b)};b.util.isHostMethod(q,"createContextualFragment")&&(c.createContextualFragment=function(a){return this.nativeRange.createContextualFragment(a)});e.getBody(document).removeChild(i);q.detach();f.detach()})(),b.createNativeRange=function(a){a=a||document;return a.createRange()};else if(b.features.implementsTextRange){d=function(a){this.textRange=a;this.refresh()};
+d.prototype=new g(document);d.prototype.refresh=function(){var a,b,d=this.textRange;a=d.parentElement();var f=d.duplicate();f.collapse(!0);b=f.parentElement();f=d.duplicate();f.collapse(!1);d=f.parentElement();b=b==d?b:e.getCommonAncestor(b,d);b=b==a?b:e.getCommonAncestor(a,b);0==this.textRange.compareEndPoints("StartToEnd",this.textRange)?b=a=c(this.textRange,b,!0,!0):(a=c(this.textRange,b,!0,!1),b=c(this.textRange,b,!1,!1));this.setStart(a.node,a.offset);this.setEnd(b.node,b.offset)};g.copyComparisonConstants(d);
+var i=function(){return this}();"undefined"==typeof i.Range&&(i.Range=d);b.createNativeRange=function(a){a=a||document;return a.body.createTextRange()}}b.features.implementsTextRange&&(d.rangeToTextRange=function(b){if(b.collapsed)return a(new f(b.startContainer,b.startOffset),!0);var c=a(new f(b.startContainer,b.startOffset),!0),d=a(new f(b.endContainer,b.endOffset),!1),b=e.getDocument(b.startContainer).body.createTextRange();b.setEndPoint("StartToStart",c);b.setEndPoint("EndToEnd",d);return b});
+d.prototype.getName=function(){return"WrappedRange"};b.WrappedRange=d;b.createRange=function(a){a=a||document;return new d(b.createNativeRange(a))};b.createRangyRange=function(a){a=a||document;return new g(a)};b.createIframeRange=function(a){return b.createRange(e.getIframeDocument(a))};b.createIframeRangyRange=function(a){return b.createRangyRange(e.getIframeDocument(a))};b.addCreateMissingNativeApiListener(function(a){a=a.document;if(typeof a.createRange=="undefined")a.createRange=function(){return b.createRange(this)};
+a=a=null})});
+rangy.createModule("WrappedSelection",function(b,c){function a(a){return(a||window).getSelection()}function d(a){return(a||window).document.selection}function e(a,b,c){var d=c?"end":"start",c=c?"start":"end";a.anchorNode=b[d+"Container"];a.anchorOffset=b[d+"Offset"];a.focusNode=b[c+"Container"];a.focusOffset=b[c+"Offset"]}function f(a){a.anchorNode=a.focusNode=null;a.anchorOffset=a.focusOffset=0;a.rangeCount=0;a.isCollapsed=!0;a._ranges.length=0}function g(a){var c;a instanceof x?(c=a._selectionNativeRange,
+c||(c=b.createNativeRange(m.getDocument(a.startContainer)),c.setEnd(a.endContainer,a.endOffset),c.setStart(a.startContainer,a.startOffset),a._selectionNativeRange=c,a.attachListener("detach",function(){this._selectionNativeRange=null}))):a instanceof o?c=a.nativeRange:b.features.implementsDomRange&&a instanceof m.getWindow(a.startContainer).Range&&(c=a);return c}function i(a){var b=a.getNodes(),c;a:if(!b.length||1!=b[0].nodeType)c=!1;else{c=1;for(var d=b.length;c<d;++c)if(!m.isAncestorOf(b[0],b[c])){c=
+!1;break a}c=!0}if(!c)throw Error("getSingleElementFromRange: range "+a.inspect()+" did not consist of a single element");return b[0]}function h(a,b){var c=new o(b);a._ranges=[c];e(a,c,!1);a.rangeCount=1;a.isCollapsed=c.collapsed}function k(a){a._ranges.length=0;if("None"==a.docSelection.type)f(a);else{var c=a.docSelection.createRange();if(c&&"undefined"!=typeof c.text)h(a,c);else{a.rangeCount=c.length;for(var d,j=m.getDocument(c.item(0)),k=0;k<a.rangeCount;++k)d=b.createRange(j),d.selectNode(c.item(k)),
+a._ranges.push(d);a.isCollapsed=1==a.rangeCount&&a._ranges[0].collapsed;e(a,a._ranges[a.rangeCount-1],!1)}}}function j(a,b){for(var c=a.docSelection.createRange(),d=i(b),e=m.getDocument(c.item(0)),e=m.getBody(e).createControlRange(),h=0,j=c.length;h<j;++h)e.add(c.item(h));try{e.add(d)}catch(f){throw Error("addRange(): Element within the specified Range could not be added to control selection (does it have layout?)");}e.select();k(a)}function n(a,b,c){this.nativeSelection=a;this.docSelection=b;this._ranges=
+[];this.win=c;this.refresh()}function p(a,b){for(var c=m.getDocument(b[0].startContainer),c=m.getBody(c).createControlRange(),d=0,e;d<rangeCount;++d){e=i(b[d]);try{c.add(e)}catch(h){throw Error("setRanges(): Element within the one of the specified Ranges could not be added to control selection (does it have layout?)");}}c.select();k(a)}function q(a,b){if(a.anchorNode&&m.getDocument(a.anchorNode)!==m.getDocument(b))throw new z("WRONG_DOCUMENT_ERR");}function r(a){var b=[],c=new w(a.anchorNode,a.anchorOffset),
+d=new w(a.focusNode,a.focusOffset),e="function"==typeof a.getName?a.getName():"Selection";if("undefined"!=typeof a.rangeCount)for(var h=0,j=a.rangeCount;h<j;++h)b[h]=x.inspect(a.getRangeAt(h));return"["+e+"(Ranges: "+b.join(", ")+")(anchor: "+c.inspect()+", focus: "+d.inspect()+"]"}b.requireModules(["DomUtil","DomRange","WrappedRange"]);b.config.checkSelectionRanges=!0;var m=b.dom,s=b.util,x=b.DomRange,o=b.WrappedRange,z=b.DOMException,w=m.DomPosition,y,A,t=b.util.isHostMethod(window,"getSelection"),
+v=b.util.isHostObject(document,"selection"),D=v&&(!t||b.config.preferTextRange);D?(y=d,b.isSelectionValid=function(a){var a=(a||window).document,b=a.selection;return"None"!=b.type||m.getDocument(b.createRange().parentElement())==a}):t?(y=a,b.isSelectionValid=function(){return!0}):c.fail("Neither document.selection or window.getSelection() detected.");b.getNativeSelection=y;var t=y(),K=b.createNativeRange(document),F=m.getBody(document),E=s.areHostObjects(t,s.areHostProperties(t,["anchorOffset","focusOffset"]));
+b.features.selectionHasAnchorAndFocus=E;var I=s.isHostMethod(t,"extend");b.features.selectionHasExtend=I;var N="number"==typeof t.rangeCount;b.features.selectionHasRangeCount=N;var J=!1,C=!0;s.areHostMethods(t,["addRange","getRangeAt","removeAllRanges"])&&("number"==typeof t.rangeCount&&b.features.implementsDomRange)&&function(){var a=document.createElement("iframe");F.appendChild(a);var b=m.getIframeDocument(a);b.open();b.write("<html><head></head><body>12</body></html>");b.close();var c=m.getIframeWindow(a).getSelection(),
+d=b.documentElement.lastChild.firstChild,b=b.createRange();b.setStart(d,1);b.collapse(true);c.addRange(b);C=c.rangeCount==1;c.removeAllRanges();var e=b.cloneRange();b.setStart(d,0);e.setEnd(d,2);c.addRange(b);c.addRange(e);J=c.rangeCount==2;b.detach();e.detach();F.removeChild(a)}();b.features.selectionSupportsMultipleRanges=J;b.features.collapsedNonEditableSelectionsSupported=C;var l=!1,u;F&&s.isHostMethod(F,"createControlRange")&&(u=F.createControlRange(),s.areHostProperties(u,["item","add"])&&(l=
+!0));b.features.implementsControlRange=l;A=E?function(a){return a.anchorNode===a.focusNode&&a.anchorOffset===a.focusOffset}:function(a){return a.rangeCount?a.getRangeAt(a.rangeCount-1).collapsed:false};var B;s.isHostMethod(t,"getRangeAt")?B=function(a,b){try{return a.getRangeAt(b)}catch(c){return null}}:E&&(B=function(a){var c=m.getDocument(a.anchorNode),c=b.createRange(c);c.setStart(a.anchorNode,a.anchorOffset);c.setEnd(a.focusNode,a.focusOffset);if(c.collapsed!==this.isCollapsed){c.setStart(a.focusNode,
+a.focusOffset);c.setEnd(a.anchorNode,a.anchorOffset)}return c});b.getSelection=function(a){var a=a||window,b=a._rangySelection,c=y(a),e=v?d(a):null;if(b){b.nativeSelection=c;b.docSelection=e;b.refresh(a)}else{b=new n(c,e,a);a._rangySelection=b}return b};b.getIframeSelection=function(a){return b.getSelection(m.getIframeWindow(a))};u=n.prototype;if(!D&&E&&s.areHostMethods(t,["removeAllRanges","addRange"])){u.removeAllRanges=function(){this.nativeSelection.removeAllRanges();f(this)};var L=function(a,
+c){var d=x.getRangeDocument(c),d=b.createRange(d);d.collapseToPoint(c.endContainer,c.endOffset);a.nativeSelection.addRange(g(d));a.nativeSelection.extend(c.startContainer,c.startOffset);a.refresh()};u.addRange=N?function(a,c){if(l&&v&&this.docSelection.type=="Control")j(this,a);else if(c&&I)L(this,a);else{var d;if(J)d=this.rangeCount;else{this.removeAllRanges();d=0}this.nativeSelection.addRange(g(a));this.rangeCount=this.nativeSelection.rangeCount;if(this.rangeCount==d+1){if(b.config.checkSelectionRanges)(d=
+B(this.nativeSelection,this.rangeCount-1))&&!x.rangesEqual(d,a)&&(a=new o(d));this._ranges[this.rangeCount-1]=a;e(this,a,H(this.nativeSelection));this.isCollapsed=A(this)}else this.refresh()}}:function(a,b){if(b&&I)L(this,a);else{this.nativeSelection.addRange(g(a));this.refresh()}};u.setRanges=function(a){if(l&&a.length>1)p(this,a);else{this.removeAllRanges();for(var b=0,c=a.length;b<c;++b)this.addRange(a[b])}}}else if(s.isHostMethod(t,"empty")&&s.isHostMethod(K,"select")&&l&&D)u.removeAllRanges=
+function(){try{this.docSelection.empty();if(this.docSelection.type!="None"){var a;if(this.anchorNode)a=m.getDocument(this.anchorNode);else if(this.docSelection.type=="Control"){var b=this.docSelection.createRange();b.length&&(a=m.getDocument(b.item(0)).body.createTextRange())}if(a){a.body.createTextRange().select();this.docSelection.empty()}}}catch(c){}f(this)},u.addRange=function(a){if(this.docSelection.type=="Control")j(this,a);else{o.rangeToTextRange(a).select();this._ranges[0]=a;this.rangeCount=
+1;this.isCollapsed=this._ranges[0].collapsed;e(this,a,false)}},u.setRanges=function(a){this.removeAllRanges();var b=a.length;b>1?p(this,a):b&&this.addRange(a[0])};else return c.fail("No means of selecting a Range or TextRange was found"),!1;u.getRangeAt=function(a){if(a<0||a>=this.rangeCount)throw new z("INDEX_SIZE_ERR");return this._ranges[a]};var G;if(D)G=function(a){var c;if(b.isSelectionValid(a.win))c=a.docSelection.createRange();else{c=m.getBody(a.win.document).createTextRange();c.collapse(true)}a.docSelection.type==
+"Control"?k(a):c&&typeof c.text!="undefined"?h(a,c):f(a)};else if(s.isHostMethod(t,"getRangeAt")&&"number"==typeof t.rangeCount)G=function(a){if(l&&v&&a.docSelection.type=="Control")k(a);else{a._ranges.length=a.rangeCount=a.nativeSelection.rangeCount;if(a.rangeCount){for(var c=0,d=a.rangeCount;c<d;++c)a._ranges[c]=new b.WrappedRange(a.nativeSelection.getRangeAt(c));e(a,a._ranges[a.rangeCount-1],H(a.nativeSelection));a.isCollapsed=A(a)}else f(a)}};else if(E&&"boolean"==typeof t.isCollapsed&&"boolean"==
+typeof K.collapsed&&b.features.implementsDomRange)G=function(a){var b;b=a.nativeSelection;if(b.anchorNode){b=B(b,0);a._ranges=[b];a.rangeCount=1;b=a.nativeSelection;a.anchorNode=b.anchorNode;a.anchorOffset=b.anchorOffset;a.focusNode=b.focusNode;a.focusOffset=b.focusOffset;a.isCollapsed=A(a)}else f(a)};else return c.fail("No means of obtaining a Range or TextRange from the user's selection was found"),!1;u.refresh=function(a){var b=a?this._ranges.slice(0):null;G(this);if(a){a=b.length;if(a!=this._ranges.length)return false;
+for(;a--;)if(!x.rangesEqual(b[a],this._ranges[a]))return false;return true}};var P=function(a,b){var c=a.getAllRanges(),d=false;a.removeAllRanges();for(var e=0,h=c.length;e<h;++e)d||b!==c[e]?a.addRange(c[e]):d=true;a.rangeCount||f(a)};u.removeRange=l?function(a){if(this.docSelection.type=="Control"){for(var b=this.docSelection.createRange(),a=i(a),c=m.getDocument(b.item(0)),c=m.getBody(c).createControlRange(),d,e=false,h=0,j=b.length;h<j;++h){d=b.item(h);d!==a||e?c.add(b.item(h)):e=true}c.select();
+k(this)}else P(this,a)}:function(a){P(this,a)};var H;!D&&E&&b.features.implementsDomRange?(H=function(a){var b=false;a.anchorNode&&(b=m.comparePoints(a.anchorNode,a.anchorOffset,a.focusNode,a.focusOffset)==1);return b},u.isBackwards=function(){return H(this)}):H=u.isBackwards=function(){return false};u.toString=function(){for(var a=[],b=0,c=this.rangeCount;b<c;++b)a[b]=""+this._ranges[b];return a.join("")};u.collapse=function(a,c){q(this,a);var d=b.createRange(m.getDocument(a));d.collapseToPoint(a,
+c);this.removeAllRanges();this.addRange(d);this.isCollapsed=true};u.collapseToStart=function(){if(this.rangeCount){var a=this._ranges[0];this.collapse(a.startContainer,a.startOffset)}else throw new z("INVALID_STATE_ERR");};u.collapseToEnd=function(){if(this.rangeCount){var a=this._ranges[this.rangeCount-1];this.collapse(a.endContainer,a.endOffset)}else throw new z("INVALID_STATE_ERR");};u.selectAllChildren=function(a){q(this,a);var c=b.createRange(m.getDocument(a));c.selectNodeContents(a);this.removeAllRanges();
+this.addRange(c)};u.deleteFromDocument=function(){if(l&&v&&this.docSelection.type=="Control"){for(var a=this.docSelection.createRange(),b;a.length;){b=a.item(0);a.remove(b);b.parentNode.removeChild(b)}this.refresh()}else if(this.rangeCount){a=this.getAllRanges();this.removeAllRanges();b=0;for(var c=a.length;b<c;++b)a[b].deleteContents();this.addRange(a[c-1])}};u.getAllRanges=function(){return this._ranges.slice(0)};u.setSingleRange=function(a){this.setRanges([a])};u.containsNode=function(a,b){for(var c=
+0,d=this._ranges.length;c<d;++c)if(this._ranges[c].containsNode(a,b))return true;return false};u.toHtml=function(){var a="";if(this.rangeCount){for(var a=x.getRangeDocument(this._ranges[0]).createElement("div"),b=0,c=this._ranges.length;b<c;++b)a.appendChild(this._ranges[b].cloneContents());a=a.innerHTML}return a};u.getName=function(){return"WrappedSelection"};u.inspect=function(){return r(this)};u.detach=function(){this.win=this.anchorNode=this.focusNode=this.win._rangySelection=null};n.inspect=
+r;b.Selection=n;b.selectionPrototype=u;b.addCreateMissingNativeApiListener(function(a){if(typeof a.getSelection=="undefined")a.getSelection=function(){return b.getSelection(this)};a=null})});var Base=function(){};
+Base.extend=function(b,c){var a=Base.prototype.extend;Base._prototyping=!0;var d=new this;a.call(d,b);d.base=function(){};delete Base._prototyping;var e=d.constructor,f=d.constructor=function(){if(!Base._prototyping)if(this._constructing||this.constructor==f)this._constructing=!0,e.apply(this,arguments),delete this._constructing;else if(null!=arguments[0])return(arguments[0].extend||a).call(arguments[0],d)};f.ancestor=this;f.extend=this.extend;f.forEach=this.forEach;f.implement=this.implement;f.prototype=
+d;f.toString=this.toString;f.valueOf=function(a){return"object"==a?f:e.valueOf()};a.call(f,c);"function"==typeof f.init&&f.init();return f};
+Base.prototype={extend:function(b,c){if(1<arguments.length){var a=this[b];if(a&&"function"==typeof c&&(!a.valueOf||a.valueOf()!=c.valueOf())&&/\bbase\b/.test(c)){var d=c.valueOf(),c=function(){var b=this.base||Base.prototype.base;this.base=a;var c=d.apply(this,arguments);this.base=b;return c};c.valueOf=function(a){return"object"==a?c:d};c.toString=Base.toString}this[b]=c}else if(b){var e=Base.prototype.extend;!Base._prototyping&&"function"!=typeof this&&(e=this.extend||e);for(var f={toSource:null},
+g=["constructor","toString","valueOf"],i=Base._prototyping?0:1;h=g[i++];)b[h]!=f[h]&&e.call(this,h,b[h]);for(var h in b)f[h]||e.call(this,h,b[h])}return this}};
+Base=Base.extend({constructor:function(b){this.extend(b)}},{ancestor:Object,version:"1.1",forEach:function(b,c,a){for(var d in b)void 0===this.prototype[d]&&c.call(a,b[d],d,b)},implement:function(){for(var b=0;b<arguments.length;b++)if("function"==typeof arguments[b])arguments[b](this.prototype);else this.prototype.extend(arguments[b]);return this},toString:function(){return""+this.valueOf()}});
+wysihtml5.browser=function(){var b=navigator.userAgent,c=document.createElement("div"),a=-1!==b.indexOf("MSIE")&&-1===b.indexOf("Opera"),d=-1!==b.indexOf("Gecko")&&-1===b.indexOf("KHTML"),e=-1!==b.indexOf("AppleWebKit/"),f=-1!==b.indexOf("Chrome/"),g=-1!==b.indexOf("Opera/");return{USER_AGENT:b,supported:function(){var a=this.USER_AGENT.toLowerCase(),b="contentEditable"in c,d=document.execCommand&&document.queryCommandSupported&&document.queryCommandState,e=document.querySelector&&document.querySelectorAll,
+a=this.isIos()&&5>(/ipad|iphone|ipod/.test(a)&&a.match(/ os (\d+).+? like mac os x/)||[,0])[1]||-1!==a.indexOf("opera mobi")||-1!==a.indexOf("hpwos/");return b&&d&&e&&!a},isTouchDevice:function(){return this.supportsEvent("touchmove")},isIos:function(){var a=this.USER_AGENT.toLowerCase();return-1!==a.indexOf("webkit")&&-1!==a.indexOf("mobile")},supportsSandboxedIframes:function(){return a},throwsMixedContentWarningWhenIframeSrcIsEmpty:function(){return!("querySelector"in document)},displaysCaretInEmptyContentEditableCorrectly:function(){return!d},
+hasCurrentStyleProperty:function(){return"currentStyle"in c},insertsLineBreaksOnReturn:function(){return d},supportsPlaceholderAttributeOn:function(a){return"placeholder"in a},supportsEvent:function(a){var b;if(!(b="on"+a in c))c.setAttribute("on"+a,"return;"),b="function"===typeof c["on"+a];return b},supportsEventsInIframeCorrectly:function(){return!g},firesOnDropOnlyWhenOnDragOverIsCancelled:function(){return e||d},supportsDataTransfer:function(){try{return e&&(window.Clipboard||window.DataTransfer).prototype.getData}catch(a){return!1}},
+supportsHTML5Tags:function(a){a=a.createElement("div");a.innerHTML="<article>foo</article>";return"<article>foo</article>"===a.innerHTML.toLowerCase()},supportsCommand:function(){var b={formatBlock:a,insertUnorderedList:a||g||e,insertOrderedList:a||g||e},c={insertHTML:d};return function(a,d){if(!b[d]){try{return a.queryCommandSupported(d)}catch(e){}try{return a.queryCommandEnabled(d)}catch(f){return!!c[d]}}return!1}}(),doesAutoLinkingInContentEditable:function(){return a},canDisableAutoLinking:function(){return this.supportsCommand(document,
+"AutoUrlDetect")},clearsContentEditableCorrectly:function(){return d||g||e},supportsGetAttributeCorrectly:function(){return"1"!=document.createElement("td").getAttribute("rowspan")},canSelectImagesInContentEditable:function(){return d||a||g},clearsListsInContentEditableCorrectly:function(){return d||a||e},autoScrollsToCaret:function(){return!e},autoClosesUnclosedTags:function(){var a=c.cloneNode(!1),b;a.innerHTML="<p><div></div>";a=a.innerHTML.toLowerCase();b="<p></p><div></div>"===a||"<p><div></div></p>"===
+a;this.autoClosesUnclosedTags=function(){return b};return b},supportsNativeGetElementsByClassName:function(){return-1!==(""+document.getElementsByClassName).indexOf("[native code]")},supportsSelectionModify:function(){return"getSelection"in window&&"modify"in window.getSelection()},supportsClassList:function(){return"classList"in c},needsSpaceAfterLineBreak:function(){return g},supportsSpeechApiOn:function(a){return 11<=(b.match(/Chrome\/(\d+)/)||[,0])[1]&&("onwebkitspeechchange"in a||"speech"in a)},
+crashesWhenDefineProperty:function(b){return a&&("XMLHttpRequest"===b||"XDomainRequest"===b)},doesAsyncFocus:function(){return a},hasProblemsSettingCaretAfterImg:function(){return a},hasUndoInContextMenu:function(){return d||f||g}}}();
+wysihtml5.lang.array=function(b){return{contains:function(c){if(b.indexOf)return-1!==b.indexOf(c);for(var a=0,d=b.length;a<d;a++)if(b[a]===c)return!0;return!1},without:function(c){for(var c=wysihtml5.lang.array(c),a=[],d=0,e=b.length;d<e;d++)c.contains(b[d])||a.push(b[d]);return a},get:function(){for(var c=0,a=b.length,d=[];c<a;c++)d.push(b[c]);return d}}};
+wysihtml5.lang.Dispatcher=Base.extend({observe:function(b,c){this.events=this.events||{};this.events[b]=this.events[b]||[];this.events[b].push(c);return this},on:function(){return this.observe.apply(this,wysihtml5.lang.array(arguments).get())},fire:function(b,c){this.events=this.events||{};for(var a=this.events[b]||[],d=0;d<a.length;d++)a[d].call(this,c);return this},stopObserving:function(b,c){this.events=this.events||{};var a=0,d,e;if(b){d=this.events[b]||[];for(e=[];a<d.length;a++)d[a]!==c&&c&&
+e.push(d[a]);this.events[b]=e}else this.events={};return this}});wysihtml5.lang.object=function(b){return{merge:function(c){for(var a in c)b[a]=c[a];return this},get:function(){return b},clone:function(){var c={},a;for(a in b)c[a]=b[a];return c},isArray:function(){return"[object Array]"===Object.prototype.toString.call(b)}}};
+(function(){var b=/^\s+/,c=/\s+$/;wysihtml5.lang.string=function(a){a=""+a;return{trim:function(){return a.replace(b,"").replace(c,"")},interpolate:function(b){for(var c in b)a=this.replace("#{"+c+"}").by(b[c]);return a},replace:function(b){return{by:function(c){return a.split(b).join(c)}}}}}})();
+(function(b){function c(a){return a.replace(e,function(a,b){var c=(b.match(f)||[])[1]||"",d=i[c],b=b.replace(f,"");b.split(d).length>b.split(c).length&&(b+=c,c="");var e=d=b;b.length>g&&(e=e.substr(0,g)+"...");"www."===d.substr(0,4)&&(d="http://"+d);return'<a href="'+d+'">'+e+"</a>"+c})}function a(h){if(!d.contains(h.nodeName))if(h.nodeType===b.TEXT_NODE&&h.data.match(e)){var f=h.parentNode,j;j=f.ownerDocument;var g=j._wysihtml5_tempElement;g||(g=j._wysihtml5_tempElement=j.createElement("div"));j=
+g;j.innerHTML="<span></span>"+c(h.data);for(j.removeChild(j.firstChild);j.firstChild;)f.insertBefore(j.firstChild,h);f.removeChild(h)}else{f=b.lang.array(h.childNodes).get();j=f.length;for(g=0;g<j;g++)a(f[g]);return h}}var d=b.lang.array("CODE PRE A SCRIPT HEAD TITLE STYLE".split(" ")),e=/((https?:\/\/|www\.)[^\s<]{3,})/gi,f=/([^\w\/\-](,?))$/i,g=100,i={")":"(","]":"[","}":"{"};b.dom.autoLink=function(b){var c;a:{c=b;for(var e;c.parentNode;){c=c.parentNode;e=c.nodeName;if(d.contains(e)){c=!0;break a}if("body"===
+e)break}c=!1}if(c)return b;b===b.ownerDocument.documentElement&&(b=b.ownerDocument.body);return a(b)};b.dom.autoLink.URL_REG_EXP=e})(wysihtml5);
+(function(b){var c=b.browser.supportsClassList(),a=b.dom;a.addClass=function(b,e){if(c)return b.classList.add(e);a.hasClass(b,e)||(b.className+=" "+e)};a.removeClass=function(a,b){if(c)return a.classList.remove(b);a.className=a.className.replace(RegExp("(^|\\s+)"+b+"(\\s+|$)")," ")};a.hasClass=function(a,b){if(c)return a.classList.contains(b);var f=a.className;return 0<f.length&&(f==b||RegExp("(^|\\s)"+b+"(\\s|$)").test(f))}})(wysihtml5);
+wysihtml5.dom.contains=function(){var b=document.documentElement;if(b.contains)return function(b,a){a.nodeType!==wysihtml5.ELEMENT_NODE&&(a=a.parentNode);return b!==a&&b.contains(a)};if(b.compareDocumentPosition)return function(b,a){return!!(b.compareDocumentPosition(a)&16)}}();
+wysihtml5.dom.convertToList=function(){function b(b,a){var d=b.createElement("li");a.appendChild(d);return d}return function(c,a){if("UL"===c.nodeName||"OL"===c.nodeName||"MENU"===c.nodeName)return c;var d=c.ownerDocument,e=d.createElement(a),f=c.querySelectorAll("br"),g=f.length,i,h,k,j,n;for(n=0;n<g;n++)for(i=f[n];(h=i.parentNode)&&h!==c&&h.lastChild===i;){if("block"===wysihtml5.dom.getStyle("display").from(h)){h.removeChild(i);break}wysihtml5.dom.insert(i).after(i.parentNode)}f=wysihtml5.lang.array(c.childNodes).get();
+g=f.length;for(n=0;n<g;n++)j=j||b(d,e),i=f[n],h="block"===wysihtml5.dom.getStyle("display").from(i),k="BR"===i.nodeName,h?(j=j.firstChild?b(d,e):j,j.appendChild(i),j=null):k?j=j.firstChild?null:j:j.appendChild(i);c.parentNode.replaceChild(e,c);return e}}();wysihtml5.dom.copyAttributes=function(b){return{from:function(c){return{to:function(a){for(var d,e=0,f=b.length;e<f;e++)d=b[e],"undefined"!==typeof c[d]&&""!==c[d]&&(a[d]=c[d]);return{andTo:arguments.callee}}}}}};
+(function(b){var c=["-webkit-box-sizing","-moz-box-sizing","-ms-box-sizing","box-sizing"],a=function(a){var e;a:for(var f=0,g=c.length;f<g;f++)if("border-box"===b.getStyle(c[f]).from(a)){e=c[f];break a}return e?parseInt(b.getStyle("width").from(a),10)<a.offsetWidth:!1};b.copyStyles=function(d){return{from:function(e){a(e)&&(d=wysihtml5.lang.array(d).without(c));for(var f="",g=d.length,i=0,h;i<g;i++)h=d[i],f+=h+":"+b.getStyle(h).from(e)+";";return{to:function(a){b.setStyles(f).on(a);return{andTo:arguments.callee}}}}}}})(wysihtml5.dom);
+(function(b){b.dom.delegate=function(c,a,d,e){return b.dom.observe(c,d,function(d){for(var g=d.target,i=b.lang.array(c.querySelectorAll(a));g&&g!==c;){if(i.contains(g)){e.call(g,d);break}g=g.parentNode}})}})(wysihtml5);
+wysihtml5.dom.getAsDom=function(){var b="abbr article aside audio bdi canvas command datalist details figcaption figure footer header hgroup keygen mark meter nav output progress rp rt ruby svg section source summary time track video wbr".split(" ");return function(c,a){var a=a||document,d;if("object"===typeof c&&c.nodeType)d=a.createElement("div"),d.appendChild(c);else if(wysihtml5.browser.supportsHTML5Tags(a))d=a.createElement("div"),d.innerHTML=c;else{d=a;if(!d._wysihtml5_supportsHTML5Tags){for(var e=
+0,f=b.length;e<f;e++)d.createElement(b[e]);d._wysihtml5_supportsHTML5Tags=!0}d=a;e=d.createElement("div");e.style.display="none";d.body.appendChild(e);try{e.innerHTML=c}catch(g){}d.body.removeChild(e);d=e}return d}}();
+wysihtml5.dom.getParentElement=function(){function b(b,a){return!a||!a.length?!0:"string"===typeof a?b===a:wysihtml5.lang.array(a).contains(b)}return function(c,a,d){d=d||50;if(a.className||a.classRegExp){a:{for(var e=a.nodeName,f=a.className,a=a.classRegExp;d--&&c&&"BODY"!==c.nodeName;){var g;if(g=c.nodeType===wysihtml5.ELEMENT_NODE)if(g=b(c.nodeName,e)){g=f;var i=(c.className||"").match(a)||[];g=!g?!!i.length:i[i.length-1]===g}if(g)break a;c=c.parentNode}c=null}return c}a:{e=a.nodeName;for(f=d;f--&&
+c&&"BODY"!==c.nodeName;){if(b(c.nodeName,e))break a;c=c.parentNode}c=null}return c}}();
+wysihtml5.dom.getStyle=function(){function b(b){return b.replace(a,function(a){return a.charAt(1).toUpperCase()})}var c={"float":"styleFloat"in document.createElement("div").style?"styleFloat":"cssFloat"},a=/\-[a-z]/g;return function(a){return{from:function(e){if(e.nodeType===wysihtml5.ELEMENT_NODE){var f=e.ownerDocument,g=c[a]||b(a),i=e.style,h=e.currentStyle,k=i[g];if(k)return k;if(h)try{return h[g]}catch(j){}var g=f.defaultView||f.parentWindow,f=("height"===a||"width"===a)&&"TEXTAREA"===e.nodeName,
+n;if(g.getComputedStyle)return f&&(n=i.overflow,i.overflow="hidden"),e=g.getComputedStyle(e,null).getPropertyValue(a),f&&(i.overflow=n||""),e}}}}}();wysihtml5.dom.hasElementWithTagName=function(){var b={},c=1;return function(a,d){var e=(a._wysihtml5_identifier||(a._wysihtml5_identifier=c++))+":"+d,f=b[e];f||(f=b[e]=a.getElementsByTagName(d));return 0<f.length}}();
+(function(b){var c={},a=1;b.dom.hasElementWithClassName=function(d,e){if(!b.browser.supportsNativeGetElementsByClassName())return!!d.querySelector("."+e);var f=(d._wysihtml5_identifier||(d._wysihtml5_identifier=a++))+":"+e,g=c[f];g||(g=c[f]=d.getElementsByClassName(e));return 0<g.length}})(wysihtml5);wysihtml5.dom.insert=function(b){return{after:function(c){c.parentNode.insertBefore(b,c.nextSibling)},before:function(c){c.parentNode.insertBefore(b,c)},into:function(c){c.appendChild(b)}}};
+wysihtml5.dom.insertCSS=function(b){b=b.join("\n");return{into:function(c){var a=c.head||c.getElementsByTagName("head")[0],d=c.createElement("style");d.type="text/css";d.styleSheet?d.styleSheet.cssText=b:d.appendChild(c.createTextNode(b));a&&a.appendChild(d)}}};
+wysihtml5.dom.observe=function(b,c,a){for(var c="string"===typeof c?[c]:c,d,e,f=0,g=c.length;f<g;f++)e=c[f],b.addEventListener?b.addEventListener(e,a,!1):(d=function(c){"target"in c||(c.target=c.srcElement);c.preventDefault=c.preventDefault||function(){this.returnValue=false};c.stopPropagation=c.stopPropagation||function(){this.cancelBubble=true};a.call(b,c)},b.attachEvent("on"+e,d));return{stop:function(){for(var e,h=0,f=c.length;h<f;h++)e=c[h],b.removeEventListener?b.removeEventListener(e,a,!1):
+b.detachEvent("on"+e,d)}}};
+wysihtml5.dom.parse=function(){function b(c,e){var h=c.childNodes,f=h.length,g;g=a[c.nodeType];var k=0;g=g&&g(c);if(!g)return null;for(k=0;k<f;k++)(newChild=b(h[k],e))&&g.appendChild(newChild);return e&&1>=g.childNodes.length&&g.nodeName.toLowerCase()===d&&!g.attributes.length?g.firstChild:g}function c(a,b){var b=b.toLowerCase(),c;if(c="IMG"==a.nodeName)if(c="src"==b){var d;try{d=a.complete&&!a.mozMatchesSelector(":-moz-broken")}catch(e){a.complete&&"complete"===a.readyState&&(d=!0)}c=!0===d}return c?
+a.src:i&&"outerHTML"in a?-1!=a.outerHTML.toLowerCase().indexOf(" "+b+"=")?a.getAttribute(b):null:a.getAttribute(b)}var a={1:function(a){var b,f,i=g.tags;f=a.nodeName.toLowerCase();b=a.scopeName;if(a._wysihtml5)return null;a._wysihtml5=1;if("wysihtml5-temp"===a.className)return null;b&&"HTML"!=b&&(f=b+":"+f);"outerHTML"in a&&!wysihtml5.browser.autoClosesUnclosedTags()&&("P"===a.nodeName&&"</p>"!==a.outerHTML.slice(-4).toLowerCase())&&(f="div");if(f in i){b=i[f];if(!b||b.remove)return null;b="string"===
+typeof b?{rename_tag:b}:b}else if(a.firstChild)b={rename_tag:d};else return null;f=a.ownerDocument.createElement(b.rename_tag||f);var i={},r=b.set_class,m=b.add_class,s=b.set_attributes,x=b.check_attributes,o=g.classes,z=0,w=[];b=[];var y=[],A=[],t;s&&(i=wysihtml5.lang.object(s).clone());if(x)for(t in x)if(s=h[x[t]])s=s(c(a,t)),"string"===typeof s&&(i[t]=s);r&&w.push(r);if(m)for(t in m)if(s=k[m[t]])r=s(c(a,t)),"string"===typeof r&&w.push(r);o["_wysihtml5-temp-placeholder"]=1;(A=a.getAttribute("class"))&&
+(w=w.concat(A.split(e)));for(m=w.length;z<m;z++)a=w[z],o[a]&&b.push(a);for(o=b.length;o--;)a=b[o],wysihtml5.lang.array(y).contains(a)||y.unshift(a);y.length&&(i["class"]=y.join(" "));for(t in i)try{f.setAttribute(t,i[t])}catch(v){}i.src&&("undefined"!==typeof i.width&&f.setAttribute("width",i.width),"undefined"!==typeof i.height&&f.setAttribute("height",i.height));return f},3:function(a){return a.ownerDocument.createTextNode(a.data)}},d="span",e=/\s+/,f={tags:{},classes:{}},g={},i=!wysihtml5.browser.supportsGetAttributeCorrectly(),
+h={url:function(){var a=/^https?:\/\//i;return function(b){return!b||!b.match(a)?null:b.replace(a,function(a){return a.toLowerCase()})}}(),alt:function(){var a=/[^ a-z0-9_\-]/gi;return function(b){return!b?"":b.replace(a,"")}}(),numbers:function(){var a=/\D/g;return function(b){return(b=(b||"").replace(a,""))||null}}()},k={align_img:function(){var a={left:"wysiwyg-float-left",right:"wysiwyg-float-right"};return function(b){return a[(""+b).toLowerCase()]}}(),align_text:function(){var a={left:"wysiwyg-text-align-left",
+right:"wysiwyg-text-align-right",center:"wysiwyg-text-align-center",justify:"wysiwyg-text-align-justify"};return function(b){return a[(""+b).toLowerCase()]}}(),clear_br:function(){var a={left:"wysiwyg-clear-left",right:"wysiwyg-clear-right",both:"wysiwyg-clear-both",all:"wysiwyg-clear-both"};return function(b){return a[(""+b).toLowerCase()]}}(),size_font:function(){var a={1:"wysiwyg-font-size-xx-small",2:"wysiwyg-font-size-small",3:"wysiwyg-font-size-medium",4:"wysiwyg-font-size-large",5:"wysiwyg-font-size-x-large",
+6:"wysiwyg-font-size-xx-large",7:"wysiwyg-font-size-xx-large","-":"wysiwyg-font-size-smaller","+":"wysiwyg-font-size-larger"};return function(b){return a[(""+b).charAt(0)]}}()};return function(a,c,d,e){wysihtml5.lang.object(g).merge(f).merge(c).get();for(var d=d||a.ownerDocument||document,c=d.createDocumentFragment(),h="string"===typeof a,a=h?wysihtml5.dom.getAsDom(a,d):a;a.firstChild;)d=a.firstChild,a.removeChild(d),(d=b(d,e))&&c.appendChild(d);a.innerHTML="";a.appendChild(c);return h?wysihtml5.quirks.getCorrectInnerHTML(a):
+a}}();wysihtml5.dom.removeEmptyTextNodes=function(b){for(var c=wysihtml5.lang.array(b.childNodes).get(),a=c.length,d=0;d<a;d++)b=c[d],b.nodeType===wysihtml5.TEXT_NODE&&""===b.data&&b.parentNode.removeChild(b)};wysihtml5.dom.renameElement=function(b,c){for(var a=b.ownerDocument.createElement(c),d;d=b.firstChild;)a.appendChild(d);wysihtml5.dom.copyAttributes(["align","className"]).from(b).to(a);b.parentNode.replaceChild(a,b);return a};
+wysihtml5.dom.replaceWithChildNodes=function(b){if(b.parentNode)if(b.firstChild){for(var c=b.ownerDocument.createDocumentFragment();b.firstChild;)c.appendChild(b.firstChild);b.parentNode.replaceChild(c,b)}else b.parentNode.removeChild(b)};
+(function(b){function c(a){var b=a.ownerDocument.createElement("br");a.appendChild(b)}b.resolveList=function(a){if(!("MENU"!==a.nodeName&&"UL"!==a.nodeName&&"OL"!==a.nodeName)){var d=a.ownerDocument.createDocumentFragment(),e=a.previousElementSibling||a.previousSibling,f,g,i;for(e&&"block"!==b.getStyle("display").from(e)&&c(d);i=a.firstChild;){for(f=i.lastChild;e=i.firstChild;)g=(g=e===f)&&"block"!==b.getStyle("display").from(e)&&"BR"!==e.nodeName,d.appendChild(e),g&&c(d);i.parentNode.removeChild(i)}a.parentNode.replaceChild(d,
+a)}}})(wysihtml5.dom);
+(function(b){var c=document,a="parent top opener frameElement frames localStorage globalStorage sessionStorage indexedDB".split(" "),d="open close openDialog showModalDialog alert confirm prompt openDatabase postMessage XMLHttpRequest XDomainRequest".split(" "),e=["referrer","write","open","close"];b.dom.Sandbox=Base.extend({constructor:function(a,c){this.callback=a||b.EMPTY_FUNCTION;this.config=b.lang.object({}).merge(c).get();this.iframe=this._createIframe()},insertInto:function(a){"string"===typeof a&&
+(a=c.getElementById(a));a.appendChild(this.iframe)},getIframe:function(){return this.iframe},getWindow:function(){this._readyError()},getDocument:function(){this._readyError()},destroy:function(){var a=this.getIframe();a.parentNode.removeChild(a)},_readyError:function(){throw Error("wysihtml5.Sandbox: Sandbox iframe isn't loaded yet");},_createIframe:function(){var a=this,d=c.createElement("iframe");d.className="wysihtml5-sandbox";b.dom.setAttributes({security:"restricted",allowtransparency:"true",
+frameborder:0,width:0,height:0,marginwidth:0,marginheight:0}).on(d);b.browser.throwsMixedContentWarningWhenIframeSrcIsEmpty()&&(d.src="javascript:'<html></html>'");d.onload=function(){d.onreadystatechange=d.onload=null;a._onLoadIframe(d)};d.onreadystatechange=function(){if(/loaded|complete/.test(d.readyState)){d.onreadystatechange=d.onload=null;a._onLoadIframe(d)}};return d},_onLoadIframe:function(f){if(b.dom.contains(c.documentElement,f)){var g=this,i=f.contentWindow,h=f.contentWindow.document,k=
+this._getHtml({charset:c.characterSet||c.charset||"utf-8",stylesheets:this.config.stylesheets});h.open("text/html","replace");h.write(k);h.close();this.getWindow=function(){return f.contentWindow};this.getDocument=function(){return f.contentWindow.document};i.onerror=function(a,b,c){throw Error("wysihtml5.Sandbox: "+a,b,c);};if(!b.browser.supportsSandboxedIframes()){var j,k=0;for(j=a.length;k<j;k++)this._unset(i,a[k]);k=0;for(j=d.length;k<j;k++)this._unset(i,d[k],b.EMPTY_FUNCTION);k=0;for(j=e.length;k<
+j;k++)this._unset(h,e[k]);this._unset(h,"cookie","",!0)}this.loaded=!0;setTimeout(function(){g.callback(g)},0)}},_getHtml:function(a){var c=a.stylesheets,d="",e=0,k;if(c="string"===typeof c?[c]:c)for(k=c.length;e<k;e++)d+='<link rel="stylesheet" href="'+c[e]+'">';a.stylesheets=d;return b.lang.string('<!DOCTYPE html><html><head><meta charset="#{charset}">#{stylesheets}</head><body></body></html>').interpolate(a)},_unset:function(a,c,d,e){try{a[c]=d}catch(k){}try{a.__defineGetter__(c,function(){return d})}catch(j){}if(e)try{a.__defineSetter__(c,
+function(){})}catch(n){}if(!b.browser.crashesWhenDefineProperty(c))try{var p={get:function(){return d}};e&&(p.set=function(){});Object.defineProperty(a,c,p)}catch(q){}}})})(wysihtml5);(function(){var b={className:"class"};wysihtml5.dom.setAttributes=function(c){return{on:function(a){for(var d in c)a.setAttribute(b[d]||d,c[d])}}}})();
+wysihtml5.dom.setStyles=function(b){return{on:function(c){c=c.style;if("string"===typeof b)c.cssText+=";"+b;else for(var a in b)"float"===a?(c.cssFloat=b[a],c.styleFloat=b[a]):c[a]=b[a]}}};
+(function(b){b.simulatePlaceholder=function(c,a,d){var e=function(){a.hasPlaceholderSet()&&a.clear();b.removeClass(a.element,"placeholder")},f=function(){a.isEmpty()&&(a.setValue(d),b.addClass(a.element,"placeholder"))};c.observe("set_placeholder",f).observe("unset_placeholder",e).observe("focus:composer",e).observe("paste:composer",e).observe("blur:composer",f);f()}})(wysihtml5.dom);
+(function(b){var c=document.documentElement;"textContent"in c?(b.setTextContent=function(a,b){a.textContent=b},b.getTextContent=function(a){return a.textContent}):"innerText"in c?(b.setTextContent=function(a,b){a.innerText=b},b.getTextContent=function(a){return a.innerText}):(b.setTextContent=function(a,b){a.nodeValue=b},b.getTextContent=function(a){return a.nodeValue})})(wysihtml5.dom);
+wysihtml5.quirks.cleanPastedHTML=function(){var b={"a u":wysihtml5.dom.replaceWithChildNodes};return function(c,a,d){var a=a||b,d=d||c.ownerDocument||document,e="string"===typeof c,f,g,i,h=0,c=e?wysihtml5.dom.getAsDom(c,d):c;for(i in a){f=c.querySelectorAll(i);d=a[i];for(g=f.length;h<g;h++)d(f[h])}return e?c.innerHTML:c}}();
+(function(b){var c=b.dom;b.quirks.ensureProperClearing=function(){var a=function(){var a=this;setTimeout(function(){var b=a.innerHTML.toLowerCase();if("<p> </p>"==b||"<p> </p><p> </p>"==b)a.innerHTML=""},0)};return function(b){c.observe(b.element,["cut","keydown"],a)}}();b.quirks.ensureProperClearingOfLists=function(){var a=["OL","UL","MENU"];return function(d){c.observe(d.element,"keydown",function(e){if(e.keyCode===b.BACKSPACE_KEY){var f=d.selection.getSelectedNode(),e=d.element;
+e.firstChild&&b.lang.array(a).contains(e.firstChild.nodeName)&&(f=c.getParentElement(f,{nodeName:a}))&&f==e.firstChild&&1>=f.childNodes.length&&(f.firstChild?""===f.firstChild.innerHTML:1)&&f.parentNode.removeChild(f)}})}}()})(wysihtml5);
+(function(b){b.quirks.getCorrectInnerHTML=function(c){var a=c.innerHTML;if(-1===a.indexOf("%7E"))return a;var c=c.querySelectorAll("[href*='~'], [src*='~']"),d,e,f,g;g=0;for(f=c.length;g<f;g++)d=c[g].href||c[g].src,e=b.lang.string(d).replace("~").by("%7E"),a=b.lang.string(a).replace(e).by(d);return a}})(wysihtml5);
+(function(b){var c=b.dom,a="LI P H1 H2 H3 H4 H5 H6".split(" "),d=["UL","OL","MENU"];b.quirks.insertLineBreakOnReturn=function(e){function f(a){if(a=c.getParentElement(a,{nodeName:["P","DIV"]},2)){var d=document.createTextNode(b.INVISIBLE_SPACE);c.insert(d).before(a);c.replaceWithChildNodes(a);e.selection.selectNode(d)}}c.observe(e.element.ownerDocument,"keydown",function(g){var i=g.keyCode;if(!(g.shiftKey||i!==b.ENTER_KEY&&i!==b.BACKSPACE_KEY)){var h=e.selection.getSelectedNode();(h=c.getParentElement(h,
+{nodeName:a},4))?"LI"===h.nodeName&&(i===b.ENTER_KEY||i===b.BACKSPACE_KEY)?setTimeout(function(){var a=e.selection.getSelectedNode(),b;a&&((b=c.getParentElement(a,{nodeName:d},2))||f(a))},0):h.nodeName.match(/H[1-6]/)&&i===b.ENTER_KEY&&setTimeout(function(){f(e.selection.getSelectedNode())},0):i===b.ENTER_KEY&&!b.browser.insertsLineBreaksOnReturn()&&(e.commands.exec("insertLineBreak"),g.preventDefault())}})}})(wysihtml5);
+(function(b){b.quirks.redraw=function(c){b.dom.addClass(c,"wysihtml5-quirks-redraw");b.dom.removeClass(c,"wysihtml5-quirks-redraw");try{var a=c.ownerDocument;a.execCommand("italic",!1,null);a.execCommand("italic",!1,null)}catch(d){}}})(wysihtml5);
+(function(b){var c=b.dom;b.Selection=Base.extend({constructor:function(a){window.rangy.init();this.editor=a;this.composer=a.composer;this.doc=this.composer.doc},getBookmark:function(){var a=this.getRange();return a&&a.cloneRange()},setBookmark:function(a){a&&this.setSelection(a)},setBefore:function(a){var b=rangy.createRange(this.doc);b.setStartBefore(a);b.setEndBefore(a);return this.setSelection(b)},setAfter:function(a){var b=rangy.createRange(this.doc);b.setStartAfter(a);b.setEndAfter(a);return this.setSelection(b)},
+selectNode:function(a){var d=rangy.createRange(this.doc),e=a.nodeType===b.ELEMENT_NODE,f="canHaveHTML"in a?a.canHaveHTML:"IMG"!==a.nodeName,g=e?a.innerHTML:a.data,g=""===g||g===b.INVISIBLE_SPACE,i=c.getStyle("display").from(a),i="block"===i||"list-item"===i;if(g&&e&&f)try{a.innerHTML=b.INVISIBLE_SPACE}catch(h){}f?d.selectNodeContents(a):d.selectNode(a);f&&g&&e?d.collapse(i):f&&g&&(d.setStartAfter(a),d.setEndAfter(a));this.setSelection(d)},getSelectedNode:function(a){if(a&&this.doc.selection&&"Control"===
+this.doc.selection.type&&(a=this.doc.selection.createRange())&&a.length)return a.item(0);a=this.getSelection(this.doc);return a.focusNode===a.anchorNode?a.focusNode:(a=this.getRange(this.doc))?a.commonAncestorContainer:this.doc.body},executeAndRestore:function(a,c){var e=this.doc.body,f=c&&e.scrollTop,g=c&&e.scrollLeft,i='<span class="_wysihtml5-temp-placeholder">'+b.INVISIBLE_SPACE+"</span>",h=this.getRange(this.doc);if(h){i=h.createContextualFragment(i);h.insertNode(i);try{a(h.startContainer,h.endContainer)}catch(k){setTimeout(function(){throw k;
+},0)}(caretPlaceholder=this.doc.querySelector("._wysihtml5-temp-placeholder"))?(h=rangy.createRange(this.doc),h.selectNode(caretPlaceholder),h.deleteContents(),this.setSelection(h)):e.focus();c&&(e.scrollTop=f,e.scrollLeft=g);try{caretPlaceholder.parentNode.removeChild(caretPlaceholder)}catch(j){}}else a(e,e)},executeAndRestoreSimple:function(a){var b,c,f=this.getRange(),g=this.doc.body,i;if(f){b=f.getNodes([3]);g=b[0]||f.startContainer;i=b[b.length-1]||f.endContainer;b=g===f.startContainer?f.startOffset:
+0;c=i===f.endContainer?f.endOffset:i.length;try{a(f.startContainer,f.endContainer)}catch(h){setTimeout(function(){throw h;},0)}a=rangy.createRange(this.doc);try{a.setStart(g,b)}catch(k){}try{a.setEnd(i,c)}catch(j){}try{this.setSelection(a)}catch(n){}}else a(g,g)},insertHTML:function(a){var a=rangy.createRange(this.doc).createContextualFragment(a),b=a.lastChild;this.insertNode(a);b&&this.setAfter(b)},insertNode:function(a){var b=this.getRange();b&&b.insertNode(a)},surround:function(a){var b=this.getRange();
+if(b)try{b.surroundContents(a),this.selectNode(a)}catch(c){a.appendChild(b.extractContents()),b.insertNode(a)}},scrollIntoView:function(){var a=this.doc,c=a.documentElement.scrollHeight>a.documentElement.offsetHeight,e;if(!(e=a._wysihtml5ScrollIntoViewElement))e=a.createElement("span"),e.innerHTML=b.INVISIBLE_SPACE;e=a._wysihtml5ScrollIntoViewElement=e;if(c){this.insertNode(e);var c=e,f=0;if(c.parentNode){do f+=c.offsetTop||0,c=c.offsetParent;while(c)}c=f;e.parentNode.removeChild(e);c>a.body.scrollTop&&
+(a.body.scrollTop=c)}},selectLine:function(){b.browser.supportsSelectionModify()?this._selectLine_W3C():this.doc.selection&&this._selectLine_MSIE()},_selectLine_W3C:function(){var a=this.doc.defaultView.getSelection();a.modify("extend","left","lineboundary");a.modify("extend","right","lineboundary")},_selectLine_MSIE:function(){var a=this.doc.selection.createRange(),b=a.boundingTop,c=this.doc.body.scrollWidth,f;if(a.moveToPoint){0===b&&(f=this.doc.createElement("span"),this.insertNode(f),b=f.offsetTop,
+f.parentNode.removeChild(f));b+=1;for(f=-10;f<c;f+=2)try{a.moveToPoint(f,b);break}catch(g){}for(f=this.doc.selection.createRange();0<=c;c--)try{f.moveToPoint(c,b);break}catch(i){}a.setEndPoint("EndToEnd",f);a.select()}},getText:function(){var a=this.getSelection();return a?a.toString():""},getNodes:function(a,b){var c=this.getRange();return c?c.getNodes([a],b):[]},getRange:function(){var a=this.getSelection();return a&&a.rangeCount&&a.getRangeAt(0)},getSelection:function(){return rangy.getSelection(this.doc.defaultView||
+this.doc.parentWindow)},setSelection:function(a){return rangy.getSelection(this.doc.defaultView||this.doc.parentWindow).setSingleRange(a)}})})(wysihtml5);
+(function(b,c){function a(a,b){return c.dom.isCharacterDataNode(a)?0==b?!!a.previousSibling:b==a.length?!!a.nextSibling:!0:0<b&&b<a.childNodes.length}function d(a,b,e){var f;c.dom.isCharacterDataNode(b)&&(0==e?(e=c.dom.getNodeIndex(b),b=b.parentNode):e==b.length?(e=c.dom.getNodeIndex(b)+1,b=b.parentNode):f=c.dom.splitDataNode(b,e));if(!f){f=b.cloneNode(!1);f.id&&f.removeAttribute("id");for(var g;g=b.childNodes[e];)f.appendChild(g);c.dom.insertAfter(f,b)}return b==a?f:d(a,f.parentNode,c.dom.getNodeIndex(f))}
+function e(a){this.firstTextNode=(this.isElementMerge=a.nodeType==b.ELEMENT_NODE)?a.lastChild:a;this.textNodes=[this.firstTextNode]}function f(a,b,c,d){this.tagNames=a||[g];this.cssClass=b||"";this.similarClassRegExp=c;this.normalize=d;this.applyToAnyTagName=!1}var g="span",i=/\s+/g;e.prototype={doMerge:function(){for(var a=[],b,c,d=0,e=this.textNodes.length;d<e;++d)b=this.textNodes[d],c=b.parentNode,a[d]=b.data,d&&(c.removeChild(b),c.hasChildNodes()||c.parentNode.removeChild(c));return this.firstTextNode.data=
+a=a.join("")},getLength:function(){for(var a=this.textNodes.length,b=0;a--;)b+=this.textNodes[a].length;return b},toString:function(){for(var a=[],b=0,c=this.textNodes.length;b<c;++b)a[b]="'"+this.textNodes[b].data+"'";return"[Merge("+a.join(",")+")]"}};f.prototype={getAncestorWithClass:function(a){for(var d;a;){if(this.cssClass)if(d=this.cssClass,a.className){var e=a.className.match(this.similarClassRegExp)||[];d=e[e.length-1]===d}else d=!1;else d=!0;if(a.nodeType==b.ELEMENT_NODE&&c.dom.arrayContains(this.tagNames,
+a.tagName.toLowerCase())&&d)return a;a=a.parentNode}return!1},postApply:function(a,b){for(var c=a[0],d=a[a.length-1],f=[],g,i=c,m=d,s=0,x=d.length,o,z,w=0,y=a.length;w<y;++w)if(o=a[w],z=this.getAdjacentMergeableTextNode(o.parentNode,!1)){if(g||(g=new e(z),f.push(g)),g.textNodes.push(o),o===c&&(i=g.firstTextNode,s=i.length),o===d)m=g.firstTextNode,x=g.getLength()}else g=null;if(c=this.getAdjacentMergeableTextNode(d.parentNode,!0))g||(g=new e(d),f.push(g)),g.textNodes.push(c);if(f.length){w=0;for(y=
+f.length;w<y;++w)f[w].doMerge();b.setStart(i,s);b.setEnd(m,x)}},getAdjacentMergeableTextNode:function(a,c){var d=a.nodeType==b.TEXT_NODE,e=d?a.parentNode:a,f=c?"nextSibling":"previousSibling";if(d){if((d=a[f])&&d.nodeType==b.TEXT_NODE)return d}else if((d=e[f])&&this.areElementsMergeable(a,d))return d[c?"firstChild":"lastChild"];return null},areElementsMergeable:function(a,b){var d;if(d=c.dom.arrayContains(this.tagNames,(a.tagName||"").toLowerCase()))if(d=c.dom.arrayContains(this.tagNames,(b.tagName||
+"").toLowerCase()))if(d=a.className.replace(i," ")==b.className.replace(i," "))a:if(a.attributes.length!=b.attributes.length)d=!1;else{d=0;for(var e=a.attributes.length,f,g;d<e;++d)if(f=a.attributes[d],g=f.name,"class"!=g&&(g=b.attributes.getNamedItem(g),f.specified!=g.specified||f.specified&&f.nodeValue!==g.nodeValue)){d=!1;break a}d=!0}return d},createContainer:function(a){a=a.createElement(this.tagNames[0]);this.cssClass&&(a.className=this.cssClass);return a},applyToTextNode:function(a){var b=
+a.parentNode;1==b.childNodes.length&&c.dom.arrayContains(this.tagNames,b.tagName.toLowerCase())?this.cssClass&&(a=this.cssClass,b.className?(b.className&&(b.className=b.className.replace(this.similarClassRegExp,"")),b.className+=" "+a):b.className=a):(b=this.createContainer(c.dom.getDocument(a)),a.parentNode.insertBefore(b,a),b.appendChild(a))},isRemovable:function(a){return c.dom.arrayContains(this.tagNames,a.tagName.toLowerCase())&&b.lang.string(a.className).trim()==this.cssClass},undoToTextNode:function(b,
+c,e){c.containsNode(e)||(b=c.cloneRange(),b.selectNode(e),b.isPointInRange(c.endContainer,c.endOffset)&&a(c.endContainer,c.endOffset)&&(d(e,c.endContainer,c.endOffset),c.setEndAfter(e)),b.isPointInRange(c.startContainer,c.startOffset)&&a(c.startContainer,c.startOffset)&&(e=d(e,c.startContainer,c.startOffset)));this.similarClassRegExp&&e.className&&(e.className=e.className.replace(this.similarClassRegExp,""));if(this.isRemovable(e)){c=e;for(e=c.parentNode;c.firstChild;)e.insertBefore(c.firstChild,
+c);e.removeChild(c)}},applyToRange:function(a){var c=a.getNodes([b.TEXT_NODE]);if(!c.length)try{var d=this.createContainer(a.endContainer.ownerDocument);a.surroundContents(d);this.selectNode(a,d);return}catch(e){}a.splitBoundaries();c=a.getNodes([b.TEXT_NODE]);if(c.length){for(var f=0,g=c.length;f<g;++f)d=c[f],this.getAncestorWithClass(d)||this.applyToTextNode(d);a.setStart(c[0],0);d=c[c.length-1];a.setEnd(d,d.length);this.normalize&&this.postApply(c,a)}},undoToRange:function(a){var c=a.getNodes([b.TEXT_NODE]),
+d,e;c.length?(a.splitBoundaries(),c=a.getNodes([b.TEXT_NODE])):(c=a.endContainer.ownerDocument.createTextNode(b.INVISIBLE_SPACE),a.insertNode(c),a.selectNode(c),c=[c]);for(var f=0,g=c.length;f<g;++f)d=c[f],(e=this.getAncestorWithClass(d))&&this.undoToTextNode(d,a,e);1==g?this.selectNode(a,c[0]):(a.setStart(c[0],0),d=c[c.length-1],a.setEnd(d,d.length),this.normalize&&this.postApply(c,a))},selectNode:function(a,c){var d=c.nodeType===b.ELEMENT_NODE,e="canHaveHTML"in c?c.canHaveHTML:!0,f=d?c.innerHTML:
+c.data;if((f=""===f||f===b.INVISIBLE_SPACE)&&d&&e)try{c.innerHTML=b.INVISIBLE_SPACE}catch(g){}a.selectNodeContents(c);f&&d?a.collapse(!1):f&&(a.setStartAfter(c),a.setEndAfter(c))},getTextSelectedByRange:function(a,b){var c=b.cloneRange();c.selectNodeContents(a);var d=c.intersection(b),d=d?d.toString():"";c.detach();return d},isAppliedToRange:function(a){var c=[],d,e=a.getNodes([b.TEXT_NODE]);if(!e.length)return(d=this.getAncestorWithClass(a.startContainer))?[d]:!1;for(var f=0,g=e.length,i;f<g;++f){i=
+this.getTextSelectedByRange(e[f],a);d=this.getAncestorWithClass(e[f]);if(""!=i&&!d)return!1;c.push(d)}return c},toggleRange:function(a){this.isAppliedToRange(a)?this.undoToRange(a):this.applyToRange(a)}};b.selection.HTMLApplier=f})(wysihtml5,rangy);
+wysihtml5.Commands=Base.extend({constructor:function(b){this.editor=b;this.composer=b.composer;this.doc=this.composer.doc},support:function(b){return wysihtml5.browser.supportsCommand(this.doc,b)},exec:function(b,c){var a=wysihtml5.commands[b],d=wysihtml5.lang.array(arguments).get(),e=a&&a.exec,f=null;this.editor.fire("beforecommand:composer");if(e)d.unshift(this.composer),f=e.apply(a,d);else try{f=this.doc.execCommand(b,!1,c)}catch(g){}this.editor.fire("aftercommand:composer");return f},state:function(b,
+c){var a=wysihtml5.commands[b],d=wysihtml5.lang.array(arguments).get(),e=a&&a.state;if(e)return d.unshift(this.composer),e.apply(a,d);try{return this.doc.queryCommandState(b)}catch(f){return!1}},value:function(b){var c=wysihtml5.commands[b],a=c&&c.value;if(a)return a.call(c,this.composer,b);try{return this.doc.queryCommandValue(b)}catch(d){return null}}});
+(function(b){b.commands.bold={exec:function(c,a){return b.commands.formatInline.exec(c,a,"b")},state:function(c,a){return b.commands.formatInline.state(c,a,"b")},value:function(){}}})(wysihtml5);
+(function(b){function c(c,g){var i=c.doc,h="_wysihtml5-temp-"+ +new Date,k=0,j,n,p;b.commands.formatInline.exec(c,a,d,h,/non-matching-class/g);j=i.querySelectorAll(d+"."+h);for(h=j.length;k<h;k++)for(p in n=j[k],n.removeAttribute("class"),g)n.setAttribute(p,g[p]);k=n;1===h&&(p=e.getTextContent(n),h=!!n.querySelector("*"),p=""===p||p===b.INVISIBLE_SPACE,!h&&p&&(e.setTextContent(n,g.text||n.href),i=i.createTextNode(" "),c.selection.setAfter(n),c.selection.insertNode(i),k=i));c.selection.setAfter(k)}
+var a,d="A",e=b.dom;b.commands.createLink={exec:function(a,b,d){var h=this.state(a,b);h?a.selection.executeAndRestore(function(){for(var a=h.length,b=0,c,d,f;b<a;b++)c=h[b],d=e.getParentElement(c,{nodeName:"code"}),f=e.getTextContent(c),f.match(e.autoLink.URL_REG_EXP)&&!d?e.renameElement(c,"code"):e.replaceWithChildNodes(c)}):(d="object"===typeof d?d:{href:d},c(a,d))},state:function(a,c){return b.commands.formatInline.state(a,c,"A")},value:function(){return a}}})(wysihtml5);
+(function(b){var c=/wysiwyg-font-size-[a-z\-]+/g;b.commands.fontSize={exec:function(a,d,e){return b.commands.formatInline.exec(a,d,"span","wysiwyg-font-size-"+e,c)},state:function(a,d,e){return b.commands.formatInline.state(a,d,"span","wysiwyg-font-size-"+e,c)},value:function(){}}})(wysihtml5);
+(function(b){var c=/wysiwyg-color-[a-z]+/g;b.commands.foreColor={exec:function(a,d,e){return b.commands.formatInline.exec(a,d,"span","wysiwyg-color-"+e,c)},state:function(a,d,e){return b.commands.formatInline.state(a,d,"span","wysiwyg-color-"+e,c)},value:function(){}}})(wysihtml5);
+(function(b){function c(a){for(a=a.previousSibling;a&&a.nodeType===b.TEXT_NODE&&!b.lang.string(a.data).trim();)a=a.previousSibling;return a}function a(a){for(a=a.nextSibling;a&&a.nodeType===b.TEXT_NODE&&!b.lang.string(a.data).trim();)a=a.nextSibling;return a}function d(a){return"BR"===a.nodeName||"block"===g.getStyle("display").from(a)?!0:!1}function e(a,c,d,e){if(e)var f=g.observe(a,"DOMNodeInserted",function(a){var a=a.target,c;a.nodeType===b.ELEMENT_NODE&&(c=g.getStyle("display").from(a),"inline"!==
+c.substr(0,6)&&(a.className+=" "+e))});a.execCommand(c,!1,d);f&&f.stop()}function f(b,d){b.selection.selectLine();b.selection.surround(d);var e=a(d),f=c(d);e&&"BR"===e.nodeName&&e.parentNode.removeChild(e);f&&"BR"===f.nodeName&&f.parentNode.removeChild(f);(e=d.lastChild)&&"BR"===e.nodeName&&e.parentNode.removeChild(e);b.selection.selectNode(d)}var g=b.dom,i="H1 H2 H3 H4 H5 H6 P BLOCKQUOTE DIV".split(" ");b.commands.formatBlock={exec:function(h,k,j,n,p){var q=h.doc,r=this.state(h,k,j,n,p),m,j="string"===
+typeof j?j.toUpperCase():j;if(r)h.selection.executeAndRestoreSimple(function(){p&&(r.className=r.className.replace(p,""));var e=!!b.lang.string(r.className).trim();if(!e&&r.nodeName===(j||"DIV")){var e=r,f=e.ownerDocument,h=a(e),i=c(e);h&&!d(h)&&e.parentNode.insertBefore(f.createElement("br"),h);i&&!d(i)&&e.parentNode.insertBefore(f.createElement("br"),e);g.replaceWithChildNodes(r)}else e&&g.renameElement(r,"DIV")});else{if(null===j||b.lang.array(i).contains(j))if(m=h.selection.getSelectedNode(),
+r=g.getParentElement(m,{nodeName:i})){h.selection.executeAndRestoreSimple(function(){j&&(r=g.renameElement(r,j));if(n){var a=r;a.className?(a.className=a.className.replace(p,""),a.className+=" "+n):a.className=n}});return}h.commands.support(k)?e(q,k,j||"DIV",n):(r=q.createElement(j||"DIV"),n&&(r.className=n),f(h,r))}},state:function(a,b,c,d,e){c="string"===typeof c?c.toUpperCase():c;a=a.selection.getSelectedNode();return g.getParentElement(a,{nodeName:c,className:d,classRegExp:e})},value:function(){}}})(wysihtml5);
+(function(b){function c(c,f,g){var i=c+":"+f;if(!d[i]){var h=d,k=b.selection.HTMLApplier,j=a[c],c=j?[c.toLowerCase(),j.toLowerCase()]:[c.toLowerCase()];h[i]=new k(c,f,g,!0)}return d[i]}var a={strong:"b",em:"i",b:"strong",i:"em"},d={};b.commands.formatInline={exec:function(a,b,d,i,h){b=a.selection.getRange();if(!b)return!1;c(d,i,h).toggleRange(b);a.selection.setSelection(b)},state:function(d,f,g,i,h){var f=d.doc,k=a[g]||g;if(!b.dom.hasElementWithTagName(f,g)&&!b.dom.hasElementWithTagName(f,k)||i&&
+!b.dom.hasElementWithClassName(f,i))return!1;d=d.selection.getRange();return!d?!1:c(g,i,h).isAppliedToRange(d)},value:function(){}}})(wysihtml5);(function(b){b.commands.insertHTML={exec:function(b,a,d){b.commands.support(a)?b.doc.execCommand(a,!1,d):b.selection.insertHTML(d)},state:function(){return!1},value:function(){}}})(wysihtml5);
+(function(b){b.commands.insertImage={exec:function(c,a,d){var d="object"===typeof d?d:{src:d},e=c.doc,a=this.state(c),f;if(a)c.selection.setBefore(a),d=a.parentNode,d.removeChild(a),b.dom.removeEmptyTextNodes(d),"A"===d.nodeName&&!d.firstChild&&(c.selection.setAfter(d),d.parentNode.removeChild(d)),b.quirks.redraw(c.element);else{a=e.createElement("IMG");for(f in d)a[f]=d[f];c.selection.insertNode(a);b.browser.hasProblemsSettingCaretAfterImg()?(d=e.createTextNode(b.INVISIBLE_SPACE),c.selection.insertNode(d),
+c.selection.setAfter(d)):c.selection.setAfter(a)}},state:function(c){var a;if(!b.dom.hasElementWithTagName(c.doc,"IMG"))return!1;a=c.selection.getSelectedNode();if(!a)return!1;if("IMG"===a.nodeName)return a;if(a.nodeType!==b.ELEMENT_NODE)return!1;a=c.selection.getText();if(a=b.lang.string(a).trim())return!1;c=c.selection.getNodes(b.ELEMENT_NODE,function(a){return"IMG"===a.nodeName});return 1!==c.length?!1:c[0]},value:function(b){return(b=this.state(b))&&b.src}}})(wysihtml5);
+(function(b){var c="<br>"+(b.browser.needsSpaceAfterLineBreak()?" ":"");b.commands.insertLineBreak={exec:function(a,d){a.commands.support(d)?(a.doc.execCommand(d,!1,null),b.browser.autoScrollsToCaret()||a.selection.scrollIntoView()):a.commands.exec("insertHTML",c)},state:function(){return!1},value:function(){}}})(wysihtml5);
+(function(b){b.commands.insertOrderedList={exec:function(c,a){var d=c.doc,e=c.selection.getSelectedNode(),f=b.dom.getParentElement(e,{nodeName:"OL"}),g=b.dom.getParentElement(e,{nodeName:"UL"}),e="_wysihtml5-temp-"+(new Date).getTime(),i;c.commands.support(a)?d.execCommand(a,!1,null):f?c.selection.executeAndRestoreSimple(function(){b.dom.resolveList(f)}):g?c.selection.executeAndRestoreSimple(function(){b.dom.renameElement(g,"ol")}):(c.commands.exec("formatBlock","div",e),i=d.querySelector("."+e),
+d=""===i.innerHTML||i.innerHTML===b.INVISIBLE_SPACE,c.selection.executeAndRestoreSimple(function(){f=b.dom.convertToList(i,"ol")}),d&&c.selection.selectNode(f.querySelector("li")))},state:function(c){c=c.selection.getSelectedNode();return b.dom.getParentElement(c,{nodeName:"OL"})},value:function(){}}})(wysihtml5);
+(function(b){b.commands.insertUnorderedList={exec:function(c,a){var d=c.doc,e=c.selection.getSelectedNode(),f=b.dom.getParentElement(e,{nodeName:"UL"}),g=b.dom.getParentElement(e,{nodeName:"OL"}),e="_wysihtml5-temp-"+(new Date).getTime(),i;c.commands.support(a)?d.execCommand(a,!1,null):f?c.selection.executeAndRestoreSimple(function(){b.dom.resolveList(f)}):g?c.selection.executeAndRestoreSimple(function(){b.dom.renameElement(g,"ul")}):(c.commands.exec("formatBlock","div",e),i=d.querySelector("."+e),
+d=""===i.innerHTML||i.innerHTML===b.INVISIBLE_SPACE,c.selection.executeAndRestoreSimple(function(){f=b.dom.convertToList(i,"ul")}),d&&c.selection.selectNode(f.querySelector("li")))},state:function(c){c=c.selection.getSelectedNode();return b.dom.getParentElement(c,{nodeName:"UL"})},value:function(){}}})(wysihtml5);(function(b){b.commands.italic={exec:function(c,a){return b.commands.formatInline.exec(c,a,"i")},state:function(c,a){return b.commands.formatInline.state(c,a,"i")},value:function(){}}})(wysihtml5);
+(function(b){var c=/wysiwyg-text-align-[a-z]+/g;b.commands.justifyCenter={exec:function(a){return b.commands.formatBlock.exec(a,"formatBlock",null,"wysiwyg-text-align-center",c)},state:function(a){return b.commands.formatBlock.state(a,"formatBlock",null,"wysiwyg-text-align-center",c)},value:function(){}}})(wysihtml5);
+(function(b){var c=/wysiwyg-text-align-[a-z]+/g;b.commands.justifyLeft={exec:function(a){return b.commands.formatBlock.exec(a,"formatBlock",null,"wysiwyg-text-align-left",c)},state:function(a){return b.commands.formatBlock.state(a,"formatBlock",null,"wysiwyg-text-align-left",c)},value:function(){}}})(wysihtml5);
+(function(b){var c=/wysiwyg-text-align-[a-z]+/g;b.commands.justifyRight={exec:function(a){return b.commands.formatBlock.exec(a,"formatBlock",null,"wysiwyg-text-align-right",c)},state:function(a){return b.commands.formatBlock.state(a,"formatBlock",null,"wysiwyg-text-align-right",c)},value:function(){}}})(wysihtml5);(function(b){b.commands.underline={exec:function(c,a){return b.commands.formatInline.exec(c,a,"u")},state:function(c,a){return b.commands.formatInline.state(c,a,"u")},value:function(){}}})(wysihtml5);
+(function(b){var c='<span id="_wysihtml5-undo" class="_wysihtml5-temp">'+b.INVISIBLE_SPACE+"</span>",a='<span id="_wysihtml5-redo" class="_wysihtml5-temp">'+b.INVISIBLE_SPACE+"</span>",d=b.dom;b.UndoManager=b.lang.Dispatcher.extend({constructor:function(a){this.editor=a;this.composer=a.composer;this.element=this.composer.element;this.history=[this.composer.getValue()];this.position=1;this.composer.commands.support("insertHTML")&&this._observe()},_observe:function(){var e=this,f=this.composer.sandbox.getDocument(),
+g;d.observe(this.element,"keydown",function(a){if(!(a.altKey||!a.ctrlKey&&!a.metaKey)){var b=a.keyCode,c=90===b&&a.shiftKey||89===b;90===b&&!a.shiftKey?(e.undo(),a.preventDefault()):c&&(e.redo(),a.preventDefault())}});d.observe(this.element,"keydown",function(a){a=a.keyCode;a!==g&&(g=a,(8===a||46===a)&&e.transact())});if(b.browser.hasUndoInContextMenu()){var i,h,k=function(){for(var a;a=f.querySelector("._wysihtml5-temp");)a.parentNode.removeChild(a);clearInterval(i)};d.observe(this.element,"contextmenu",
+function(){k();e.composer.selection.executeAndRestoreSimple(function(){e.element.lastChild&&e.composer.selection.setAfter(e.element.lastChild);f.execCommand("insertHTML",!1,c);f.execCommand("insertHTML",!1,a);f.execCommand("undo",!1,null)});i=setInterval(function(){f.getElementById("_wysihtml5-redo")?(k(),e.redo()):f.getElementById("_wysihtml5-undo")||(k(),e.undo())},400);h||(h=!0,d.observe(document,"mousedown",k),d.observe(f,["mousedown","paste","cut","copy"],k))})}this.editor.observe("newword:composer",
+function(){e.transact()}).observe("beforecommand:composer",function(){e.transact()})},transact:function(){var a=this.history[this.position-1],b=this.composer.getValue();if(b!=a){if(40<(this.history.length=this.position))this.history.shift(),this.position--;this.position++;this.history.push(b)}},undo:function(){this.transact();1>=this.position||(this.set(this.history[--this.position-1]),this.editor.fire("undo:composer"))},redo:function(){this.position>=this.history.length||(this.set(this.history[++this.position-
+1]),this.editor.fire("redo:composer"))},set:function(a){this.composer.setValue(a);this.editor.focus(!0)}})})(wysihtml5);
+wysihtml5.views.View=Base.extend({constructor:function(b,c,a){this.parent=b;this.element=c;this.config=a;this._observeViewChange()},_observeViewChange:function(){var b=this;this.parent.observe("beforeload",function(){b.parent.observe("change_view",function(c){c===b.name?(b.parent.currentView=b,b.show(),setTimeout(function(){b.focus()},0)):b.hide()})})},focus:function(){if(this.element.ownerDocument.querySelector(":focus")!==this.element)try{this.element.focus()}catch(b){}},hide:function(){this.element.style.display=
+"none"},show:function(){this.element.style.display=""},disable:function(){this.element.setAttribute("disabled","disabled")},enable:function(){this.element.removeAttribute("disabled")}});
+(function(b){var c=b.dom,a=b.browser;b.views.Composer=b.views.View.extend({name:"composer",CARET_HACK:"<br>",constructor:function(a,b,c){this.base(a,b,c);this.textarea=this.parent.textarea;this._initSandbox()},clear:function(){this.element.innerHTML=a.displaysCaretInEmptyContentEditableCorrectly()?"":this.CARET_HACK},getValue:function(a){var c=this.isEmpty()?"":b.quirks.getCorrectInnerHTML(this.element);a&&(c=this.parent.parse(c));return c=b.lang.string(c).replace(b.INVISIBLE_SPACE).by("")},setValue:function(a,
+b){b&&(a=this.parent.parse(a));this.element.innerHTML=a},show:function(){this.iframe.style.display=this._displayStyle||"";this.disable();this.enable()},hide:function(){this._displayStyle=c.getStyle("display").from(this.iframe);"none"===this._displayStyle&&(this._displayStyle=null);this.iframe.style.display="none"},disable:function(){this.element.removeAttribute("contentEditable");this.base()},enable:function(){this.element.setAttribute("contentEditable","true");this.base()},focus:function(a){b.browser.doesAsyncFocus()&&
+this.hasPlaceholderSet()&&this.clear();this.base();var c=this.element.lastChild;a&&c&&("BR"===c.nodeName?this.selection.setBefore(this.element.lastChild):this.selection.setAfter(this.element.lastChild))},getTextContent:function(){return c.getTextContent(this.element)},hasPlaceholderSet:function(){return this.getTextContent()==this.textarea.element.getAttribute("placeholder")},isEmpty:function(){var a=this.element.innerHTML;return""===a||a===this.CARET_HACK||this.hasPlaceholderSet()||""===this.getTextContent()&&
+!this.element.querySelector("blockquote, ul, ol, img, embed, object, table, iframe, svg, video, audio, button, input, select, textarea")},_initSandbox:function(){var a=this;this.sandbox=new c.Sandbox(function(){a._create()},{stylesheets:this.config.stylesheets});this.iframe=this.sandbox.getIframe();var b=document.createElement("input");b.type="hidden";b.name="_wysihtml5_mode";b.value=1;var f=this.textarea.element;c.insert(this.iframe).after(f);c.insert(b).after(f)},_create:function(){var d=this;this.doc=
+this.sandbox.getDocument();this.element=this.doc.body;this.textarea=this.parent.textarea;this.element.innerHTML=this.textarea.getValue(!0);this.enable();this.selection=new b.Selection(this.parent);this.commands=new b.Commands(this.parent);c.copyAttributes("className spellcheck title lang dir accessKey".split(" ")).from(this.textarea.element).to(this.element);c.addClass(this.element,this.config.composerClassName);this.config.style&&this.style();this.observe();var e=this.config.name;e&&(c.addClass(this.element,
+e),c.addClass(this.iframe,e));(e="string"===typeof this.config.placeholder?this.config.placeholder:this.textarea.element.getAttribute("placeholder"))&&c.simulatePlaceholder(this.parent,this,e);this.commands.exec("styleWithCSS",!1);this._initAutoLinking();this._initObjectResizing();this._initUndoManager();(this.textarea.element.hasAttribute("autofocus")||document.querySelector(":focus")==this.textarea.element)&&setTimeout(function(){d.focus()},100);b.quirks.insertLineBreakOnReturn(this);a.clearsContentEditableCorrectly()||
+b.quirks.ensureProperClearing(this);a.clearsListsInContentEditableCorrectly()||b.quirks.ensureProperClearingOfLists(this);this.initSync&&this.config.sync&&this.initSync();this.textarea.hide();this.parent.fire("beforeload").fire("load")},_initAutoLinking:function(){var d=this,e=a.canDisableAutoLinking(),f=a.doesAutoLinkingInContentEditable();e&&this.commands.exec("autoUrlDetect",!1);if(this.config.autoLink){(!f||f&&e)&&this.parent.observe("newword:composer",function(){d.selection.executeAndRestore(function(a,
+b){c.autoLink(b.parentNode)})});var g=this.sandbox.getDocument().getElementsByTagName("a"),i=c.autoLink.URL_REG_EXP,h=function(a){a=b.lang.string(c.getTextContent(a)).trim();"www."===a.substr(0,4)&&(a="http://"+a);return a};c.observe(this.element,"keydown",function(a){if(g.length){var a=d.selection.getSelectedNode(a.target.ownerDocument),b=c.getParentElement(a,{nodeName:"A"},4),e;b&&(e=h(b),setTimeout(function(){var a=h(b);a!==e&&a.match(i)&&b.setAttribute("href",a)},0))}})}},_initObjectResizing:function(){var d=
+["width","height"],e=d.length,f=this.element;this.commands.exec("enableObjectResizing",this.config.allowObjectResizing);this.config.allowObjectResizing?a.supportsEvent("resizeend")&&c.observe(f,"resizeend",function(a){for(var a=a.target||a.srcElement,c=a.style,h=0,k;h<e;h++)k=d[h],c[k]&&(a.setAttribute(k,parseInt(c[k],10)),c[k]="");b.quirks.redraw(f)}):a.supportsEvent("resizestart")&&c.observe(f,"resizestart",function(a){a.preventDefault()})},_initUndoManager:function(){new b.UndoManager(this.parent)}})})(wysihtml5);
+(function(b){var c=b.dom,a=document,d=window,e=a.createElement("div"),f="background-color color cursor font-family font-size font-style font-variant font-weight line-height letter-spacing text-align text-decoration text-indent text-rendering word-break word-wrap word-spacing".split(" "),g="background-color border-collapse border-bottom-color border-bottom-style border-bottom-width border-left-color border-left-style border-left-width border-right-color border-right-style border-right-width border-top-color border-top-style border-top-width clear display float margin-bottom margin-left margin-right margin-top outline-color outline-offset outline-width outline-style padding-left padding-right padding-top padding-bottom position top left right bottom z-index vertical-align text-align -webkit-box-sizing -moz-box-sizing -ms-box-sizing box-sizing -webkit-box-shadow -moz-box-shadow -ms-box-shadow box-shadow -webkit-border-top-right-radius -moz-border-radius-topright border-top-right-radius -webkit-border-bottom-right-radius -moz-border-radius-bottomright border-bottom-right-radius -webkit-border-bottom-left-radius -moz-border-radius-bottomleft border-bottom-left-radius -webkit-border-top-left-radius -moz-border-radius-topleft border-top-left-radius width height".split(" "),
+i="width height top left right bottom".split(" "),h=["html { height: 100%; }","body { min-height: 100%; padding: 0; margin: 0; margin-top: -1px; padding-top: 1px; }","._wysihtml5-temp { display: none; }",b.browser.isGecko?"body.placeholder { color: graytext !important; }":"body.placeholder { color: #a9a9a9 !important; }","body[disabled] { background-color: #eee !important; color: #999 !important; cursor: default !important; }","img:-moz-broken { -moz-force-broken-image-icon: 1; height: 24px; width: 24px; }"],
+k=function(b){if(b.setActive)try{b.setActive()}catch(e){}else{var f=b.style,h=a.documentElement.scrollTop||a.body.scrollTop,g=a.documentElement.scrollLeft||a.body.scrollLeft,f={position:f.position,top:f.top,left:f.left,WebkitUserSelect:f.WebkitUserSelect};c.setStyles({position:"absolute",top:"-99999px",left:"-99999px",WebkitUserSelect:"none"}).on(b);b.focus();c.setStyles(f).on(b);d.scrollTo&&d.scrollTo(g,h)}};b.views.Composer.prototype.style=function(){var j=this,n=a.querySelector(":focus"),p=this.textarea.element,
+q=p.hasAttribute("placeholder"),r=q&&p.getAttribute("placeholder");this.focusStylesHost=this.focusStylesHost||e.cloneNode(!1);this.blurStylesHost=this.blurStylesHost||e.cloneNode(!1);q&&p.removeAttribute("placeholder");p===n&&p.blur();c.copyStyles(g).from(p).to(this.iframe).andTo(this.blurStylesHost);c.copyStyles(f).from(p).to(this.element).andTo(this.blurStylesHost);c.insertCSS(h).into(this.element.ownerDocument);k(p);c.copyStyles(g).from(p).to(this.focusStylesHost);c.copyStyles(f).from(p).to(this.focusStylesHost);
+var m=b.lang.array(g).without(["display"]);n?n.focus():p.blur();q&&p.setAttribute("placeholder",r);if(!b.browser.hasCurrentStyleProperty())var s=c.observe(d,"resize",function(){if(c.contains(document.documentElement,j.iframe)){var a=c.getStyle("display").from(p),b=c.getStyle("display").from(j.iframe);p.style.display="";j.iframe.style.display="none";c.copyStyles(i).from(p).to(j.iframe).andTo(j.focusStylesHost).andTo(j.blurStylesHost);j.iframe.style.display=b;p.style.display=a}else s.stop()});this.parent.observe("focus:composer",
+function(){c.copyStyles(m).from(j.focusStylesHost).to(j.iframe);c.copyStyles(f).from(j.focusStylesHost).to(j.element)});this.parent.observe("blur:composer",function(){c.copyStyles(m).from(j.blurStylesHost).to(j.iframe);c.copyStyles(f).from(j.blurStylesHost).to(j.element)});return this}})(wysihtml5);
+(function(b){var c=b.dom,a=b.browser,d={66:"bold",73:"italic",85:"underline"};b.views.Composer.prototype.observe=function(){var e=this,f=this.getValue(),g=this.sandbox.getIframe(),i=this.element,h=a.supportsEventsInIframeCorrectly()?i:this.sandbox.getWindow(),k=a.supportsEvent("drop")?["drop","paste"]:["dragdrop","paste"];c.observe(g,"DOMNodeRemoved",function(){clearInterval(j);e.parent.fire("destroy:composer")});var j=setInterval(function(){c.contains(document.documentElement,g)||(clearInterval(j),
+e.parent.fire("destroy:composer"))},250);c.observe(h,"focus",function(){e.parent.fire("focus").fire("focus:composer");setTimeout(function(){f=e.getValue()},0)});c.observe(h,"blur",function(){f!==e.getValue()&&e.parent.fire("change").fire("change:composer");e.parent.fire("blur").fire("blur:composer")});b.browser.isIos()&&c.observe(i,"blur",function(){var a=i.ownerDocument.createElement("input"),b=document.documentElement.scrollTop||document.body.scrollTop,c=document.documentElement.scrollLeft||document.body.scrollLeft;
+try{e.selection.insertNode(a)}catch(d){i.appendChild(a)}a.focus();a.parentNode.removeChild(a);window.scrollTo(c,b)});c.observe(i,"dragenter",function(){e.parent.fire("unset_placeholder")});a.firesOnDropOnlyWhenOnDragOverIsCancelled()&&c.observe(i,["dragover","dragenter"],function(a){a.preventDefault()});c.observe(i,k,function(b){var c=b.dataTransfer,d;c&&a.supportsDataTransfer()&&(d=c.getData("text/html")||c.getData("text/plain"));d?(i.focus(),e.commands.exec("insertHTML",d),e.parent.fire("paste").fire("paste:composer"),
+b.stopPropagation(),b.preventDefault()):setTimeout(function(){e.parent.fire("paste").fire("paste:composer")},0)});c.observe(i,"keyup",function(a){a=a.keyCode;(a===b.SPACE_KEY||a===b.ENTER_KEY)&&e.parent.fire("newword:composer")});this.parent.observe("paste:composer",function(){setTimeout(function(){e.parent.fire("newword:composer")},0)});a.canSelectImagesInContentEditable()||c.observe(i,"mousedown",function(a){var b=a.target;"IMG"===b.nodeName&&(e.selection.selectNode(b),a.preventDefault())});c.observe(i,
+"keydown",function(a){var b=d[a.keyCode];if((a.ctrlKey||a.metaKey)&&!a.altKey&&b)e.commands.exec(b),a.preventDefault()});c.observe(i,"keydown",function(a){var c=e.selection.getSelectedNode(!0),d=a.keyCode;if(c&&"IMG"===c.nodeName&&(d===b.BACKSPACE_KEY||d===b.DELETE_KEY))d=c.parentNode,d.removeChild(c),"A"===d.nodeName&&!d.firstChild&&d.parentNode.removeChild(d),setTimeout(function(){b.quirks.redraw(i)},0),a.preventDefault()});var n={IMG:"Image: ",A:"Link: "};c.observe(i,"mouseover",function(a){var a=
+a.target,b=a.nodeName;!("A"!==b&&"IMG"!==b)&&!a.hasAttribute("title")&&(b=n[b]+(a.getAttribute("href")||a.getAttribute("src")),a.setAttribute("title",b))})}})(wysihtml5);
+(function(b){b.views.Synchronizer=Base.extend({constructor:function(b,a,d){this.editor=b;this.textarea=a;this.composer=d;this._observe()},fromComposerToTextarea:function(c){this.textarea.setValue(b.lang.string(this.composer.getValue()).trim(),c)},fromTextareaToComposer:function(b){var a=this.textarea.getValue();a?this.composer.setValue(a,b):(this.composer.clear(),this.editor.fire("set_placeholder"))},sync:function(b){"textarea"===this.editor.currentView.name?this.fromTextareaToComposer(b):this.fromComposerToTextarea(b)},
+_observe:function(){var c,a=this,d=this.textarea.element.form,e=function(){c=setInterval(function(){a.fromComposerToTextarea()},400)},f=function(){clearInterval(c);c=null};e();d&&(b.dom.observe(d,"submit",function(){a.sync(!0)}),b.dom.observe(d,"reset",function(){setTimeout(function(){a.fromTextareaToComposer()},0)}));this.editor.observe("change_view",function(b){if(b==="composer"&&!c){a.fromTextareaToComposer(true);e()}else if(b==="textarea"){a.fromComposerToTextarea(true);f()}});this.editor.observe("destroy:composer",
+f)}})})(wysihtml5);
+wysihtml5.views.Textarea=wysihtml5.views.View.extend({name:"textarea",constructor:function(b,c,a){this.base(b,c,a);this._observe()},clear:function(){this.element.value=""},getValue:function(b){var c=this.isEmpty()?"":this.element.value;b&&(c=this.parent.parse(c));return c},setValue:function(b,c){c&&(b=this.parent.parse(b));this.element.value=b},hasPlaceholderSet:function(){var b=wysihtml5.browser.supportsPlaceholderAttributeOn(this.element),c=this.element.getAttribute("placeholder")||null,a=this.element.value;
+return b&&!a||a===c},isEmpty:function(){return!wysihtml5.lang.string(this.element.value).trim()||this.hasPlaceholderSet()},_observe:function(){var b=this.element,c=this.parent,a={focusin:"focus",focusout:"blur"},d=wysihtml5.browser.supportsEvent("focusin")?["focusin","focusout","change"]:["focus","blur","change"];c.observe("beforeload",function(){wysihtml5.dom.observe(b,d,function(b){b=a[b.type]||b.type;c.fire(b).fire(b+":textarea")});wysihtml5.dom.observe(b,["paste","drop"],function(){setTimeout(function(){c.fire("paste").fire("paste:textarea")},
+0)})})}});
+(function(b){var c=b.dom;b.toolbar.Dialog=b.lang.Dispatcher.extend({constructor:function(a,b){this.link=a;this.container=b},_observe:function(){if(!this._observed){var a=this,d=function(b){var c=a._serialize();c==a.elementToChange?a.fire("edit",c):a.fire("save",c);a.hide();b.preventDefault();b.stopPropagation()};c.observe(a.link,"click",function(){c.hasClass(a.link,"wysihtml5-command-dialog-opened")&&setTimeout(function(){a.hide()},0)});c.observe(this.container,"keydown",function(c){var e=c.keyCode;
+e===b.ENTER_KEY&&d(c);e===b.ESCAPE_KEY&&a.hide()});c.delegate(this.container,"[data-wysihtml5-dialog-action=save]","click",d);c.delegate(this.container,"[data-wysihtml5-dialog-action=cancel]","click",function(b){a.fire("cancel");a.hide();b.preventDefault();b.stopPropagation()});for(var e=this.container.querySelectorAll("input, select, textarea"),f=0,g=e.length,i=function(){clearInterval(a.interval)};f<g;f++)c.observe(e[f],"change",i);this._observed=!0}},_serialize:function(){for(var a=this.elementToChange||
+{},b=this.container.querySelectorAll("[data-wysihtml5-dialog-field]"),c=b.length,f=0;f<c;f++)a[b[f].getAttribute("data-wysihtml5-dialog-field")]=b[f].value;return a},_interpolate:function(a){for(var b,c,f=document.querySelector(":focus"),g=this.container.querySelectorAll("[data-wysihtml5-dialog-field]"),i=g.length,h=0;h<i;h++)b=g[h],b!==f&&!(a&&"hidden"===b.type)&&(c=b.getAttribute("data-wysihtml5-dialog-field"),c=this.elementToChange?this.elementToChange[c]||"":b.defaultValue,b.value=c)},show:function(a){var b=
+this,e=this.container.querySelector("input, select, textarea");this.elementToChange=a;this._observe();this._interpolate();a&&(this.interval=setInterval(function(){b._interpolate(!0)},500));c.addClass(this.link,"wysihtml5-command-dialog-opened");this.container.style.display="";this.fire("show");if(e&&!a)try{e.focus()}catch(f){}},hide:function(){clearInterval(this.interval);this.elementToChange=null;c.removeClass(this.link,"wysihtml5-command-dialog-opened");this.container.style.display="none";this.fire("hide")}})})(wysihtml5);
+(function(b){var c=b.dom,a={position:"relative"},d={left:0,margin:0,opacity:0,overflow:"hidden",padding:0,position:"absolute",top:0,zIndex:1},e={cursor:"inherit",fontSize:"50px",height:"50px",marginTop:"-25px",outline:0,padding:0,position:"absolute",right:"-4px",top:"50%"},f={"x-webkit-speech":"",speech:""};b.toolbar.Speech=function(g,i){var h=document.createElement("input");if(b.browser.supportsSpeechApiOn(h)){var k=document.createElement("div");b.lang.object(d).merge({width:i.offsetWidth+"px",height:i.offsetHeight+
+"px"});c.insert(h).into(k);c.insert(k).into(i);c.setStyles(e).on(h);c.setAttributes(f).on(h);c.setStyles(d).on(k);c.setStyles(a).on(i);c.observe(h,"onwebkitspeechchange"in h?"webkitspeechchange":"speechchange",function(){g.execCommand("insertText",h.value);h.value=""});c.observe(h,"click",function(a){c.hasClass(i,"wysihtml5-command-disabled")&&a.preventDefault();a.stopPropagation()})}else i.style.display="none"}})(wysihtml5);
+(function(b){var c=b.dom;b.toolbar.Toolbar=Base.extend({constructor:function(a,c){this.editor=a;this.container="string"===typeof c?document.getElementById(c):c;this.composer=a.composer;this._getLinks("command");this._getLinks("action");this._observe();this.show();for(var e=this.container.querySelectorAll("[data-wysihtml5-command=insertSpeech]"),f=e.length,g=0;g<f;g++)new b.toolbar.Speech(this,e[g])},_getLinks:function(a){for(var c=this[a+"Links"]=b.lang.array(this.container.querySelectorAll("[data-wysihtml5-"+
+a+"]")).get(),e=c.length,f=0,g=this[a+"Mapping"]={},i,h,k,j,n;f<e;f++)i=c[f],k=i.getAttribute("data-wysihtml5-"+a),j=i.getAttribute("data-wysihtml5-"+a+"-value"),h=this.container.querySelector("[data-wysihtml5-"+a+"-group='"+k+"']"),n=this._getDialog(i,k),g[k+":"+j]={link:i,group:h,name:k,value:j,dialog:n,state:!1}},_getDialog:function(a,c){var e=this,f=this.container.querySelector("[data-wysihtml5-dialog='"+c+"']"),g,i;f&&(g=new b.toolbar.Dialog(a,f),g.observe("show",function(){i=e.composer.selection.getBookmark();
+e.editor.fire("show:dialog",{command:c,dialogContainer:f,commandLink:a})}),g.observe("save",function(b){i&&e.composer.selection.setBookmark(i);e._execCommand(c,b);e.editor.fire("save:dialog",{command:c,dialogContainer:f,commandLink:a})}),g.observe("cancel",function(){e.editor.focus(!1);e.editor.fire("cancel:dialog",{command:c,dialogContainer:f,commandLink:a})}));return g},execCommand:function(a,b){if(!this.commandsDisabled){var c=this.commandMapping[a+":"+b];c&&c.dialog&&!c.state?c.dialog.show():
+this._execCommand(a,b)}},_execCommand:function(a,b){this.editor.focus(!1);this.composer.commands.exec(a,b);this._updateLinkStates()},execAction:function(a){var b=this.editor;switch(a){case "change_view":b.currentView===b.textarea?b.fire("change_view","composer"):b.fire("change_view","textarea")}},_observe:function(){for(var a=this,b=this.editor,e=this.container,f=this.commandLinks.concat(this.actionLinks),g=f.length,i=0;i<g;i++)c.setAttributes({href:"javascript:;",unselectable:"on"}).on(f[i]);c.delegate(e,
+"[data-wysihtml5-command]","mousedown",function(a){a.preventDefault()});c.delegate(e,"[data-wysihtml5-command]","click",function(b){var c=this.getAttribute("data-wysihtml5-command"),d=this.getAttribute("data-wysihtml5-command-value");a.execCommand(c,d);b.preventDefault()});c.delegate(e,"[data-wysihtml5-action]","click",function(b){var c=this.getAttribute("data-wysihtml5-action");a.execAction(c);b.preventDefault()});b.observe("focus:composer",function(){a.bookmark=null;clearInterval(a.interval);a.interval=
+setInterval(function(){a._updateLinkStates()},500)});b.observe("blur:composer",function(){clearInterval(a.interval)});b.observe("destroy:composer",function(){clearInterval(a.interval)});b.observe("change_view",function(b){setTimeout(function(){a.commandsDisabled="composer"!==b;a._updateLinkStates();a.commandsDisabled?c.addClass(e,"wysihtml5-commands-disabled"):c.removeClass(e,"wysihtml5-commands-disabled")},0)})},_updateLinkStates:function(){var a=this.commandMapping,d=this.actionMapping,e,f,g;for(e in a)if(g=
+a[e],this.commandsDisabled?(f=!1,c.removeClass(g.link,"wysihtml5-command-active"),g.group&&c.removeClass(g.group,"wysihtml5-command-active"),g.dialog&&g.dialog.hide()):(f=this.composer.commands.state(g.name,g.value),b.lang.object(f).isArray()&&(f=1===f.length?f[0]:!0),c.removeClass(g.link,"wysihtml5-command-disabled"),g.group&&c.removeClass(g.group,"wysihtml5-command-disabled")),g.state!==f)(g.state=f)?(c.addClass(g.link,"wysihtml5-command-active"),g.group&&c.addClass(g.group,"wysihtml5-command-active"),
+g.dialog&&("object"===typeof f?g.dialog.show(f):g.dialog.hide())):(c.removeClass(g.link,"wysihtml5-command-active"),g.group&&c.removeClass(g.group,"wysihtml5-command-active"),g.dialog&&g.dialog.hide());for(e in d)a=d[e],"change_view"===a.name&&(a.state=this.editor.currentView===this.editor.textarea,a.state?c.addClass(a.link,"wysihtml5-action-active"):c.removeClass(a.link,"wysihtml5-action-active"))},show:function(){this.container.style.display=""},hide:function(){this.container.style.display="none"}})})(wysihtml5);
+(function(b){var c={name:void 0,style:!0,toolbar:void 0,autoLink:!0,parserRules:{tags:{br:{},span:{},div:{},p:{}},classes:{}},parser:b.dom.parse,composerClassName:"wysihtml5-editor",bodyClassName:"wysihtml5-supported",stylesheets:[],placeholderText:void 0,allowObjectResizing:!0,supportTouchDevices:!0};b.Editor=b.lang.Dispatcher.extend({constructor:function(a,d){this.textareaElement="string"===typeof a?document.getElementById(a):a;this.config=b.lang.object({}).merge(c).merge(d).get();this.currentView=
+this.textarea=new b.views.Textarea(this,this.textareaElement,this.config);this._isCompatible=b.browser.supported();if(!this._isCompatible||!this.config.supportTouchDevices&&b.browser.isTouchDevice()){var e=this;setTimeout(function(){e.fire("beforeload").fire("load")},0)}else{b.dom.addClass(document.body,this.config.bodyClassName);this.currentView=this.composer=new b.views.Composer(this,this.textareaElement,this.config);"function"===typeof this.config.parser&&this._initParser();this.observe("beforeload",
+function(){this.synchronizer=new b.views.Synchronizer(this,this.textarea,this.composer);this.config.toolbar&&(this.toolbar=new b.toolbar.Toolbar(this,this.config.toolbar))});try{console.log("Heya! This page is using wysihtml5 for rich text editing. Check out https://github.com/xing/wysihtml5")}catch(f){}}},isCompatible:function(){return this._isCompatible},clear:function(){this.currentView.clear();return this},getValue:function(a){return this.currentView.getValue(a)},setValue:function(a,b){if(!a)return this.clear();
+this.currentView.setValue(a,b);return this},focus:function(a){this.currentView.focus(a);return this},disable:function(){this.currentView.disable();return this},enable:function(){this.currentView.enable();return this},isEmpty:function(){return this.currentView.isEmpty()},hasPlaceholderSet:function(){return this.currentView.hasPlaceholderSet()},parse:function(a){var c=this.config.parser(a,this.config.parserRules,this.composer.sandbox.getDocument(),!0);"object"===typeof a&&b.quirks.redraw(a);return c},
+_initParser:function(){this.observe("paste:composer",function(){var a=this;a.composer.selection.executeAndRestore(function(){b.quirks.cleanPastedHTML(a.composer.element);a.parse(a.composer.element)},!0)});this.observe("paste:textarea",function(){this.textarea.setValue(this.parse(this.textarea.getValue()))})}})})(wysihtml5);
1
0
r1884 - in trunk/wao-web/src/main/webapp: . js js/bootstrap-wysiwyg js/bootstrap-wysiwyg/external js/bootstrap-wysiwyg/external/google-code-prettify js/jquery.hotkeys js/jquery.hotkeys/test js/jquery.hotkeys/test/lib js/jquery.hotkeys/test/spec
by sbavencoff@users.forge.codelutin.com 14 Apr '14
by sbavencoff@users.forge.codelutin.com 14 Apr '14
14 Apr '14
Author: sbavencoff
Date: 2014-04-14 11:57:45 +0200 (Mon, 14 Apr 2014)
New Revision: 1884
Url: http://forge.codelutin.com/projects/wao/repository/revisions/1884
Log:
refs #4553 : add editor wysiwyg
Added:
trunk/wao-web/src/main/webapp/js/
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/LICENSE
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/README.md
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/bootstrap-wysiwyg.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-apollo.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-basic.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-clj.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-css.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-dart.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-erlang.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-go.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-hs.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-lisp.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-llvm.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-lua.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-matlab.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-ml.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-mumps.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-n.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-pascal.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-proto.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-r.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-rd.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-scala.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-sql.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-tcl.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-tex.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-vb.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-vhdl.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-wiki.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-xq.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-yaml.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/prettify.css
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/prettify.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/run_prettify.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/jquery.hotkeys.js
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/index.css
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/index.html
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/promo-868x350.png
trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/republish.sh
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/.gitignore
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/.travis.yml
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/Gruntfile.js
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/README.md
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/hotkeys.jquery.json
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/jquery-1.4.2.js
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/jquery.hotkeys.js
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/package.json
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-01.html
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-02.html
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-03.html
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-04.html
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-05.html
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-06.html
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-07.html
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/SpecRunner.html
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/boot.js
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/console.js
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/jasmine-html.js
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/jasmine.css
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/jasmine.js
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/jasmine_favicon.png
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/sinon.js
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/underscore.js
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/spec/
trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/spec/bindingSpec.js
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/LICENSE
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/LICENSE (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/LICENSE 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2013 Damjan Vujnovic, David de Florinier, Gojko Adzic
+
+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.
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/README.md
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/README.md (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/README.md 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,85 @@
+bootstrap-wysiwyg
+=================
+
+Tiny bootstrap-compatible WISWYG rich text editor, based on browser execCommand, built originally for [MindMup](http://www.mindmup.com). Here are the key features:
+
+* Automatically binds standard hotkeys for common operations on Mac and Windows
+* Drag and drop files to insert images, support image upload (also taking photos on mobile devices)
+* Allows a custom built toolbar, no magic markup generators, enabling the web site to use all the goodness of Bootstrap, Font Awesome and so on...
+* Does not force any styling - it's all up to you
+* Uses standard browser features, no magic non-standard code, toolbar and keyboard configurable to execute any supported [browser command](https://developer.mozilla.org/en/docs/Rich-Text_Editing_in_Mozilla
+)
+* Does not create a separate frame, backup text areas etc - instead keeps it simple and runs everything inline in a DIV
+* (Optionally) cleans up trailing whitespace and empty divs and spans
+* Requires a modern browser (tested in Chrome 26, Firefox 19, Safari 6)
+* Supports mobile devices (tested on IOS 6 Ipad/Iphone and Android 4.1.1 Chrome)
+
+Basic Usage
+-----------
+
+See http://mindmup.github.com/bootstrap-wysiwyg/
+
+Customising
+-----------
+You can assign commands to hotkeys and toolbar links. For a toolbar link, just put the execCommand command name into a data-edit attribute.
+For more info on execCommand, see http://www.quirksmode.org/dom/execCommand.html and https://developer.mozilla.org/en/docs/Rich-Text_Editing_in_Mozilla
+
+```html
+<div class="btn-toolbar" data-role="editor-toolbar" data-target="#editor">
+ <a class="btn btn-large" data-edit="bold"><i class="icon-bold"></i></a>
+</div>
+```
+
+To pass arguments to a command, separate a command with a space.
+
+```html
+ <a data-edit="fontName Arial">...</a>
+```
+
+You can also use input type='text' with a data-edit attribute. When the value
+is changed, the command from the data-edit attribute will be applied using the
+input value as the command argument
+
+```html
+<input type="text" data-edit="createLink">
+```
+If the input type is file, when a file is selected the contents will be read in using the FileReader API and the data URL will be used as the argument
+
+```html
+<input type="file" data-edit="insertImage">
+```
+
+To change hotkeys, specify the map of hotkeys to commands in the hotKeys option. For example:
+
+```javascript
+$('#editor').wysiwyg({
+ hotKeys: {
+ 'ctrl+b meta+b': 'bold',
+ 'ctrl+i meta+i': 'italic',
+ 'ctrl+u meta+u': 'underline',
+ 'ctrl+z meta+z': 'undo',
+ 'ctrl+y meta+y meta+shift+z': 'redo'
+ }
+});
+```
+
+Styling for mobile devices
+--------------------------
+
+This editor should work pretty well with mobile devices, but you'll need to consider the following things when styling it:
+- keyboards on mobile devices take a huge part of the screen
+- having to scroll the screen to touch the toolbar can cause the editing component to lose focus, and the mobile device keyboard might go away
+- mobile devices tend to move the screen viewport around to ensure that the focused element is shown, so it's best that the edit box is glued to the top
+
+For the content attachment editor on MindMup, we apply the following rules to mobile device styling:
+- edit box is glued to the top, so the focus doesn't jump around
+- toolbar is below the edit box
+- on portrait screens, edit box size is 50% of the screen
+- on landscape screens, edit box size is 30% of the screen
+- as the screen gets smaller, non-critical toolbar buttons get hidden into a "other" menu
+
+Dependencies
+------------
+* jQuery http://jquery.com/
+* jQuery HotKeys https://github.com/jeresig/jquery.hotkeys
+* Bootstrap http://twitter.github.com/bootstrap/
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/bootstrap-wysiwyg.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/bootstrap-wysiwyg.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/bootstrap-wysiwyg.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,200 @@
+/* http://github.com/mindmup/bootstrap-wysiwyg */
+/*global jQuery, $, FileReader*/
+/*jslint browser:true*/
+(function ($) {
+ 'use strict';
+ var readFileIntoDataUrl = function (fileInfo) {
+ var loader = $.Deferred(),
+ fReader = new FileReader();
+ fReader.onload = function (e) {
+ loader.resolve(e.target.result);
+ };
+ fReader.onerror = loader.reject;
+ fReader.onprogress = loader.notify;
+ fReader.readAsDataURL(fileInfo);
+ return loader.promise();
+ };
+ $.fn.cleanHtml = function () {
+ var html = $(this).html();
+ return html && html.replace(/(<br>|\s|<div><br><\/div>| )*$/, '');
+ };
+ $.fn.wysiwyg = function (userOptions) {
+ var editor = this,
+ selectedRange,
+ options,
+ toolbarBtnSelector,
+ updateToolbar = function () {
+ if (options.activeToolbarClass) {
+ $(options.toolbarSelector).find(toolbarBtnSelector).each(function () {
+ var command = $(this).data(options.commandRole);
+ if (document.queryCommandState(command)) {
+ $(this).addClass(options.activeToolbarClass);
+ } else {
+ $(this).removeClass(options.activeToolbarClass);
+ }
+ });
+ }
+ },
+ execCommand = function (commandWithArgs, valueArg) {
+ var commandArr = commandWithArgs.split(' '),
+ command = commandArr.shift(),
+ args = commandArr.join(' ') + (valueArg || '');
+ document.execCommand(command, 0, args);
+ updateToolbar();
+ },
+ bindHotkeys = function (hotKeys) {
+ $.each(hotKeys, function (hotkey, command) {
+ editor.keydown(hotkey, function (e) {
+ if (editor.attr('contenteditable') && editor.is(':visible')) {
+ e.preventDefault();
+ e.stopPropagation();
+ execCommand(command);
+ }
+ }).keyup(hotkey, function (e) {
+ if (editor.attr('contenteditable') && editor.is(':visible')) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ });
+ });
+ },
+ getCurrentRange = function () {
+ var sel = window.getSelection();
+ if (sel.getRangeAt && sel.rangeCount) {
+ return sel.getRangeAt(0);
+ }
+ },
+ saveSelection = function () {
+ selectedRange = getCurrentRange();
+ },
+ restoreSelection = function () {
+ var selection = window.getSelection();
+ if (selectedRange) {
+ try {
+ selection.removeAllRanges();
+ } catch (ex) {
+ document.body.createTextRange().select();
+ document.selection.empty();
+ }
+
+ selection.addRange(selectedRange);
+ }
+ },
+ insertFiles = function (files) {
+ editor.focus();
+ $.each(files, function (idx, fileInfo) {
+ if (/^image\//.test(fileInfo.type)) {
+ $.when(readFileIntoDataUrl(fileInfo)).done(function (dataUrl) {
+ execCommand('insertimage', dataUrl);
+ }).fail(function (e) {
+ options.fileUploadError("file-reader", e);
+ });
+ } else {
+ options.fileUploadError("unsupported-file-type", fileInfo.type);
+ }
+ });
+ },
+ markSelection = function (input, color) {
+ restoreSelection();
+ if (document.queryCommandSupported('hiliteColor')) {
+ document.execCommand('hiliteColor', 0, color || 'transparent');
+ }
+ saveSelection();
+ input.data(options.selectionMarker, color);
+ },
+ bindToolbar = function (toolbar, options) {
+ toolbar.find(toolbarBtnSelector).click(function () {
+ restoreSelection();
+ editor.focus();
+ execCommand($(this).data(options.commandRole));
+ saveSelection();
+ });
+ toolbar.find('[data-toggle=dropdown]').click(restoreSelection);
+
+ toolbar.find('input[type=text][data-' + options.commandRole + ']').on('webkitspeechchange change', function () {
+ var newValue = this.value; /* ugly but prevents fake double-calls due to selection restoration */
+ this.value = '';
+ restoreSelection();
+ if (newValue) {
+ editor.focus();
+ execCommand($(this).data(options.commandRole), newValue);
+ }
+ saveSelection();
+ }).on('focus', function () {
+ var input = $(this);
+ if (!input.data(options.selectionMarker)) {
+ markSelection(input, options.selectionColor);
+ input.focus();
+ }
+ }).on('blur', function () {
+ var input = $(this);
+ if (input.data(options.selectionMarker)) {
+ markSelection(input, false);
+ }
+ });
+ toolbar.find('input[type=file][data-' + options.commandRole + ']').change(function () {
+ restoreSelection();
+ if (this.type === 'file' && this.files && this.files.length > 0) {
+ insertFiles(this.files);
+ }
+ saveSelection();
+ this.value = '';
+ });
+ },
+ initFileDrops = function () {
+ editor.on('dragenter dragover', false)
+ .on('drop', function (e) {
+ var dataTransfer = e.originalEvent.dataTransfer;
+ e.stopPropagation();
+ e.preventDefault();
+ if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
+ insertFiles(dataTransfer.files);
+ }
+ });
+ };
+ options = $.extend({}, $.fn.wysiwyg.defaults, userOptions);
+ toolbarBtnSelector = 'a[data-' + options.commandRole + '],button[data-' + options.commandRole + '],input[type=button][data-' + options.commandRole + ']';
+ bindHotkeys(options.hotKeys);
+ if (options.dragAndDropImages) {
+ initFileDrops();
+ }
+ bindToolbar($(options.toolbarSelector), options);
+ editor.attr('contenteditable', true)
+ .on('mouseup keyup mouseout', function () {
+ saveSelection();
+ updateToolbar();
+ });
+ $(window).bind('touchend', function (e) {
+ var isInside = (editor.is(e.target) || editor.has(e.target).length > 0),
+ currentRange = getCurrentRange(),
+ clear = currentRange && (currentRange.startContainer === currentRange.endContainer && currentRange.startOffset === currentRange.endOffset);
+ if (!clear || isInside) {
+ saveSelection();
+ updateToolbar();
+ }
+ });
+ return this;
+ };
+ $.fn.wysiwyg.defaults = {
+ hotKeys: {
+ 'ctrl+b meta+b': 'bold',
+ 'ctrl+i meta+i': 'italic',
+ 'ctrl+u meta+u': 'underline',
+ 'ctrl+z meta+z': 'undo',
+ 'ctrl+y meta+y meta+shift+z': 'redo',
+ 'ctrl+l meta+l': 'justifyleft',
+ 'ctrl+r meta+r': 'justifyright',
+ 'ctrl+e meta+e': 'justifycenter',
+ 'ctrl+j meta+j': 'justifyfull',
+ 'shift+tab': 'outdent',
+ 'tab': 'indent'
+ },
+ toolbarSelector: '[data-role=editor-toolbar]',
+ commandRole: 'edit',
+ activeToolbarClass: 'btn-info',
+ selectionMarker: 'edit-focus-marker',
+ selectionColor: 'darkgrey',
+ dragAndDropImages: true,
+ fileUploadError: function (reason, detail) { console.log("File upload error", reason, detail); }
+ };
+}(window.jQuery));
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-apollo.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-apollo.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-apollo.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["com",/^#[^\n\r]*/,null,"#"],["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,null,'"']],[["kwd",/^(?:ADS|AD|AUG|BZF|BZMF|CAE|CAF|CA|CCS|COM|CS|DAS|DCA|DCOM|DCS|DDOUBL|DIM|DOUBLE|DTCB|DTCF|DV|DXCH|EDRUPT|EXTEND|INCR|INDEX|NDX|INHINT|LXCH|MASK|MSK|MP|MSU|NOOP|OVSK|QXCH|RAND|READ|RELINT|RESUME|RETURN|ROR|RXOR|SQUARE|SU|TCR|TCAA|OVSK|TCF|TC|TS|WAND|WOR|WRITE|XCH|XLQ|XXALQ|ZL|ZQ|ADD|ADZ|SUB|SUZ|MPY|MPR|MPZ|DVP|COM|ABS|CLA|CLZ|LDQ|STO|STQ|ALS|LLS|LRS|TRA|TSQ|TMI|TOV|AXT|TIX|DLY|INP|OUT)\s/,
+null],["typ",/^(?:-?GENADR|=MINUS|2BCADR|VN|BOF|MM|-?2CADR|-?[1-6]DNADR|ADRES|BBCON|[ES]?BANK=?|BLOCK|BNKSUM|E?CADR|COUNT\*?|2?DEC\*?|-?DNCHAN|-?DNPTR|EQUALS|ERASE|MEMORY|2?OCT|REMADR|SETLOC|SUBRO|ORG|BSS|BES|SYN|EQU|DEFINE|END)\s/,null],["lit",/^'(?:-*(?:\w|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?)?/],["pln",/^-*(?:[!-z]|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?/],["pun",/^[^\w\t\n\r "'-);\\\xa0]+/]]),["apollo","agc","aea"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-basic.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-basic.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-basic.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,3 @@
+var a=null;
+PR.registerLangHandler(PR.createSimpleLexer([["str",/^"(?:[^\n\r"\\]|\\.)*(?:"|$)/,a,'"'],["pln",/^\s+/,a," \r\n\t\u00a0"]],[["com",/^REM[^\n\r]*/,a],["kwd",/^\b(?:AND|CLOSE|CLR|CMD|CONT|DATA|DEF ?FN|DIM|END|FOR|GET|GOSUB|GOTO|IF|INPUT|LET|LIST|LOAD|NEW|NEXT|NOT|ON|OPEN|OR|POKE|PRINT|READ|RESTORE|RETURN|RUN|SAVE|STEP|STOP|SYS|THEN|TO|VERIFY|WAIT)\b/,a],["pln",/^[a-z][^\W_]?(?:\$|%)?/i,a],["lit",/^(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?/i,a,"0123456789"],["pun",
+/^.[^\s\w"$%.]*/,a]]),["basic","cbm"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-clj.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-clj.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-clj.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,18 @@
+/*
+ Copyright (C) 2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+var a=null;
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^[([{]+/,a,"([{"],["clo",/^[)\]}]+/,a,")]}"],["com",/^;[^\n\r]*/,a,";"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \u00a0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:def|if|do|let|quote|var|fn|loop|recur|throw|try|monitor-enter|monitor-exit|defmacro|defn|defn-|macroexpand|macroexpand-1|for|doseq|dosync|dotimes|and|or|when|not|assert|doto|proxy|defstruct|first|rest|cons|defprotocol|deftype|defrecord|reify|defmulti|defmethod|meta|with-meta|ns|in-ns|create-ns|import|intern|refer|alias|namespace|resolve|ref|deref|refset|new|set!|memfn|to-array|into-array|aset|gen-class|reduce|map|filter|find|nil?|empty?|hash-map|hash-set|vec|vector|seq|flatten|reverse|assoc|dissoc|list|list?|disj|get|union|difference|intersection|extend|extend-type|extend-protocol|prn)\b/,a],
+["typ",/^:[\dA-Za-z-]+/]]),["clj"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-css.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-css.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-css.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n\u000c"]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]+)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],
+["com",/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}\b/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-dart.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-dart.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-dart.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,3 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"]],[["com",/^#!.*/],["kwd",/^\b(?:import|library|part of|part|as|show|hide)\b/i],["com",/^\/\/.*/],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["kwd",/^\b(?:class|interface)\b/i],["kwd",/^\b(?:assert|break|case|catch|continue|default|do|else|finally|for|if|in|is|new|return|super|switch|this|throw|try|while)\b/i],["kwd",/^\b(?:abstract|const|extends|factory|final|get|implements|native|operator|set|static|typedef|var)\b/i],
+["typ",/^\b(?:bool|double|dynamic|int|num|object|string|void)\b/i],["kwd",/^\b(?:false|null|true)\b/i],["str",/^r?'''[\S\s]*?[^\\]'''/],["str",/^r?"""[\S\s]*?[^\\]"""/],["str",/^r?'('|[^\n\f\r]*?[^\\]')/],["str",/^r?"("|[^\n\f\r]*?[^\\]")/],["pln",/^[$_a-z]\w*/i],["pun",/^[!%&*+/:<-?^|~-]/],["lit",/^\b0x[\da-f]+/i],["lit",/^\b\d+(?:\.\d*)?(?:e[+-]?\d+)?/i],["lit",/^\b\.\d+(?:e[+-]?\d+)?/i],["pun",/^[(),.;[\]{}]/]]),
+["dart"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-erlang.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-erlang.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-erlang.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t-\r ]+/,null,"\t\n\u000b\u000c\r "],["str",/^"(?:[^\n\f\r"\\]|\\[\S\s])*(?:"|$)/,null,'"'],["lit",/^[a-z]\w*/],["lit",/^'(?:[^\n\f\r'\\]|\\[^&])+'?/,null,"'"],["lit",/^\?[^\t\n ({]+/,null,"?"],["lit",/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?)/i,null,"0123456789"]],[["com",/^%[^\n]*/],["kwd",/^(?:module|attributes|do|let|in|letrec|apply|call|primop|case|of|end|when|fun|try|catch|receive|after|char|integer|float,atom,string,var)\b/],
+["kwd",/^-[_a-z]+/],["typ",/^[A-Z_]\w*/],["pun",/^[,.;]/]]),["erlang","erl"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-go.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-go.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-go.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["pln",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])+(?:'|$)|`[^`]*(?:`|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\n\r]*|\/\*[\S\s]*?\*\/)/],["pln",/^(?:[^"'/`]|\/(?![*/]))+/]]),["go"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-hs.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-hs.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-hs.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t-\r ]+/,null,"\t\n\u000b\u000c\r "],["str",/^"(?:[^\n\f\r"\\]|\\[\S\s])*(?:"|$)/,null,'"'],["str",/^'(?:[^\n\f\r'\\]|\\[^&])'?/,null,"'"],["lit",/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?)/i,null,"0123456789"]],[["com",/^(?:--+[^\n\f\r]*|{-(?:[^-]|-+[^}-])*-})/],["kwd",/^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^\d'A-Za-z]|$)/,
+null],["pln",/^(?:[A-Z][\w']*\.)*[A-Za-z][\w']*/],["pun",/^[^\d\t-\r "'A-Za-z]+/]]),["hs"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-lisp.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-lisp.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-lisp.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,3 @@
+var a=null;
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\(+/,a,"("],["clo",/^\)+/,a,")"],["com",/^;[^\n\r]*/,a,";"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \u00a0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/,a],
+["lit",/^[+-]?(?:[#0]x[\da-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[de][+-]?\d+)?)/i],["lit",/^'(?:-*(?:\w|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?)?/],["pln",/^-*(?:[_a-z]|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?/i],["pun",/^[^\w\t\n\r "'-);\\\xa0]+/]]),["cl","el","lisp","lsp","scm","ss","rkt"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-llvm.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-llvm.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-llvm.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["str",/^!?"(?:[^"\\]|\\[\S\s])*(?:"|$)/,null,'"'],["com",/^;[^\n\r]*/,null,";"]],[["pln",/^[!%@](?:[$\-.A-Z_a-z][\w$\-.]*|\d+)/],["kwd",/^[^\W\d]\w*/,null],["lit",/^\d+\.\d+/],["lit",/^(?:\d+|0[Xx][\dA-Fa-f]+)/],["pun",/^[(-*,:<->[\]{}]|\.\.\.$/]]),["llvm","ll"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-lua.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-lua.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-lua.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["str",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$))/,null,"\"'"]],[["com",/^--(?:\[(=*)\[[\S\s]*?(?:]\1]|$)|[^\n\r]*)/],["str",/^\[(=*)\[[\S\s]*?(?:]\1]|$)/],["kwd",/^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,null],["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i],
+["pln",/^[_a-z]\w*/i],["pun",/^[^\w\t\n\r \xa0][^\w\t\n\r "'+=\xa0-]*/]]),["lua"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-matlab.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-matlab.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-matlab.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,6 @@
+var a=null,b=window.PR,c=[[b.PR_PLAIN,/^[\t-\r \xa0]+/,a," \t\r\n\u000b\u000c\u00a0"],[b.PR_COMMENT,/^%{[^%]*%+(?:[^%}][^%]*%+)*}/,a],[b.PR_COMMENT,/^%[^\n\r]*/,a,"%"],["syscmd",/^![^\n\r]*/,a,"!"]],d=[["linecont",/^\.\.\.\s*[\n\r]/,a],["err",/^\?\?\? [^\n\r]*/,a],["wrn",/^Warning: [^\n\r]*/,a],["codeoutput",/^>>\s+/,a],["codeoutput",/^octave:\d+>\s+/,a],["lang-matlab-operators",/^((?:[A-Za-z]\w*(?:\.[A-Za-z]\w*)*|[).\]}])')/,a],["lang-matlab-identifiers",/^([A-Za-z]\w*(?:\.[A-Za-z]\w*)*)(?!')/,a],
+[b.PR_STRING,/^'(?:[^']|'')*'/,a],[b.PR_LITERAL,/^[+-]?\.?\d+(?:\.\d*)?(?:[Ee][+-]?\d+)?[ij]?/,a],[b.PR_TAG,/^[()[\]{}]/,a],[b.PR_PUNCTUATION,/^[!&*-/:->@\\^|~]/,a]],e=[["lang-matlab-identifiers",/^([A-Za-z]\w*(?:\.[A-Za-z]\w*)*)/,a],[b.PR_TAG,/^[()[\]{}]/,a],[b.PR_PUNCTUATION,/^[!&*-/:->@\\^|~]/,a],["transpose",/^'/,a]];
+b.registerLangHandler(b.createSimpleLexer([],[[b.PR_KEYWORD,/^\b(?:break|case|catch|classdef|continue|else|elseif|end|for|function|global|if|otherwise|parfor|persistent|return|spmd|switch|try|while)\b/,a],["const",/^\b(?:true|false|inf|Inf|nan|NaN|eps|pi|ans|nargin|nargout|varargin|varargout)\b/,a],[b.PR_TYPE,/^\b(?:cell|struct|char|double|single|logical|u?int(?:8|16|32|64)|sparse)\b/,a],["fun",/^\b(?:abs|accumarray|acos(?:d|h)?|acot(?:d|h)?|acsc(?:d|h)?|actxcontrol(?:list|select)?|actxGetRunningServer|actxserver|addlistener|addpath|addpref|addtodate|airy|align|alim|all|allchild|alpha|alphamap|amd|ancestor|and|angle|annotation|any|area|arrayfun|asec(?:d|h)?|asin(?:d|h)?|assert|assignin|atan[2dh]?|audiodevinfo|audioplayer|audiorecorder|aufinfo|auread|autumn|auwrite|avifile|aviinfo|aviread|axes|axis|balance|bar(?:3|3h|h)?|base2dec|beep|BeginInvoke|bench|bessel[h-ky]|beta|betainc|betaincinv|betaln|bicg|bicgstab|bicgstabl|bin2dec|bitand|bitcmp|bitget|bitmax|bitnot|bitor|bitset|bitshift|bitxor|blanks|blkdiag|bone|box|brighten|brush|bsxfun|builddocsearchdb|builtin|bvp4c|bvp5c|bvpget|bvpinit|bvpset|bvpxtend|calendar|calllib|callSoapService|camdolly|cameratoolbar|camlight|camlookat|camorbit|campan|campos|camproj|camroll|camtarget|camup|camva|camzoom|cart2pol|cart2sph|cast|cat|caxis|cd|cdf2rdf|cdfepoch|cdfinfo|cdflib(?:.(?:close|closeVar|computeEpoch|computeEpoch16|create|createAttr|createVar|delete|deleteAttr|deleteAttrEntry|deleteAttrgEntry|deleteVar|deleteVarRecords|epoch16Breakdown|epochBreakdown|getAttrEntry|getAttrgEntry|getAttrMaxEntry|getAttrMaxgEntry|getAttrName|getAttrNum|getAttrScope|getCacheSize|getChecksum|getCompression|getCompressionCacheSize|getConstantNames|getConstantValue|getCopyright|getFileBackward|getFormat|getLibraryCopyright|getLibraryVersion|getMajority|getName|getNumAttrEntries|getNumAttrgEntries|getNumAttributes|getNumgAttributes|getReadOnlyMode|getStageCacheSize|getValidate|getVarAllocRecords|getVarBlockingFactor|getVarCacheSize|getVarCompression|getVarData|getVarMaxAllocRecNum|getVarMaxWrittenRecNum|getVarName|getVarNum|getVarNumRecsWritten|getVarPadValue|getVarRecordData|getVarReservePercent|getVarsMaxWrittenRecNum|getVarSparseRecords|getVersion|hyperGetVarData|hyperPutVarData|inquire|inquireAttr|inquireAttrEntry|inquireAttrgEntry|inquireVar|open|putAttrEntry|putAttrgEntry|putVarData|putVarRecordData|renameAttr|renameVar|setCacheSize|setChecksum|setCompression|setCompressionCacheSize|setFileBackward|setFormat|setMajority|setReadOnlyMode|setStageCacheSize|setValidate|setVarAllocBlockRecords|setVarBlockingFactor|setVarCacheSize|setVarCompression|setVarInitialRecs|setVarPadValue|SetVarReservePercent|setVarsCacheSize|setVarSparseRecords))?|cdfread|cdfwrite|ceil|cell2mat|cell2struct|celldisp|cellfun|cellplot|cellstr|cgs|checkcode|checkin|checkout|chol|cholinc|cholupdate|circshift|cla|clabel|class|clc|clear|clearvars|clf|clipboard|clock|close|closereq|cmopts|cmpermute|cmunique|colamd|colon|colorbar|colordef|colormap|colormapeditor|colperm|Combine|comet|comet3|commandhistory|commandwindow|compan|compass|complex|computer|cond|condeig|condest|coneplot|conj|containers.Map|contour(?:[3cf]|slice)?|contrast|conv|conv2|convhull|convhulln|convn|cool|copper|copyfile|copyobj|corrcoef|cos(?:d|h)?|cot(?:d|h)?|cov|cplxpair|cputime|createClassFromWsdl|createSoapMessage|cross|csc(?:d|h)?|csvread|csvwrite|ctranspose|cumprod|cumsum|cumtrapz|curl|customverctrl|cylinder|daqread|daspect|datacursormode|datatipinfo|date|datenum|datestr|datetick|datevec|dbclear|dbcont|dbdown|dblquad|dbmex|dbquit|dbstack|dbstatus|dbstep|dbstop|dbtype|dbup|dde23|ddeget|ddesd|ddeset|deal|deblank|dec2base|dec2bin|dec2hex|decic|deconv|del2|delaunay|delaunay3|delaunayn|DelaunayTri|delete|demo|depdir|depfun|det|detrend|deval|diag|dialog|diary|diff|diffuse|dir|disp|display|dither|divergence|dlmread|dlmwrite|dmperm|doc|docsearch|dos|dot|dragrect|drawnow|dsearch|dsearchn|dynamicprops|echo|echodemo|edit|eig|eigs|ellipj|ellipke|ellipsoid|empty|enableNETfromNetworkDrive|enableservice|EndInvoke|enumeration|eomday|eq|erf|erfc|erfcinv|erfcx|erfinv|error|errorbar|errordlg|etime|etree|etreeplot|eval|evalc|evalin|event.(?:EventData|listener|PropertyEvent|proplistener)|exifread|exist|exit|exp|expint|expm|expm1|export2wsdlg|eye|ezcontour|ezcontourf|ezmesh|ezmeshc|ezplot|ezplot3|ezpolar|ezsurf|ezsurfc|factor|factorial|fclose|feather|feature|feof|ferror|feval|fft|fft2|fftn|fftshift|fftw|fgetl|fgets|fieldnames|figure|figurepalette|fileattrib|filebrowser|filemarker|fileparts|fileread|filesep|fill|fill3|filter|filter2|find|findall|findfigs|findobj|findstr|finish|fitsdisp|fitsinfo|fitsread|fitswrite|fix|flag|flipdim|fliplr|flipud|floor|flow|fminbnd|fminsearch|fopen|format|fplot|fprintf|frame2im|fread|freqspace|frewind|fscanf|fseek|ftell|FTP|full|fullfile|func2str|functions|funm|fwrite|fzero|gallery|gamma|gammainc|gammaincinv|gammaln|gca|gcbf|gcbo|gcd|gcf|gco|ge|genpath|genvarname|get|getappdata|getenv|getfield|getframe|getpixelposition|getpref|ginput|gmres|gplot|grabcode|gradient|gray|graymon|grid|griddata(?:3|n)?|griddedInterpolant|gsvd|gt|gtext|guidata|guide|guihandles|gunzip|gzip|h5create|h5disp|h5info|h5read|h5readatt|h5write|h5writeatt|hadamard|handle|hankel|hdf|hdf5|hdf5info|hdf5read|hdf5write|hdfinfo|hdfread|hdftool|help|helpbrowser|helpdesk|helpdlg|helpwin|hess|hex2dec|hex2num|hgexport|hggroup|hgload|hgsave|hgsetget|hgtransform|hidden|hilb|hist|histc|hold|home|horzcat|hostid|hot|hsv|hsv2rgb|hypot|ichol|idivide|ifft|ifft2|ifftn|ifftshift|ilu|im2frame|im2java|imag|image|imagesc|imapprox|imfinfo|imformats|import|importdata|imread|imwrite|ind2rgb|ind2sub|inferiorto|info|inline|inmem|inpolygon|input|inputdlg|inputname|inputParser|inspect|instrcallback|instrfind|instrfindall|int2str|integral(?:2|3)?|interp(?:1|1q|2|3|ft|n)|interpstreamspeed|intersect|intmax|intmin|inv|invhilb|ipermute|isa|isappdata|iscell|iscellstr|ischar|iscolumn|isdir|isempty|isequal|isequaln|isequalwithequalnans|isfield|isfinite|isfloat|isglobal|ishandle|ishghandle|ishold|isinf|isinteger|isjava|iskeyword|isletter|islogical|ismac|ismatrix|ismember|ismethod|isnan|isnumeric|isobject|isocaps|isocolors|isonormals|isosurface|ispc|ispref|isprime|isprop|isreal|isrow|isscalar|issorted|isspace|issparse|isstr|isstrprop|isstruct|isstudent|isunix|isvarname|isvector|javaaddpath|javaArray|javachk|javaclasspath|javacomponent|javaMethod|javaMethodEDT|javaObject|javaObjectEDT|javarmpath|jet|keyboard|kron|lasterr|lasterror|lastwarn|lcm|ldivide|ldl|le|legend|legendre|length|libfunctions|libfunctionsview|libisloaded|libpointer|libstruct|license|light|lightangle|lighting|lin2mu|line|lines|linkaxes|linkdata|linkprop|linsolve|linspace|listdlg|listfonts|load|loadlibrary|loadobj|log|log10|log1p|log2|loglog|logm|logspace|lookfor|lower|ls|lscov|lsqnonneg|lsqr|lt|lu|luinc|magic|makehgtform|mat2cell|mat2str|material|matfile|matlab.io.MatFile|matlab.mixin.(?:Copyable|Heterogeneous(?:.getDefaultScalarElement)?)|matlabrc|matlabroot|max|maxNumCompThreads|mean|median|membrane|memmapfile|memory|menu|mesh|meshc|meshgrid|meshz|meta.(?:class(?:.fromName)?|DynamicProperty|EnumeratedValue|event|MetaData|method|package(?:.(?:fromName|getAllPackages))?|property)|metaclass|methods|methodsview|mex(?:.getCompilerConfigurations)?|MException|mexext|mfilename|min|minres|minus|mislocked|mkdir|mkpp|mldivide|mlint|mlintrpt|mlock|mmfileinfo|mmreader|mod|mode|more|move|movefile|movegui|movie|movie2avi|mpower|mrdivide|msgbox|mtimes|mu2lin|multibandread|multibandwrite|munlock|namelengthmax|nargchk|narginchk|nargoutchk|native2unicode|nccreate|ncdisp|nchoosek|ncinfo|ncread|ncreadatt|ncwrite|ncwriteatt|ncwriteschema|ndgrid|ndims|ne|NET(?:.(?:addAssembly|Assembly|convertArray|createArray|createGeneric|disableAutoRelease|enableAutoRelease|GenericClass|invokeGenericMethod|NetException|setStaticProperty))?|netcdf.(?:abort|close|copyAtt|create|defDim|defGrp|defVar|defVarChunking|defVarDeflate|defVarFill|defVarFletcher32|delAtt|endDef|getAtt|getChunkCache|getConstant|getConstantNames|getVar|inq|inqAtt|inqAttID|inqAttName|inqDim|inqDimID|inqDimIDs|inqFormat|inqGrpName|inqGrpNameFull|inqGrpParent|inqGrps|inqLibVers|inqNcid|inqUnlimDims|inqVar|inqVarChunking|inqVarDeflate|inqVarFill|inqVarFletcher32|inqVarID|inqVarIDs|open|putAtt|putVar|reDef|renameAtt|renameDim|renameVar|setChunkCache|setDefaultFormat|setFill|sync)|newplot|nextpow2|nnz|noanimate|nonzeros|norm|normest|not|notebook|now|nthroot|null|num2cell|num2hex|num2str|numel|nzmax|ode(?:113|15i|15s|23|23s|23t|23tb|45)|odeget|odeset|odextend|onCleanup|ones|open|openfig|opengl|openvar|optimget|optimset|or|ordeig|orderfields|ordqz|ordschur|orient|orth|pack|padecoef|pagesetupdlg|pan|pareto|parseSoapResponse|pascal|patch|path|path2rc|pathsep|pathtool|pause|pbaspect|pcg|pchip|pcode|pcolor|pdepe|pdeval|peaks|perl|perms|permute|pie|pink|pinv|planerot|playshow|plot|plot3|plotbrowser|plotedit|plotmatrix|plottools|plotyy|plus|pol2cart|polar|poly|polyarea|polyder|polyeig|polyfit|polyint|polyval|polyvalm|pow2|power|ppval|prefdir|preferences|primes|print|printdlg|printopt|printpreview|prod|profile|profsave|propedit|propertyeditor|psi|publish|PutCharArray|PutFullMatrix|PutWorkspaceData|pwd|qhull|qmr|qr|qrdelete|qrinsert|qrupdate|quad|quad2d|quadgk|quadl|quadv|questdlg|quit|quiver|quiver3|qz|rand|randi|randn|randperm|RandStream(?:.(?:create|getDefaultStream|getGlobalStream|list|setDefaultStream|setGlobalStream))?|rank|rat|rats|rbbox|rcond|rdivide|readasync|real|reallog|realmax|realmin|realpow|realsqrt|record|rectangle|rectint|recycle|reducepatch|reducevolume|refresh|refreshdata|regexp|regexpi|regexprep|regexptranslate|rehash|rem|Remove|RemoveAll|repmat|reset|reshape|residue|restoredefaultpath|rethrow|rgb2hsv|rgb2ind|rgbplot|ribbon|rmappdata|rmdir|rmfield|rmpath|rmpref|rng|roots|rose|rosser|rot90|rotate|rotate3d|round|rref|rsf2csf|run|save|saveas|saveobj|savepath|scatter|scatter3|schur|sec|secd|sech|selectmoveresize|semilogx|semilogy|sendmail|serial|set|setappdata|setdiff|setenv|setfield|setpixelposition|setpref|setstr|setxor|shading|shg|shiftdim|showplottool|shrinkfaces|sign|sin(?:d|h)?|size|slice|smooth3|snapnow|sort|sortrows|sound|soundsc|spalloc|spaugment|spconvert|spdiags|specular|speye|spfun|sph2cart|sphere|spinmap|spline|spones|spparms|sprand|sprandn|sprandsym|sprank|spring|sprintf|spy|sqrt|sqrtm|squeeze|ss2tf|sscanf|stairs|startup|std|stem|stem3|stopasync|str2double|str2func|str2mat|str2num|strcat|strcmp|strcmpi|stream2|stream3|streamline|streamparticles|streamribbon|streamslice|streamtube|strfind|strjust|strmatch|strncmp|strncmpi|strread|strrep|strtok|strtrim|struct2cell|structfun|strvcat|sub2ind|subplot|subsasgn|subsindex|subspace|subsref|substruct|subvolume|sum|summer|superclasses|superiorto|support|surf|surf2patch|surface|surfc|surfl|surfnorm|svd|svds|swapbytes|symamd|symbfact|symmlq|symrcm|symvar|system|tan(?:d|h)?|tar|tempdir|tempname|tetramesh|texlabel|text|textread|textscan|textwrap|tfqmr|throw|tic|Tiff(?:.(?:getTagNames|getVersion))?|timer|timerfind|timerfindall|times|timeseries|title|toc|todatenum|toeplitz|toolboxdir|trace|transpose|trapz|treelayout|treeplot|tril|trimesh|triplequad|triplot|TriRep|TriScatteredInterp|trisurf|triu|tscollection|tsearch|tsearchn|tstool|type|typecast|uibuttongroup|uicontextmenu|uicontrol|uigetdir|uigetfile|uigetpref|uiimport|uimenu|uiopen|uipanel|uipushtool|uiputfile|uiresume|uisave|uisetcolor|uisetfont|uisetpref|uistack|uitable|uitoggletool|uitoolbar|uiwait|uminus|undocheckout|unicode2native|union|unique|unix|unloadlibrary|unmesh|unmkpp|untar|unwrap|unzip|uplus|upper|urlread|urlwrite|usejava|userpath|validateattributes|validatestring|vander|var|vectorize|ver|verctrl|verLessThan|version|vertcat|VideoReader(?:.isPlatformSupported)?|VideoWriter(?:.getProfiles)?|view|viewmtx|visdiff|volumebounds|voronoi|voronoin|wait|waitbar|waitfor|waitforbuttonpress|warndlg|warning|waterfall|wavfinfo|wavplay|wavread|wavrecord|wavwrite|web|weekday|what|whatsnew|which|whitebg|who|whos|wilkinson|winopen|winqueryreg|winter|wk1finfo|wk1read|wk1write|workspace|xlabel|xlim|xlsfinfo|xlsread|xlswrite|xmlread|xmlwrite|xor|xslt|ylabel|ylim|zeros|zip|zlabel|zlim|zoom)\b/,
+a],["fun_tbx",/^\b(?:addedvarplot|andrewsplot|anova[12n]|ansaribradley|aoctool|barttest|bbdesign|beta(?:cdf|fit|inv|like|pdf|rnd|stat)|bino(?:cdf|fit|inv|pdf|rnd|stat)|biplot|bootci|bootstrp|boxplot|candexch|candgen|canoncorr|capability|capaplot|caseread|casewrite|categorical|ccdesign|cdfplot|chi2(?:cdf|gof|inv|pdf|rnd|stat)|cholcov|Classification(?:BaggedEnsemble|Discriminant(?:.(?:fit|make|template))?|Ensemble|KNN(?:.(?:fit|template))?|PartitionedEnsemble|PartitionedModel|Tree(?:.(?:fit|template))?)|classify|classregtree|cluster|clusterdata|cmdscale|combnk|Compact(?:Classification(?:Discriminant|Ensemble|Tree)|Regression(?:Ensemble|Tree)|TreeBagger)|confusionmat|controlchart|controlrules|cophenet|copula(?:cdf|fit|param|pdf|rnd|stat)|cordexch|corr|corrcov|coxphfit|createns|crosstab|crossval|cvpartition|datasample|dataset|daugment|dcovary|dendrogram|dfittool|disttool|dummyvar|dwtest|ecdf|ecdfhist|ev(?:cdf|fit|inv|like|pdf|rnd|stat)|ExhaustiveSearcher|exp(?:cdf|fit|inv|like|pdf|rnd|stat)|factoran|fcdf|ff2n|finv|fitdist|fitensemble|fpdf|fracfact|fracfactgen|friedman|frnd|fstat|fsurfht|fullfact|gagerr|gam(?:cdf|fit|inv|like|pdf|rnd|stat)|GeneralizedLinearModel(?:.fit)?|geo(?:cdf|inv|mean|pdf|rnd|stat)|gev(?:cdf|fit|inv|like|pdf|rnd|stat)|gline|glmfit|glmval|glyphplot|gmdistribution(?:.fit)?|gname|gp(?:cdf|fit|inv|like|pdf|rnd|stat)|gplotmatrix|grp2idx|grpstats|gscatter|haltonset|harmmean|hist3|histfit|hmm(?:decode|estimate|generate|train|viterbi)|hougen|hyge(?:cdf|inv|pdf|rnd|stat)|icdf|inconsistent|interactionplot|invpred|iqr|iwishrnd|jackknife|jbtest|johnsrnd|KDTreeSearcher|kmeans|knnsearch|kruskalwallis|ksdensity|kstest|kstest2|kurtosis|lasso|lassoglm|lassoPlot|leverage|lhsdesign|lhsnorm|lillietest|LinearModel(?:.fit)?|linhyptest|linkage|logn(?:cdf|fit|inv|like|pdf|rnd|stat)|lsline|mad|mahal|maineffectsplot|manova1|manovacluster|mdscale|mhsample|mle|mlecov|mnpdf|mnrfit|mnrnd|mnrval|moment|multcompare|multivarichart|mvn(?:cdf|pdf|rnd)|mvregress|mvregresslike|mvt(?:cdf|pdf|rnd)|NaiveBayes(?:.fit)?|nan(?:cov|max|mean|median|min|std|sum|var)|nbin(?:cdf|fit|inv|pdf|rnd|stat)|ncf(?:cdf|inv|pdf|rnd|stat)|nct(?:cdf|inv|pdf|rnd|stat)|ncx2(?:cdf|inv|pdf|rnd|stat)|NeighborSearcher|nlinfit|nlintool|nlmefit|nlmefitsa|nlparci|nlpredci|nnmf|nominal|NonLinearModel(?:.fit)?|norm(?:cdf|fit|inv|like|pdf|rnd|stat)|normplot|normspec|ordinal|outlierMeasure|parallelcoords|paretotails|partialcorr|pcacov|pcares|pdf|pdist|pdist2|pearsrnd|perfcurve|perms|piecewisedistribution|plsregress|poiss(?:cdf|fit|inv|pdf|rnd|tat)|polyconf|polytool|prctile|princomp|ProbDist(?:Kernel|Parametric|UnivKernel|UnivParam)?|probplot|procrustes|qqplot|qrandset|qrandstream|quantile|randg|random|randsample|randtool|range|rangesearch|ranksum|rayl(?:cdf|fit|inv|pdf|rnd|stat)|rcoplot|refcurve|refline|regress|Regression(?:BaggedEnsemble|Ensemble|PartitionedEnsemble|PartitionedModel|Tree(?:.(?:fit|template))?)|regstats|relieff|ridge|robustdemo|robustfit|rotatefactors|rowexch|rsmdemo|rstool|runstest|sampsizepwr|scatterhist|sequentialfs|signrank|signtest|silhouette|skewness|slicesample|sobolset|squareform|statget|statset|stepwise|stepwisefit|surfht|tabulate|tblread|tblwrite|tcdf|tdfread|tiedrank|tinv|tpdf|TreeBagger|treedisp|treefit|treeprune|treetest|treeval|trimmean|trnd|tstat|ttest|ttest2|unid(?:cdf|inv|pdf|rnd|stat)|unif(?:cdf|inv|it|pdf|rnd|stat)|vartest(?:2|n)?|wbl(?:cdf|fit|inv|like|pdf|rnd|stat)|wblplot|wishrnd|x2fx|xptread|zscore|ztest)\b/,
+a],["fun_tbx",/^\b(?:adapthisteq|analyze75info|analyze75read|applycform|applylut|axes2pix|bestblk|blockproc|bwarea|bwareaopen|bwboundaries|bwconncomp|bwconvhull|bwdist|bwdistgeodesic|bweuler|bwhitmiss|bwlabel|bwlabeln|bwmorph|bwpack|bwperim|bwselect|bwtraceboundary|bwulterode|bwunpack|checkerboard|col2im|colfilt|conndef|convmtx2|corner|cornermetric|corr2|cp2tform|cpcorr|cpselect|cpstruct2pairs|dct2|dctmtx|deconvblind|deconvlucy|deconvreg|deconvwnr|decorrstretch|demosaic|dicom(?:anon|dict|info|lookup|read|uid|write)|edge|edgetaper|entropy|entropyfilt|fan2para|fanbeam|findbounds|fliptform|freqz2|fsamp2|fspecial|ftrans2|fwind1|fwind2|getheight|getimage|getimagemodel|getline|getneighbors|getnhood|getpts|getrangefromclass|getrect|getsequence|gray2ind|graycomatrix|graycoprops|graydist|grayslice|graythresh|hdrread|hdrwrite|histeq|hough|houghlines|houghpeaks|iccfind|iccread|iccroot|iccwrite|idct2|ifanbeam|im2bw|im2col|im2double|im2int16|im2java2d|im2single|im2uint16|im2uint8|imabsdiff|imadd|imadjust|ImageAdapter|imageinfo|imagemodel|imapplymatrix|imattributes|imbothat|imclearborder|imclose|imcolormaptool|imcomplement|imcontour|imcontrast|imcrop|imdilate|imdisplayrange|imdistline|imdivide|imellipse|imerode|imextendedmax|imextendedmin|imfill|imfilter|imfindcircles|imfreehand|imfuse|imgca|imgcf|imgetfile|imhandles|imhist|imhmax|imhmin|imimposemin|imlincomb|imline|immagbox|immovie|immultiply|imnoise|imopen|imoverview|imoverviewpanel|impixel|impixelinfo|impixelinfoval|impixelregion|impixelregionpanel|implay|impoint|impoly|impositionrect|improfile|imputfile|impyramid|imreconstruct|imrect|imregconfig|imregionalmax|imregionalmin|imregister|imresize|imroi|imrotate|imsave|imscrollpanel|imshow|imshowpair|imsubtract|imtool|imtophat|imtransform|imview|ind2gray|ind2rgb|interfileinfo|interfileread|intlut|ippl|iptaddcallback|iptcheckconn|iptcheckhandle|iptcheckinput|iptcheckmap|iptchecknargin|iptcheckstrs|iptdemos|iptgetapi|iptGetPointerBehavior|iptgetpref|ipticondir|iptnum2ordinal|iptPointerManager|iptprefs|iptremovecallback|iptSetPointerBehavior|iptsetpref|iptwindowalign|iradon|isbw|isflat|isgray|isicc|isind|isnitf|isrgb|isrset|lab2double|lab2uint16|lab2uint8|label2rgb|labelmatrix|makecform|makeConstrainToRectFcn|makehdr|makelut|makeresampler|maketform|mat2gray|mean2|medfilt2|montage|nitfinfo|nitfread|nlfilter|normxcorr2|ntsc2rgb|openrset|ordfilt2|otf2psf|padarray|para2fan|phantom|poly2mask|psf2otf|qtdecomp|qtgetblk|qtsetblk|radon|rangefilt|reflect|regionprops|registration.metric.(?:MattesMutualInformation|MeanSquares)|registration.optimizer.(?:OnePlusOneEvolutionary|RegularStepGradientDescent)|rgb2gray|rgb2ntsc|rgb2ycbcr|roicolor|roifill|roifilt2|roipoly|rsetwrite|std2|stdfilt|strel|stretchlim|subimage|tformarray|tformfwd|tforminv|tonemap|translate|truesize|uintlut|viscircles|warp|watershed|whitepoint|wiener2|xyz2double|xyz2uint16|ycbcr2rgb)\b/,
+a],["fun_tbx",/^\b(?:bintprog|color|fgoalattain|fminbnd|fmincon|fminimax|fminsearch|fminunc|fseminf|fsolve|fzero|fzmult|gangstr|ktrlink|linprog|lsqcurvefit|lsqlin|lsqnonlin|lsqnonneg|optimget|optimset|optimtool|quadprog)\b/,a],["ident",/^[A-Za-z]\w*(?:\.[A-Za-z]\w*)*/,a]]),["matlab-identifiers"]);b.registerLangHandler(b.createSimpleLexer([],e),["matlab-operators"]);b.registerLangHandler(b.createSimpleLexer(c,d),["matlab"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-ml.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-ml.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-ml.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["com",/^#(?:if[\t\n\r \xa0]+(?:[$_a-z][\w']*|``[^\t\n\r`]*(?:``|$))|else|endif|light)/i,null,"#"],["str",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])(?:'|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\n\r]*|\(\*[\S\s]*?\*\))/],["kwd",/^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\b/],
+["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i],["pln",/^(?:[_a-z][\w']*[!#?]?|``[^\t\n\r`]*(?:``|$))/i],["pun",/^[^\w\t\n\r "'\xa0]+/]]),["fs","ml"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-mumps.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-mumps.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-mumps.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["str",/^"(?:[^"]|\\.)*"/,null,'"']],[["com",/^;[^\n\r]*/,null,";"],["dec",/^\$(?:d|device|ec|ecode|es|estack|et|etrap|h|horolog|i|io|j|job|k|key|p|principal|q|quit|st|stack|s|storage|sy|system|t|test|tl|tlevel|tr|trestart|x|y|z[a-z]*|a|ascii|c|char|d|data|e|extract|f|find|fn|fnumber|g|get|j|justify|l|length|na|name|o|order|p|piece|ql|qlength|qs|qsubscript|q|query|r|random|re|reverse|s|select|st|stack|t|text|tr|translate|nan)\b/i,
+null],["kwd",/^(?:[^$]b|break|c|close|d|do|e|else|f|for|g|goto|h|halt|h|hang|i|if|j|job|k|kill|l|lock|m|merge|n|new|o|open|q|quit|r|read|s|set|tc|tcommit|tre|trestart|tro|trollback|ts|tstart|u|use|v|view|w|write|x|xecute)\b/i,null],["lit",/^[+-]?(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?/i],["pln",/^[a-z][^\W_]*/i],["pun",/^[^\w\t\n\r"$%;^\xa0]|_/]]),["mumps"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-n.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-n.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-n.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,4 @@
+var a=null;
+PR.registerLangHandler(PR.createSimpleLexer([["str",/^(?:'(?:[^\n\r'\\]|\\.)*'|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,a,'"'],["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,a,"#"],["pln",/^\s+/,a," \r\n\t\u00a0"]],[["str",/^@"(?:[^"]|"")*(?:"|$)/,a],["str",/^<#[^#>]*(?:#>|$)/,a],["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,a],["com",/^\/\/[^\n\r]*/,a],["com",/^\/\*[\S\s]*?(?:\*\/|$)/,
+a],["kwd",/^(?:abstract|and|as|base|catch|class|def|delegate|enum|event|extern|false|finally|fun|implements|interface|internal|is|macro|match|matches|module|mutable|namespace|new|null|out|override|params|partial|private|protected|public|ref|sealed|static|struct|syntax|this|throw|true|try|type|typeof|using|variant|virtual|volatile|when|where|with|assert|assert2|async|break|checked|continue|do|else|ensures|for|foreach|if|late|lock|new|nolate|otherwise|regexp|repeat|requires|return|surroundwith|unchecked|unless|using|while|yield)\b/,
+a],["typ",/^(?:array|bool|byte|char|decimal|double|float|int|list|long|object|sbyte|short|string|ulong|uint|ufloat|ulong|ushort|void)\b/,a],["lit",/^@[$_a-z][\w$@]*/i,a],["typ",/^@[A-Z]+[a-z][\w$@]*/,a],["pln",/^'?[$_a-z][\w$@]*/i,a],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,a,"0123456789"],["pun",/^.[^\s\w"-$'./@`]*/,a]]),["n","nemerle"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-pascal.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-pascal.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-pascal.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,3 @@
+var a=null;
+PR.registerLangHandler(PR.createSimpleLexer([["str",/^'(?:[^\n\r'\\]|\\.)*(?:'|$)/,a,"'"],["pln",/^\s+/,a," \r\n\t\u00a0"]],[["com",/^\(\*[\S\s]*?(?:\*\)|$)|^{[\S\s]*?(?:}|$)/,a],["kwd",/^(?:absolute|and|array|asm|assembler|begin|case|const|constructor|destructor|div|do|downto|else|end|external|for|forward|function|goto|if|implementation|in|inline|interface|interrupt|label|mod|not|object|of|or|packed|procedure|program|record|repeat|set|shl|shr|then|to|type|unit|until|uses|var|virtual|while|with|xor)\b/i,a],
+["lit",/^(?:true|false|self|nil)/i,a],["pln",/^[a-z][^\W_]*/i,a],["lit",/^(?:\$[\da-f]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?)/i,a,"0123456789"],["pun",/^.[^\s\w$'./@]*/,a]]),["pascal"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-proto.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-proto.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-proto.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1 @@
+PR.registerLangHandler(PR.sourceDecorator({keywords:"bytes,default,double,enum,extend,extensions,false,group,import,max,message,option,optional,package,repeated,required,returns,rpc,service,syntax,to,true",types:/^(bool|(double|s?fixed|[su]?int)(32|64)|float|string)\b/,cStyleComments:!0}),["proto"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-r.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-r.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-r.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,null,'"'],["str",/^'(?:[^'\\]|\\[\S\s])*(?:'|$)/,null,"'"]],[["com",/^#.*/],["kwd",/^(?:if|else|for|while|repeat|in|next|break|return|switch|function)(?![\w.])/],["lit",/^0[Xx][\dA-Fa-f]+([Pp]\d+)?[Li]?/],["lit",/^[+-]?(\d+(\.\d+)?|\.\d+)([Ee][+-]?\d+)?[Li]?/],["lit",/^(?:NULL|NA(?:_(?:integer|real|complex|character)_)?|Inf|TRUE|FALSE|NaN|\.\.(?:\.|\d+))(?![\w.])/],
+["pun",/^(?:<<?-|->>?|-|==|<=|>=|<|>|&&?|!=|\|\|?|[!*+/^]|%.*?%|[$=@~]|:{1,3}|[(),;?[\]{}])/],["pln",/^(?:[A-Za-z]+[\w.]*|\.[^\W\d][\w.]*)(?![\w.])/],["str",/^`.+`/]]),["r","s","R","S","Splus"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-rd.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-rd.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-rd.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["com",/^%[^\n\r]*/,null,"%"]],[["lit",/^\\(?:cr|l?dots|R|tab)\b/],["kwd",/^\\[@-Za-z]+/],["kwd",/^#(?:ifn?def|endif)/],["pln",/^\\[{}]/],["pun",/^[()[\]{}]+/]]),["Rd","rd"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-scala.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-scala.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-scala.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["str",/^"(?:""(?:""?(?!")|[^"\\]|\\.)*"{0,3}|(?:[^\n\r"\\]|\\.)*"?)/,null,'"'],["lit",/^`(?:[^\n\r\\`]|\\.)*`?/,null,"`"],["pun",/^[!#%&(--:-@[-^{-~]+/,null,"!#%&()*+,-:;<=>?@[\\]^{|}~"]],[["str",/^'(?:[^\n\r'\\]|\\(?:'|[^\n\r']+))'/],["lit",/^'[$A-Z_a-z][\w$]*(?![\w$'])/],["kwd",/^(?:abstract|case|catch|class|def|do|else|extends|final|finally|for|forSome|if|implicit|import|lazy|match|new|object|override|package|private|protected|requires|return|sealed|super|throw|trait|try|type|val|var|while|with|yield)\b/],
+["lit",/^(?:true|false|null|this)\b/],["lit",/^(?:0(?:[0-7]+|x[\da-f]+)l?|(?:0|[1-9]\d*)(?:(?:\.\d+)?(?:e[+-]?\d+)?f?|l?)|\\.\d+(?:e[+-]?\d+)?f?)/i],["typ",/^[$_]*[A-Z][\d$A-Z_]*[a-z][\w$]*/],["pln",/^[$A-Z_a-z][\w$]*/],["com",/^\/(?:\/.*|\*(?:\/|\**[^*/])*(?:\*+\/?)?)/],["pun",/^(?:\.+|\/)/]]),["scala"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-sql.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-sql.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-sql.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["str",/^(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/,null,"\"'"]],[["com",/^(?:--[^\n\r]*|\/\*[\S\s]*?(?:\*\/|$))/],["kwd",/^(?:add|all|alter|and|any|apply|as|asc|authorization|backup|begin|between|break|browse|bulk|by|cascade|case|check|checkpoint|close|clustered|coalesce|collate|column|commit|compute|connect|constraint|contains|containstable|continue|convert|create|cross|current|current_date|current_time|current_timestamp|current_user|cursor|database|dbcc|deallocate|declare|default|delete|deny|desc|disk|distinct|distributed|double|drop|dummy|dump|else|end|errlvl|escape|except|exec|execute|exists|exit|fetch|file|fillfactor|following|for|foreign|freetext|freetexttable|from|full|function|goto|grant|group|having|holdlock|identity|identitycol|identity_insert|if|in|index|inner|insert|intersect|into|is|join|key|kill|left|like|lineno|load|match|matched|merge|natural|national|nocheck|nonclustered|nocycle|not|null|nullif|of|off|offsets|on|open|opendatasource|openquery|openrowset|openxml|option|or|order|outer|over|partition|percent|pivot|plan|preceding|precision|primary|print|proc|procedure|public|raiserror|read|readtext|reconfigure|references|replication|restore|restrict|return|revoke|right|rollback|rowcount|rowguidcol|rows?|rule|save|schema|select|session_user|set|setuser|shutdown|some|start|statistics|system_user|table|textsize|then|to|top|tran|transaction|trigger|truncate|tsequal|unbounded|union|unique|unpivot|update|updatetext|use|user|using|values|varying|view|waitfor|when|where|while|with|within|writetext|xml)(?=[^\w-]|$)/i,
+null],["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i],["pln",/^[_a-z][\w-]*/i],["pun",/^[^\w\t\n\r "'\xa0][^\w\t\n\r "'+\xa0-]*/]]),["sql"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-tcl.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-tcl.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-tcl.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,3 @@
+var a=null;
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^{+/,a,"{"],["clo",/^}+/,a,"}"],["com",/^#[^\n\r]*/,a,"#"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \u00a0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:after|append|apply|array|break|case|catch|continue|error|eval|exec|exit|expr|for|foreach|if|incr|info|proc|return|set|switch|trace|uplevel|upvar|while)\b/,a],["lit",/^[+-]?(?:[#0]x[\da-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[de][+-]?\d+)?)/i],["lit",
+/^'(?:-*(?:\w|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?)?/],["pln",/^-*(?:[_a-z]|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?/i],["pun",/^[^\w\t\n\r "'-);\\\xa0]+/]]),["tcl"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-tex.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-tex.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-tex.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["com",/^%[^\n\r]*/,null,"%"]],[["kwd",/^\\[@-Za-z]+/],["kwd",/^\\./],["typ",/^[$&]/],["lit",/[+-]?(?:\.\d+|\d+(?:\.\d*)?)(cm|em|ex|in|pc|pt|bp|mm)/i],["pun",/^[()=[\]{}]+/]]),["latex","tex"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-vb.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-vb.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-vb.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0\u2028\u2029]+/,null,"\t\n\r \u00a0\u2028\u2029"],["str",/^(?:["\u201c\u201d](?:[^"\u201c\u201d]|["\u201c\u201d]{2})(?:["\u201c\u201d]c|$)|["\u201c\u201d](?:[^"\u201c\u201d]|["\u201c\u201d]{2})*(?:["\u201c\u201d]|$))/i,null,'"\u201c\u201d'],["com",/^['\u2018\u2019](?:_(?:\r\n?|[^\r]?)|[^\n\r_\u2028\u2029])*/,null,"'\u2018\u2019"]],[["kwd",/^(?:addhandler|addressof|alias|and|andalso|ansi|as|assembly|auto|boolean|byref|byte|byval|call|case|catch|cbool|cbyte|cchar|cdate|cdbl|cdec|char|cint|class|clng|cobj|const|cshort|csng|cstr|ctype|date|decimal|declare|default|delegate|dim|directcast|do|double|each|else|elseif|end|endif|enum|erase|error|event|exit|finally|for|friend|function|get|gettype|gosub|goto|handles|if|implements|imports|in|inherits|integer|interface|is|let|lib|like|long|loop|me|mod|module|mustinherit|mustoverride|mybase|myclass|namespace|new|next|not|notinheritable|notoverridable|object|on|option|optional|or|orelse|overloads|overridable|overrides|paramarray|preserve|private|property|protected|public|raiseevent|readonly|redim|removehandler|resume|return|select|set|shadows|shared|short|single|static|step|stop|string|structure|sub|synclock|then|throw|to|try|typeof|unicode|until|variant|wend|when|while|with|withevents|writeonly|xor|endif|gosub|let|variant|wend)\b/i,
+null],["com",/^rem\b.*/i],["lit",/^(?:true\b|false\b|nothing\b|\d+(?:e[+-]?\d+[dfr]?|[dfilrs])?|(?:&h[\da-f]+|&o[0-7]+)[ils]?|\d*\.\d+(?:e[+-]?\d+)?[dfr]?|#\s+(?:\d+[/-]\d+[/-]\d+(?:\s+\d+:\d+(?::\d+)?(\s*(?:am|pm))?)?|\d+:\d+(?::\d+)?(\s*(?:am|pm))?)\s+#)/i],["pln",/^(?:(?:[a-z]|_\w)\w*(?:\[[!#%&@]+])?|\[(?:[a-z]|_\w)\w*])/i],["pun",/^[^\w\t\n\r "'[\]\xa0\u2018\u2019\u201c\u201d\u2028\u2029]+/],["pun",/^(?:\[|])/]]),["vb","vbs"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-vhdl.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-vhdl.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-vhdl.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,3 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"]],[["str",/^(?:[box]?"(?:[^"]|"")*"|'.')/i],["com",/^--[^\n\r]*/],["kwd",/^(?:abs|access|after|alias|all|and|architecture|array|assert|attribute|begin|block|body|buffer|bus|case|component|configuration|constant|disconnect|downto|else|elsif|end|entity|exit|file|for|function|generate|generic|group|guarded|if|impure|in|inertial|inout|is|label|library|linkage|literal|loop|map|mod|nand|new|next|nor|not|null|of|on|open|or|others|out|package|port|postponed|procedure|process|pure|range|record|register|reject|rem|report|return|rol|ror|select|severity|shared|signal|sla|sll|sra|srl|subtype|then|to|transport|type|unaffected|units|until|use|variable|wait|when|while|with|xnor|xor)(?=[^\w-]|$)/i,
+null],["typ",/^(?:bit|bit_vector|character|boolean|integer|real|time|string|severity_level|positive|natural|signed|unsigned|line|text|std_u?logic(?:_vector)?)(?=[^\w-]|$)/i,null],["typ",/^'(?:active|ascending|base|delayed|driving|driving_value|event|high|image|instance_name|last_active|last_event|last_value|left|leftof|length|low|path_name|pos|pred|quiet|range|reverse_range|right|rightof|simple_name|stable|succ|transaction|val|value)(?=[^\w-]|$)/i,null],["lit",/^\d+(?:_\d+)*(?:#[\w.\\]+#(?:[+-]?\d+(?:_\d+)*)?|(?:\.\d+(?:_\d+)*)?(?:e[+-]?\d+(?:_\d+)*)?)/i],
+["pln",/^(?:[a-z]\w*|\\[^\\]*\\)/i],["pun",/^[^\w\t\n\r "'\xa0][^\w\t\n\r "'\xa0-]*/]]),["vhdl","vhd"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-wiki.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-wiki.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-wiki.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,2 @@
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\d\t a-gi-z\xa0]+/,null,"\t \u00a0abcdefgijklmnopqrstuvwxyz0123456789"],["pun",/^[*=[\]^~]+/,null,"=*~^[]"]],[["lang-wiki.meta",/(?:^^|\r\n?|\n)(#[a-z]+)\b/],["lit",/^[A-Z][a-z][\da-z]+[A-Z][a-z][^\W_]+\b/],["lang-",/^{{{([\S\s]+?)}}}/],["lang-",/^`([^\n\r`]+)`/],["str",/^https?:\/\/[^\s#/?]*(?:\/[^\s#?]*)?(?:\?[^\s#]*)?(?:#\S*)?/i],["pln",/^(?:\r\n|[\S\s])[^\n\r#*=A-[^`h{~]*/]]),["wiki"]);
+PR.registerLangHandler(PR.createSimpleLexer([["kwd",/^#[a-z]+/i,null,"#"]],[]),["wiki.meta"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-xq.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-xq.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-xq.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,3 @@
+PR.registerLangHandler(PR.createSimpleLexer([["var pln",/^\$[\w-]+/,null,"$"]],[["pln",/^[\s=][<>][\s=]/],["lit",/^@[\w-]+/],["tag",/^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["com",/^\(:[\S\s]*?:\)/],["pln",/^[(),/;[\]{}]$/],["str",/^(?:"(?:[^"\\{]|\\[\S\s])*(?:"|$)|'(?:[^'\\{]|\\[\S\s])*(?:'|$))/,null,"\"'"],["kwd",/^(?:xquery|where|version|variable|union|typeswitch|treat|to|then|text|stable|sortby|some|self|schema|satisfies|returns|return|ref|processing-instruction|preceding-sibling|preceding|precedes|parent|only|of|node|namespace|module|let|item|intersect|instance|in|import|if|function|for|follows|following-sibling|following|external|except|every|else|element|descending|descendant-or-self|descendant|define|default|declare|comment|child|cast|case|before|attribute|assert|ascending|as|ancestor-or-self|ancestor|after|eq|order|by|or|and|schema-element|document-node|node|at)\b/],
+["typ",/^(?:xs:yearMonthDuration|xs:unsignedLong|xs:time|xs:string|xs:short|xs:QName|xs:Name|xs:long|xs:integer|xs:int|xs:gYearMonth|xs:gYear|xs:gMonthDay|xs:gDay|xs:float|xs:duration|xs:double|xs:decimal|xs:dayTimeDuration|xs:dateTime|xs:date|xs:byte|xs:boolean|xs:anyURI|xf:yearMonthDuration)\b/,null],["fun pln",/^(?:xp:dereference|xinc:node-expand|xinc:link-references|xinc:link-expand|xhtml:restructure|xhtml:clean|xhtml:add-lists|xdmp:zip-manifest|xdmp:zip-get|xdmp:zip-create|xdmp:xquery-version|xdmp:word-convert|xdmp:with-namespaces|xdmp:version|xdmp:value|xdmp:user-roles|xdmp:user-last-login|xdmp:user|xdmp:url-encode|xdmp:url-decode|xdmp:uri-is-file|xdmp:uri-format|xdmp:uri-content-type|xdmp:unquote|xdmp:unpath|xdmp:triggers-database|xdmp:trace|xdmp:to-json|xdmp:tidy|xdmp:subbinary|xdmp:strftime|xdmp:spawn-in|xdmp:spawn|xdmp:sleep|xdmp:shutdown|xdmp:set-session-field|xdmp:set-response-encoding|xdmp:set-response-content-type|xdmp:set-response-code|xdmp:set-request-time-limit|xdmp:set|xdmp:servers|xdmp:server-status|xdmp:server-name|xdmp:server|xdmp:security-database|xdmp:security-assert|xdmp:schema-database|xdmp:save|xdmp:role-roles|xdmp:role|xdmp:rethrow|xdmp:restart|xdmp:request-timestamp|xdmp:request-status|xdmp:request-cancel|xdmp:request|xdmp:redirect-response|xdmp:random|xdmp:quote|xdmp:query-trace|xdmp:query-meters|xdmp:product-edition|xdmp:privilege-roles|xdmp:privilege|xdmp:pretty-print|xdmp:powerpoint-convert|xdmp:platform|xdmp:permission|xdmp:pdf-convert|xdmp:path|xdmp:octal-to-integer|xdmp:node-uri|xdmp:node-replace|xdmp:node-kind|xdmp:node-insert-child|xdmp:node-insert-before|xdmp:node-insert-after|xdmp:node-delete|xdmp:node-database|xdmp:mul64|xdmp:modules-root|xdmp:modules-database|xdmp:merging|xdmp:merge-cancel|xdmp:merge|xdmp:md5|xdmp:logout|xdmp:login|xdmp:log-level|xdmp:log|xdmp:lock-release|xdmp:lock-acquire|xdmp:load|xdmp:invoke-in|xdmp:invoke|xdmp:integer-to-octal|xdmp:integer-to-hex|xdmp:http-put|xdmp:http-post|xdmp:http-options|xdmp:http-head|xdmp:http-get|xdmp:http-delete|xdmp:hosts|xdmp:host-status|xdmp:host-name|xdmp:host|xdmp:hex-to-integer|xdmp:hash64|xdmp:hash32|xdmp:has-privilege|xdmp:groups|xdmp:group-serves|xdmp:group-servers|xdmp:group-name|xdmp:group-hosts|xdmp:group|xdmp:get-session-field-names|xdmp:get-session-field|xdmp:get-response-encoding|xdmp:get-response-code|xdmp:get-request-username|xdmp:get-request-user|xdmp:get-request-url|xdmp:get-request-protocol|xdmp:get-request-path|xdmp:get-request-method|xdmp:get-request-header-names|xdmp:get-request-header|xdmp:get-request-field-names|xdmp:get-request-field-filename|xdmp:get-request-field-content-type|xdmp:get-request-field|xdmp:get-request-client-certificate|xdmp:get-request-client-address|xdmp:get-request-body|xdmp:get-current-user|xdmp:get-current-roles|xdmp:get|xdmp:function-name|xdmp:function-module|xdmp:function|xdmp:from-json|xdmp:forests|xdmp:forest-status|xdmp:forest-restore|xdmp:forest-restart|xdmp:forest-name|xdmp:forest-delete|xdmp:forest-databases|xdmp:forest-counts|xdmp:forest-clear|xdmp:forest-backup|xdmp:forest|xdmp:filesystem-file|xdmp:filesystem-directory|xdmp:exists|xdmp:excel-convert|xdmp:eval-in|xdmp:eval|xdmp:estimate|xdmp:email|xdmp:element-content-type|xdmp:elapsed-time|xdmp:document-set-quality|xdmp:document-set-property|xdmp:document-set-properties|xdmp:document-set-permissions|xdmp:document-set-collections|xdmp:document-remove-properties|xdmp:document-remove-permissions|xdmp:document-remove-collections|xdmp:document-properties|xdmp:document-locks|xdmp:document-load|xdmp:document-insert|xdmp:document-get-quality|xdmp:document-get-properties|xdmp:document-get-permissions|xdmp:document-get-collections|xdmp:document-get|xdmp:document-forest|xdmp:document-delete|xdmp:document-add-properties|xdmp:document-add-permissions|xdmp:document-add-collections|xdmp:directory-properties|xdmp:directory-locks|xdmp:directory-delete|xdmp:directory-create|xdmp:directory|xdmp:diacritic-less|xdmp:describe|xdmp:default-permissions|xdmp:default-collections|xdmp:databases|xdmp:database-restore-validate|xdmp:database-restore-status|xdmp:database-restore-cancel|xdmp:database-restore|xdmp:database-name|xdmp:database-forests|xdmp:database-backup-validate|xdmp:database-backup-status|xdmp:database-backup-purge|xdmp:database-backup-cancel|xdmp:database-backup|xdmp:database|xdmp:collection-properties|xdmp:collection-locks|xdmp:collection-delete|xdmp:collation-canonical-uri|xdmp:castable-as|xdmp:can-grant-roles|xdmp:base64-encode|xdmp:base64-decode|xdmp:architecture|xdmp:apply|xdmp:amp-roles|xdmp:amp|xdmp:add64|xdmp:add-response-header|xdmp:access|trgr:trigger-set-recursive|trgr:trigger-set-permissions|trgr:trigger-set-name|trgr:trigger-set-module|trgr:trigger-set-event|trgr:trigger-set-description|trgr:trigger-remove-permissions|trgr:trigger-module|trgr:trigger-get-permissions|trgr:trigger-enable|trgr:trigger-disable|trgr:trigger-database-online-event|trgr:trigger-data-event|trgr:trigger-add-permissions|trgr:remove-trigger|trgr:property-content|trgr:pre-commit|trgr:post-commit|trgr:get-trigger-by-id|trgr:get-trigger|trgr:document-scope|trgr:document-content|trgr:directory-scope|trgr:create-trigger|trgr:collection-scope|trgr:any-property-content|thsr:set-entry|thsr:remove-term|thsr:remove-synonym|thsr:remove-entry|thsr:query-lookup|thsr:lookup|thsr:load|thsr:insert|thsr:expand|thsr:add-synonym|spell:suggest-detailed|spell:suggest|spell:remove-word|spell:make-dictionary|spell:load|spell:levenshtein-distance|spell:is-correct|spell:insert|spell:double-metaphone|spell:add-word|sec:users-collection|sec:user-set-roles|sec:user-set-password|sec:user-set-name|sec:user-set-description|sec:user-set-default-permissions|sec:user-set-default-collections|sec:user-remove-roles|sec:user-privileges|sec:user-get-roles|sec:user-get-description|sec:user-get-default-permissions|sec:user-get-default-collections|sec:user-doc-permissions|sec:user-doc-collections|sec:user-add-roles|sec:unprotect-collection|sec:uid-for-name|sec:set-realm|sec:security-version|sec:security-namespace|sec:security-installed|sec:security-collection|sec:roles-collection|sec:role-set-roles|sec:role-set-name|sec:role-set-description|sec:role-set-default-permissions|sec:role-set-default-collections|sec:role-remove-roles|sec:role-privileges|sec:role-get-roles|sec:role-get-description|sec:role-get-default-permissions|sec:role-get-default-collections|sec:role-doc-permissions|sec:role-doc-collections|sec:role-add-roles|sec:remove-user|sec:remove-role-from-users|sec:remove-role-from-role|sec:remove-role-from-privileges|sec:remove-role-from-amps|sec:remove-role|sec:remove-privilege|sec:remove-amp|sec:protect-collection|sec:privileges-collection|sec:privilege-set-roles|sec:privilege-set-name|sec:privilege-remove-roles|sec:privilege-get-roles|sec:privilege-add-roles|sec:priv-doc-permissions|sec:priv-doc-collections|sec:get-user-names|sec:get-unique-elem-id|sec:get-role-names|sec:get-role-ids|sec:get-privilege|sec:get-distinct-permissions|sec:get-collection|sec:get-amp|sec:create-user-with-role|sec:create-user|sec:create-role|sec:create-privilege|sec:create-amp|sec:collections-collection|sec:collection-set-permissions|sec:collection-remove-permissions|sec:collection-get-permissions|sec:collection-add-permissions|sec:check-admin|sec:amps-collection|sec:amp-set-roles|sec:amp-remove-roles|sec:amp-get-roles|sec:amp-doc-permissions|sec:amp-doc-collections|sec:amp-add-roles|search:unparse|search:suggest|search:snippet|search:search|search:resolve-nodes|search:resolve|search:remove-constraint|search:parse|search:get-default-options|search:estimate|search:check-options|prof:value|prof:reset|prof:report|prof:invoke|prof:eval|prof:enable|prof:disable|prof:allowed|ppt:clean|pki:template-set-request|pki:template-set-name|pki:template-set-key-type|pki:template-set-key-options|pki:template-set-description|pki:template-in-use|pki:template-get-version|pki:template-get-request|pki:template-get-name|pki:template-get-key-type|pki:template-get-key-options|pki:template-get-id|pki:template-get-description|pki:need-certificate|pki:is-temporary|pki:insert-trusted-certificates|pki:insert-template|pki:insert-signed-certificates|pki:insert-certificate-revocation-list|pki:get-trusted-certificate-ids|pki:get-template-ids|pki:get-template-certificate-authority|pki:get-template-by-name|pki:get-template|pki:get-pending-certificate-requests-xml|pki:get-pending-certificate-requests-pem|pki:get-pending-certificate-request|pki:get-certificates-for-template-xml|pki:get-certificates-for-template|pki:get-certificates|pki:get-certificate-xml|pki:get-certificate-pem|pki:get-certificate|pki:generate-temporary-certificate-if-necessary|pki:generate-temporary-certificate|pki:generate-template-certificate-authority|pki:generate-certificate-request|pki:delete-template|pki:delete-certificate|pki:create-template|pdf:make-toc|pdf:insert-toc-headers|pdf:get-toc|pdf:clean|p:status-transition|p:state-transition|p:remove|p:pipelines|p:insert|p:get-by-id|p:get|p:execute|p:create|p:condition|p:collection|p:action|ooxml:runs-merge|ooxml:package-uris|ooxml:package-parts-insert|ooxml:package-parts|msword:clean|mcgm:polygon|mcgm:point|mcgm:geospatial-query-from-elements|mcgm:geospatial-query|mcgm:circle|math:tanh|math:tan|math:sqrt|math:sinh|math:sin|math:pow|math:modf|math:log10|math:log|math:ldexp|math:frexp|math:fmod|math:floor|math:fabs|math:exp|math:cosh|math:cos|math:ceil|math:atan2|math:atan|math:asin|math:acos|map:put|map:map|map:keys|map:get|map:delete|map:count|map:clear|lnk:to|lnk:remove|lnk:insert|lnk:get|lnk:from|lnk:create|kml:polygon|kml:point|kml:interior-polygon|kml:geospatial-query-from-elements|kml:geospatial-query|kml:circle|kml:box|gml:polygon|gml:point|gml:interior-polygon|gml:geospatial-query-from-elements|gml:geospatial-query|gml:circle|gml:box|georss:point|georss:geospatial-query|georss:circle|geo:polygon|geo:point|geo:interior-polygon|geo:geospatial-query-from-elements|geo:geospatial-query|geo:circle|geo:box|fn:zero-or-one|fn:years-from-duration|fn:year-from-dateTime|fn:year-from-date|fn:upper-case|fn:unordered|fn:true|fn:translate|fn:trace|fn:tokenize|fn:timezone-from-time|fn:timezone-from-dateTime|fn:timezone-from-date|fn:sum|fn:subtract-dateTimes-yielding-yearMonthDuration|fn:subtract-dateTimes-yielding-dayTimeDuration|fn:substring-before|fn:substring-after|fn:substring|fn:subsequence|fn:string-to-codepoints|fn:string-pad|fn:string-length|fn:string-join|fn:string|fn:static-base-uri|fn:starts-with|fn:seconds-from-time|fn:seconds-from-duration|fn:seconds-from-dateTime|fn:round-half-to-even|fn:round|fn:root|fn:reverse|fn:resolve-uri|fn:resolve-QName|fn:replace|fn:remove|fn:QName|fn:prefix-from-QName|fn:position|fn:one-or-more|fn:number|fn:not|fn:normalize-unicode|fn:normalize-space|fn:node-name|fn:node-kind|fn:nilled|fn:namespace-uri-from-QName|fn:namespace-uri-for-prefix|fn:namespace-uri|fn:name|fn:months-from-duration|fn:month-from-dateTime|fn:month-from-date|fn:minutes-from-time|fn:minutes-from-duration|fn:minutes-from-dateTime|fn:min|fn:max|fn:matches|fn:lower-case|fn:local-name-from-QName|fn:local-name|fn:last|fn:lang|fn:iri-to-uri|fn:insert-before|fn:index-of|fn:in-scope-prefixes|fn:implicit-timezone|fn:idref|fn:id|fn:hours-from-time|fn:hours-from-duration|fn:hours-from-dateTime|fn:floor|fn:false|fn:expanded-QName|fn:exists|fn:exactly-one|fn:escape-uri|fn:escape-html-uri|fn:error|fn:ends-with|fn:encode-for-uri|fn:empty|fn:document-uri|fn:doc-available|fn:doc|fn:distinct-values|fn:distinct-nodes|fn:default-collation|fn:deep-equal|fn:days-from-duration|fn:day-from-dateTime|fn:day-from-date|fn:data|fn:current-time|fn:current-dateTime|fn:current-date|fn:count|fn:contains|fn:concat|fn:compare|fn:collection|fn:codepoints-to-string|fn:codepoint-equal|fn:ceiling|fn:boolean|fn:base-uri|fn:avg|fn:adjust-time-to-timezone|fn:adjust-dateTime-to-timezone|fn:adjust-date-to-timezone|fn:abs|feed:unsubscribe|feed:subscription|feed:subscribe|feed:request|feed:item|feed:description|excel:clean|entity:enrich|dom:set-pipelines|dom:set-permissions|dom:set-name|dom:set-evaluation-context|dom:set-domain-scope|dom:set-description|dom:remove-pipeline|dom:remove-permissions|dom:remove|dom:get|dom:evaluation-context|dom:domains|dom:domain-scope|dom:create|dom:configuration-set-restart-user|dom:configuration-set-permissions|dom:configuration-set-evaluation-context|dom:configuration-set-default-domain|dom:configuration-get|dom:configuration-create|dom:collection|dom:add-pipeline|dom:add-permissions|dls:retention-rules|dls:retention-rule-remove|dls:retention-rule-insert|dls:retention-rule|dls:purge|dls:node-expand|dls:link-references|dls:link-expand|dls:documents-query|dls:document-versions-query|dls:document-version-uri|dls:document-version-query|dls:document-version-delete|dls:document-version-as-of|dls:document-version|dls:document-update|dls:document-unmanage|dls:document-set-quality|dls:document-set-property|dls:document-set-properties|dls:document-set-permissions|dls:document-set-collections|dls:document-retention-rules|dls:document-remove-properties|dls:document-remove-permissions|dls:document-remove-collections|dls:document-purge|dls:document-manage|dls:document-is-managed|dls:document-insert-and-manage|dls:document-include-query|dls:document-history|dls:document-get-permissions|dls:document-extract-part|dls:document-delete|dls:document-checkout-status|dls:document-checkout|dls:document-checkin|dls:document-add-properties|dls:document-add-permissions|dls:document-add-collections|dls:break-checkout|dls:author-query|dls:as-of-query|dbk:convert|dbg:wait|dbg:value|dbg:stopped|dbg:stop|dbg:step|dbg:status|dbg:stack|dbg:out|dbg:next|dbg:line|dbg:invoke|dbg:function|dbg:finish|dbg:expr|dbg:eval|dbg:disconnect|dbg:detach|dbg:continue|dbg:connect|dbg:clear|dbg:breakpoints|dbg:break|dbg:attached|dbg:attach|cvt:save-converted-documents|cvt:part-uri|cvt:destination-uri|cvt:basepath|cvt:basename|cts:words|cts:word-query-weight|cts:word-query-text|cts:word-query-options|cts:word-query|cts:word-match|cts:walk|cts:uris|cts:uri-match|cts:train|cts:tokenize|cts:thresholds|cts:stem|cts:similar-query-weight|cts:similar-query-nodes|cts:similar-query|cts:shortest-distance|cts:search|cts:score|cts:reverse-query-weight|cts:reverse-query-nodes|cts:reverse-query|cts:remainder|cts:registered-query-weight|cts:registered-query-options|cts:registered-query-ids|cts:registered-query|cts:register|cts:query|cts:quality|cts:properties-query-query|cts:properties-query|cts:polygon-vertices|cts:polygon|cts:point-longitude|cts:point-latitude|cts:point|cts:or-query-queries|cts:or-query|cts:not-query-weight|cts:not-query-query|cts:not-query|cts:near-query-weight|cts:near-query-queries|cts:near-query-options|cts:near-query-distance|cts:near-query|cts:highlight|cts:geospatial-co-occurrences|cts:frequency|cts:fitness|cts:field-words|cts:field-word-query-weight|cts:field-word-query-text|cts:field-word-query-options|cts:field-word-query-field-name|cts:field-word-query|cts:field-word-match|cts:entity-highlight|cts:element-words|cts:element-word-query-weight|cts:element-word-query-text|cts:element-word-query-options|cts:element-word-query-element-name|cts:element-word-query|cts:element-word-match|cts:element-values|cts:element-value-ranges|cts:element-value-query-weight|cts:element-value-query-text|cts:element-value-query-options|cts:element-value-query-element-name|cts:element-value-query|cts:element-value-match|cts:element-value-geospatial-co-occurrences|cts:element-value-co-occurrences|cts:element-range-query-weight|cts:element-range-query-value|cts:element-range-query-options|cts:element-range-query-operator|cts:element-range-query-element-name|cts:element-range-query|cts:element-query-query|cts:element-query-element-name|cts:element-query|cts:element-pair-geospatial-values|cts:element-pair-geospatial-value-match|cts:element-pair-geospatial-query-weight|cts:element-pair-geospatial-query-region|cts:element-pair-geospatial-query-options|cts:element-pair-geospatial-query-longitude-name|cts:element-pair-geospatial-query-latitude-name|cts:element-pair-geospatial-query-element-name|cts:element-pair-geospatial-query|cts:element-pair-geospatial-boxes|cts:element-geospatial-values|cts:element-geospatial-value-match|cts:element-geospatial-query-weight|cts:element-geospatial-query-region|cts:element-geospatial-query-options|cts:element-geospatial-query-element-name|cts:element-geospatial-query|cts:element-geospatial-boxes|cts:element-child-geospatial-values|cts:element-child-geospatial-value-match|cts:element-child-geospatial-query-weight|cts:element-child-geospatial-query-region|cts:element-child-geospatial-query-options|cts:element-child-geospatial-query-element-name|cts:element-child-geospatial-query-child-name|cts:element-child-geospatial-query|cts:element-child-geospatial-boxes|cts:element-attribute-words|cts:element-attribute-word-query-weight|cts:element-attribute-word-query-text|cts:element-attribute-word-query-options|cts:element-attribute-word-query-element-name|cts:element-attribute-word-query-attribute-name|cts:element-attribute-word-query|cts:element-attribute-word-match|cts:element-attribute-values|cts:element-attribute-value-ranges|cts:element-attribute-value-query-weight|cts:element-attribute-value-query-text|cts:element-attribute-value-query-options|cts:element-attribute-value-query-element-name|cts:element-attribute-value-query-attribute-name|cts:element-attribute-value-query|cts:element-attribute-value-match|cts:element-attribute-value-geospatial-co-occurrences|cts:element-attribute-value-co-occurrences|cts:element-attribute-range-query-weight|cts:element-attribute-range-query-value|cts:element-attribute-range-query-options|cts:element-attribute-range-query-operator|cts:element-attribute-range-query-element-name|cts:element-attribute-range-query-attribute-name|cts:element-attribute-range-query|cts:element-attribute-pair-geospatial-values|cts:element-attribute-pair-geospatial-value-match|cts:element-attribute-pair-geospatial-query-weight|cts:element-attribute-pair-geospatial-query-region|cts:element-attribute-pair-geospatial-query-options|cts:element-attribute-pair-geospatial-query-longitude-name|cts:element-attribute-pair-geospatial-query-latitude-name|cts:element-attribute-pair-geospatial-query-element-name|cts:element-attribute-pair-geospatial-query|cts:element-attribute-pair-geospatial-boxes|cts:document-query-uris|cts:document-query|cts:distance|cts:directory-query-uris|cts:directory-query-depth|cts:directory-query|cts:destination|cts:deregister|cts:contains|cts:confidence|cts:collections|cts:collection-query-uris|cts:collection-query|cts:collection-match|cts:classify|cts:circle-radius|cts:circle-center|cts:circle|cts:box-west|cts:box-south|cts:box-north|cts:box-east|cts:box|cts:bearing|cts:arc-intersection|cts:and-query-queries|cts:and-query-options|cts:and-query|cts:and-not-query-positive-query|cts:and-not-query-negative-query|cts:and-not-query|css:get|css:convert|cpf:success|cpf:failure|cpf:document-set-state|cpf:document-set-processing-status|cpf:document-set-last-updated|cpf:document-set-error|cpf:document-get-state|cpf:document-get-processing-status|cpf:document-get-last-updated|cpf:document-get-error|cpf:check-transition|alert:spawn-matching-actions|alert:rule-user-id-query|alert:rule-set-user-id|alert:rule-set-query|alert:rule-set-options|alert:rule-set-name|alert:rule-set-description|alert:rule-set-action|alert:rule-remove|alert:rule-name-query|alert:rule-insert|alert:rule-id-query|alert:rule-get-user-id|alert:rule-get-query|alert:rule-get-options|alert:rule-get-name|alert:rule-get-id|alert:rule-get-description|alert:rule-get-action|alert:rule-action-query|alert:remove-triggers|alert:make-rule|alert:make-log-action|alert:make-config|alert:make-action|alert:invoke-matching-actions|alert:get-my-rules|alert:get-all-rules|alert:get-actions|alert:find-matching-rules|alert:create-triggers|alert:config-set-uri|alert:config-set-trigger-ids|alert:config-set-options|alert:config-set-name|alert:config-set-description|alert:config-set-cpf-domain-names|alert:config-set-cpf-domain-ids|alert:config-insert|alert:config-get-uri|alert:config-get-trigger-ids|alert:config-get-options|alert:config-get-name|alert:config-get-id|alert:config-get-description|alert:config-get-cpf-domain-names|alert:config-get-cpf-domain-ids|alert:config-get|alert:config-delete|alert:action-set-options|alert:action-set-name|alert:action-set-module-root|alert:action-set-module-db|alert:action-set-module|alert:action-set-description|alert:action-remove|alert:action-insert|alert:action-get-options|alert:action-get-name|alert:action-get-module-root|alert:action-get-module-db|alert:action-get-module|alert:action-get-description|zero-or-one|years-from-duration|year-from-dateTime|year-from-date|upper-case|unordered|true|translate|trace|tokenize|timezone-from-time|timezone-from-dateTime|timezone-from-date|sum|subtract-dateTimes-yielding-yearMonthDuration|subtract-dateTimes-yielding-dayTimeDuration|substring-before|substring-after|substring|subsequence|string-to-codepoints|string-pad|string-length|string-join|string|static-base-uri|starts-with|seconds-from-time|seconds-from-duration|seconds-from-dateTime|round-half-to-even|round|root|reverse|resolve-uri|resolve-QName|replace|remove|QName|prefix-from-QName|position|one-or-more|number|not|normalize-unicode|normalize-space|node-name|node-kind|nilled|namespace-uri-from-QName|namespace-uri-for-prefix|namespace-uri|name|months-from-duration|month-from-dateTime|month-from-date|minutes-from-time|minutes-from-duration|minutes-from-dateTime|min|max|matches|lower-case|local-name-from-QName|local-name|last|lang|iri-to-uri|insert-before|index-of|in-scope-prefixes|implicit-timezone|idref|id|hours-from-time|hours-from-duration|hours-from-dateTime|floor|false|expanded-QName|exists|exactly-one|escape-uri|escape-html-uri|error|ends-with|encode-for-uri|empty|document-uri|doc-available|doc|distinct-values|distinct-nodes|default-collation|deep-equal|days-from-duration|day-from-dateTime|day-from-date|data|current-time|current-dateTime|current-date|count|contains|concat|compare|collection|codepoints-to-string|codepoint-equal|ceiling|boolean|base-uri|avg|adjust-time-to-timezone|adjust-dateTime-to-timezone|adjust-date-to-timezone|abs)\b/],
+["pln",/^[\w:-]+/],["pln",/^[\t\n\r \xa0]+/]]),["xq","xquery"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-yaml.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-yaml.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/lang-yaml.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,2 @@
+var a=null;
+PR.registerLangHandler(PR.createSimpleLexer([["pun",/^[:>?|]+/,a,":|>?"],["dec",/^%(?:YAML|TAG)[^\n\r#]+/,a,"%"],["typ",/^&\S+/,a,"&"],["typ",/^!\S*/,a,"!"],["str",/^"(?:[^"\\]|\\.)*(?:"|$)/,a,'"'],["str",/^'(?:[^']|'')*(?:'|$)/,a,"'"],["com",/^#[^\n\r]*/,a,"#"],["pln",/^\s+/,a," \t\r\n"]],[["dec",/^(?:---|\.\.\.)(?:[\n\r]|$)/],["pun",/^-/],["kwd",/^\w+:[\n\r ]/],["pln",/^\w+/]]),["yaml","yml"]);
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/prettify.css
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/prettify.css (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/prettify.css 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1 @@
+.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
\ No newline at end of file
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/prettify.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/prettify.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/prettify.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,30 @@
+!function(){var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;
+(function(){function S(a){function d(e){var b=e.charCodeAt(0);if(b!==92)return b;var a=e.charAt(1);return(b=r[a])?b:"0"<=a&&a<="7"?parseInt(e.substring(1),8):a==="u"||a==="x"?parseInt(e.substring(2),16):e.charCodeAt(1)}function g(e){if(e<32)return(e<16?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);return e==="\\"||e==="-"||e==="]"||e==="^"?"\\"+e:e}function b(e){var b=e.substring(1,e.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),e=[],a=
+b[0]==="^",c=["["];a&&c.push("^");for(var a=a?1:0,f=b.length;a<f;++a){var h=b[a];if(/\\[bdsw]/i.test(h))c.push(h);else{var h=d(h),l;a+2<f&&"-"===b[a+1]?(l=d(b[a+2]),a+=2):l=h;e.push([h,l]);l<65||h>122||(l<65||h>90||e.push([Math.max(65,h)|32,Math.min(l,90)|32]),l<97||h>122||e.push([Math.max(97,h)&-33,Math.min(l,122)&-33]))}}e.sort(function(e,a){return e[0]-a[0]||a[1]-e[1]});b=[];f=[];for(a=0;a<e.length;++a)h=e[a],h[0]<=f[1]+1?f[1]=Math.max(f[1],h[1]):b.push(f=h);for(a=0;a<b.length;++a)h=b[a],c.push(g(h[0])),
+h[1]>h[0]&&(h[1]+1>h[0]&&c.push("-"),c.push(g(h[1])));c.push("]");return c.join("")}function s(e){for(var a=e.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),c=a.length,d=[],f=0,h=0;f<c;++f){var l=a[f];l==="("?++h:"\\"===l.charAt(0)&&(l=+l.substring(1))&&(l<=h?d[l]=-1:a[f]=g(l))}for(f=1;f<d.length;++f)-1===d[f]&&(d[f]=++x);for(h=f=0;f<c;++f)l=a[f],l==="("?(++h,d[h]||(a[f]="(?:")):"\\"===l.charAt(0)&&(l=+l.substring(1))&&l<=h&&
+(a[f]="\\"+d[l]);for(f=0;f<c;++f)"^"===a[f]&&"^"!==a[f+1]&&(a[f]="");if(e.ignoreCase&&m)for(f=0;f<c;++f)l=a[f],e=l.charAt(0),l.length>=2&&e==="["?a[f]=b(l):e!=="\\"&&(a[f]=l.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var x=0,m=!1,j=!1,k=0,c=a.length;k<c;++k){var i=a[k];if(i.ignoreCase)j=!0;else if(/[a-z]/i.test(i.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){m=!0;j=!1;break}}for(var r={b:8,t:9,n:10,v:11,
+f:12,r:13},n=[],k=0,c=a.length;k<c;++k){i=a[k];if(i.global||i.multiline)throw Error(""+i);n.push("(?:"+s(i)+")")}return RegExp(n.join("|"),j?"gi":"g")}function T(a,d){function g(a){var c=a.nodeType;if(c==1){if(!b.test(a.className)){for(c=a.firstChild;c;c=c.nextSibling)g(c);c=a.nodeName.toLowerCase();if("br"===c||"li"===c)s[j]="\n",m[j<<1]=x++,m[j++<<1|1]=a}}else if(c==3||c==4)c=a.nodeValue,c.length&&(c=d?c.replace(/\r\n?/g,"\n"):c.replace(/[\t\n\r ]+/g," "),s[j]=c,m[j<<1]=x,x+=c.length,m[j++<<1|1]=
+a)}var b=/(?:^|\s)nocode(?:\s|$)/,s=[],x=0,m=[],j=0;g(a);return{a:s.join("").replace(/\n$/,""),d:m}}function H(a,d,g,b){d&&(a={a:d,e:a},g(a),b.push.apply(b,a.g))}function U(a){for(var d=void 0,g=a.firstChild;g;g=g.nextSibling)var b=g.nodeType,d=b===1?d?a:g:b===3?V.test(g.nodeValue)?a:d:d;return d===a?void 0:d}function C(a,d){function g(a){for(var j=a.e,k=[j,"pln"],c=0,i=a.a.match(s)||[],r={},n=0,e=i.length;n<e;++n){var z=i[n],w=r[z],t=void 0,f;if(typeof w==="string")f=!1;else{var h=b[z.charAt(0)];
+if(h)t=z.match(h[1]),w=h[0];else{for(f=0;f<x;++f)if(h=d[f],t=z.match(h[1])){w=h[0];break}t||(w="pln")}if((f=w.length>=5&&"lang-"===w.substring(0,5))&&!(t&&typeof t[1]==="string"))f=!1,w="src";f||(r[z]=w)}h=c;c+=z.length;if(f){f=t[1];var l=z.indexOf(f),B=l+f.length;t[2]&&(B=z.length-t[2].length,l=B-f.length);w=w.substring(5);H(j+h,z.substring(0,l),g,k);H(j+h+l,f,I(w,f),k);H(j+h+B,z.substring(B),g,k)}else k.push(j+h,w)}a.g=k}var b={},s;(function(){for(var g=a.concat(d),j=[],k={},c=0,i=g.length;c<i;++c){var r=
+g[c],n=r[3];if(n)for(var e=n.length;--e>=0;)b[n.charAt(e)]=r;r=r[1];n=""+r;k.hasOwnProperty(n)||(j.push(r),k[n]=q)}j.push(/[\S\s]/);s=S(j)})();var x=d.length;return g}function v(a){var d=[],g=[];a.tripleQuotedStrings?d.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?d.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
+q,"'\"`"]):d.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&g.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var b=a.hashComments;b&&(a.cStyleComments?(b>1?d.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):d.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),g.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,q])):d.push(["com",
+/^#[^\n\r]*/,q,"#"]));a.cStyleComments&&(g.push(["com",/^\/\/[^\n\r]*/,q]),g.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));if(b=a.regexLiterals){var s=(b=b>1?"":"\n\r")?".":"[\\S\\s]";g.push(["lang-regex",RegExp("^(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+("/(?=[^/*"+b+"])(?:[^/\\x5B\\x5C"+b+"]|\\x5C"+s+"|\\x5B(?:[^\\x5C\\x5D"+b+"]|\\x5C"+
+s+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&g.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&g.push(["kwd",RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),q]);d.push(["pln",/^\s+/,q," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");g.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,
+q],["pun",RegExp(b),q]);return C(d,g)}function J(a,d,g){function b(a){var c=a.nodeType;if(c==1&&!x.test(a.className))if("br"===a.nodeName)s(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((c==3||c==4)&&g){var d=a.nodeValue,i=d.match(m);if(i)c=d.substring(0,i.index),a.nodeValue=c,(d=d.substring(i.index+i[0].length))&&a.parentNode.insertBefore(j.createTextNode(d),a.nextSibling),s(a),c||a.parentNode.removeChild(a)}}function s(a){function b(a,c){var d=
+c?a.cloneNode(!1):a,e=a.parentNode;if(e){var e=b(e,1),g=a.nextSibling;e.appendChild(d);for(var i=g;i;i=g)g=i.nextSibling,e.appendChild(i)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),d;(d=a.parentNode)&&d.nodeType===1;)a=d;c.push(a)}for(var x=/(?:^|\s)nocode(?:\s|$)/,m=/\r\n?|\n/,j=a.ownerDocument,k=j.createElement("li");a.firstChild;)k.appendChild(a.firstChild);for(var c=[k],i=0;i<c.length;++i)b(c[i]);d===(d|0)&&c[0].setAttribute("value",d);var r=j.createElement("ol");
+r.className="linenums";for(var d=Math.max(0,d-1|0)||0,i=0,n=c.length;i<n;++i)k=c[i],k.className="L"+(i+d)%10,k.firstChild||k.appendChild(j.createTextNode("\u00a0")),r.appendChild(k);a.appendChild(r)}function p(a,d){for(var g=d.length;--g>=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn("cannot override language handler %s",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\s*</.test(d)?"default-markup":"default-code";return F[a]}function K(a){var d=a.h;try{var g=T(a.c,a.i),b=g.a;
+a.a=b;a.d=g.d;a.e=0;I(d,b)(a);var s=/\bMSIE\s(\d+)/.exec(navigator.userAgent),s=s&&+s[1]<=8,d=/\n/g,x=a.a,m=x.length,g=0,j=a.d,k=j.length,b=0,c=a.g,i=c.length,r=0;c[i]=m;var n,e;for(e=n=0;e<i;)c[e]!==c[e+2]?(c[n++]=c[e++],c[n++]=c[e++]):e+=2;i=n;for(e=n=0;e<i;){for(var p=c[e],w=c[e+1],t=e+2;t+2<=i&&c[t+1]===w;)t+=2;c[n++]=p;c[n++]=w;e=t}c.length=n;var f=a.c,h;if(f)h=f.style.display,f.style.display="none";try{for(;b<k;){var l=j[b+2]||m,B=c[r+2]||m,t=Math.min(l,B),A=j[b+1],G;if(A.nodeType!==1&&(G=x.substring(g,
+t))){s&&(G=G.replace(d,"\r"));A.nodeValue=G;var L=A.ownerDocument,o=L.createElement("span");o.className=c[r+1];var v=A.parentNode;v.replaceChild(o,A);o.appendChild(A);g<l&&(j[b+1]=A=L.createTextNode(x.substring(t,l)),v.insertBefore(A,o.nextSibling))}g=t;g>=l&&(b+=2);g>=B&&(r+=2)}}finally{if(f)f.style.display=h}}catch(u){D.console&&console.log(u&&u.stack||u)}}var D=window,y=["break,continue,do,else,for,if,return,while"],E=[[y,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
+"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],M=[E,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],N=[E,"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"],
+O=[N,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],E=[E,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],P=[y,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
+Q=[y,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],W=[y,"as,assert,const,copy,drop,enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv,pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"],y=[y,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],R=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/,
+V=/\S/,X=v({keywords:[M,O,E,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",P,Q,y],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),F={};p(X,["default-code"]);p(C([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",
+/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);p(C([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],
+["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);p(C([],[["atv",/^[\S\s]+/]]),["uq.val"]);p(v({keywords:M,hashComments:!0,cStyleComments:!0,types:R}),["c","cc","cpp","cxx","cyc","m"]);p(v({keywords:"null,true,false"}),["json"]);p(v({keywords:O,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:R}),
+["cs"]);p(v({keywords:N,cStyleComments:!0}),["java"]);p(v({keywords:y,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);p(v({keywords:P,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);p(v({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:2}),["perl","pl","pm"]);p(v({keywords:Q,
+hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);p(v({keywords:E,cStyleComments:!0,regexLiterals:!0}),["javascript","js"]);p(v({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);p(v({keywords:W,cStyleComments:!0,multilineStrings:!0}),["rc","rs","rust"]);
+p(C([],[["str",/^[\S\s]+/]]),["regex"]);var Y=D.PR={createSimpleLexer:C,registerLangHandler:p,sourceDecorator:v,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:D.prettyPrintOne=function(a,d,g){var b=document.createElement("div");b.innerHTML="<pre>"+a+"</pre>";b=b.firstChild;g&&J(b,g,!0);K({h:d,j:g,c:b,i:1});
+return b.innerHTML},prettyPrint:D.prettyPrint=function(a,d){function g(){for(var b=D.PR_SHOULD_USE_CONTINUATION?c.now()+250:Infinity;i<p.length&&c.now()<b;i++){for(var d=p[i],j=h,k=d;k=k.previousSibling;){var m=k.nodeType,o=(m===7||m===8)&&k.nodeValue;if(o?!/^\??prettify\b/.test(o):m!==3||/\S/.test(k.nodeValue))break;if(o){j={};o.replace(/\b(\w+)=([\w%+\-.:]+)/g,function(a,b,c){j[b]=c});break}}k=d.className;if((j!==h||e.test(k))&&!v.test(k)){m=!1;for(o=d.parentNode;o;o=o.parentNode)if(f.test(o.tagName)&&
+o.className&&e.test(o.className)){m=!0;break}if(!m){d.className+=" prettyprinted";m=j.lang;if(!m){var m=k.match(n),y;if(!m&&(y=U(d))&&t.test(y.tagName))m=y.className.match(n);m&&(m=m[1])}if(w.test(d.tagName))o=1;else var o=d.currentStyle,u=s.defaultView,o=(o=o?o.whiteSpace:u&&u.getComputedStyle?u.getComputedStyle(d,q).getPropertyValue("white-space"):0)&&"pre"===o.substring(0,3);u=j.linenums;if(!(u=u==="true"||+u))u=(u=k.match(/\blinenums\b(?::(\d+))?/))?u[1]&&u[1].length?+u[1]:!0:!1;u&&J(d,u,o);r=
+{h:m,c:d,j:u,i:o};K(r)}}}i<p.length?setTimeout(g,250):"function"===typeof a&&a()}for(var b=d||document.body,s=b.ownerDocument||document,b=[b.getElementsByTagName("pre"),b.getElementsByTagName("code"),b.getElementsByTagName("xmp")],p=[],m=0;m<b.length;++m)for(var j=0,k=b[m].length;j<k;++j)p.push(b[m][j]);var b=q,c=Date;c.now||(c={now:function(){return+new Date}});var i=0,r,n=/\blang(?:uage)?-([\w.]+)(?!\S)/,e=/\bprettyprint\b/,v=/\bprettyprinted\b/,w=/pre|xmp/i,t=/^code$/i,f=/^(?:pre|code|xmp)$/i,
+h={};g()}};typeof define==="function"&&define.amd&&define("google-code-prettify",[],function(){return Y})})();}()
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/run_prettify.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/run_prettify.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/google-code-prettify/run_prettify.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,34 @@
+!function(){var r=null;
+(function(){function X(e){function j(){try{J.doScroll("left")}catch(e){P(j,50);return}w("poll")}function w(j){if(!(j.type=="readystatechange"&&x.readyState!="complete")&&((j.type=="load"?n:x)[z](i+j.type,w,!1),!m&&(m=!0)))e.call(n,j.type||j)}var Y=x.addEventListener,m=!1,C=!0,t=Y?"addEventListener":"attachEvent",z=Y?"removeEventListener":"detachEvent",i=Y?"":"on";if(x.readyState=="complete")e.call(n,"lazy");else{if(x.createEventObject&&J.doScroll){try{C=!n.frameElement}catch(A){}C&&j()}x[t](i+"DOMContentLoaded",
+w,!1);x[t](i+"readystatechange",w,!1);n[t](i+"load",w,!1)}}function Q(){S&&X(function(){var e=K.length;$(e?function(){for(var j=0;j<e;++j)(function(e){P(function(){n.exports[K[e]].apply(n,arguments)},0)})(j)}:void 0)})}for(var n=window,P=n.setTimeout,x=document,J=x.documentElement,L=x.head||x.getElementsByTagName("head")[0]||J,z="",A=x.scripts,m=A.length;--m>=0;){var M=A[m],T=M.src.match(/^[^#?]*\/run_prettify\.js(\?[^#]*)?(?:#.*)?$/);if(T){z=T[1]||"";M.parentNode.removeChild(M);break}}var S=!0,D=
+[],N=[],K=[];z.replace(/[&?]([^&=]+)=([^&]+)/g,function(e,j,w){w=decodeURIComponent(w);j=decodeURIComponent(j);j=="autorun"?S=!/^[0fn]/i.test(w):j=="lang"?D.push(w):j=="skin"?N.push(w):j=="callback"&&K.push(w)});m=0;for(z=D.length;m<z;++m)(function(){var e=x.createElement("script");e.onload=e.onerror=e.onreadystatechange=function(){if(e&&(!e.readyState||/loaded|complete/.test(e.readyState)))e.onerror=e.onload=e.onreadystatechange=r,--R,R||P(Q,0),e.parentNode&&e.parentNode.removeChild(e),e=r};e.type=
+"text/javascript";e.src="https://google-code-prettify.googlecode.com/svn/loader/lang-"+encodeURIComponent(D[m])+".js";L.insertBefore(e,L.firstChild)})(D[m]);for(var R=D.length,A=[],m=0,z=N.length;m<z;++m)A.push("https://google-code-prettify.googlecode.com/svn/loader/skins/"+encodeURIComponent(N[m])+".css");A.push("https://google-code-prettify.googlecode.com/svn/loader/prettify.css");(function(e){function j(m){if(m!==w){var n=x.createElement("link");n.rel="stylesheet";n.type="text/css";if(m+1<w)n.error=
+n.onerror=function(){j(m+1)};n.href=e[m];L.appendChild(n)}}var w=e.length;j(0)})(A);var $=function(){window.PR_SHOULD_USE_CONTINUATION=!0;var e;(function(){function j(a){function d(f){var b=f.charCodeAt(0);if(b!==92)return b;var a=f.charAt(1);return(b=i[a])?b:"0"<=a&&a<="7"?parseInt(f.substring(1),8):a==="u"||a==="x"?parseInt(f.substring(2),16):f.charCodeAt(1)}function h(f){if(f<32)return(f<16?"\\x0":"\\x")+f.toString(16);f=String.fromCharCode(f);return f==="\\"||f==="-"||f==="]"||f==="^"?"\\"+f:
+f}function b(f){var b=f.substring(1,f.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),f=[],a=b[0]==="^",c=["["];a&&c.push("^");for(var a=a?1:0,g=b.length;a<g;++a){var k=b[a];if(/\\[bdsw]/i.test(k))c.push(k);else{var k=d(k),o;a+2<g&&"-"===b[a+1]?(o=d(b[a+2]),a+=2):o=k;f.push([k,o]);o<65||k>122||(o<65||k>90||f.push([Math.max(65,k)|32,Math.min(o,90)|32]),o<97||k>122||f.push([Math.max(97,k)&-33,Math.min(o,122)&-33]))}}f.sort(function(f,a){return f[0]-
+a[0]||a[1]-f[1]});b=[];g=[];for(a=0;a<f.length;++a)k=f[a],k[0]<=g[1]+1?g[1]=Math.max(g[1],k[1]):b.push(g=k);for(a=0;a<b.length;++a)k=b[a],c.push(h(k[0])),k[1]>k[0]&&(k[1]+1>k[0]&&c.push("-"),c.push(h(k[1])));c.push("]");return c.join("")}function e(f){for(var a=f.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),c=a.length,d=[],g=0,k=0;g<c;++g){var o=a[g];o==="("?++k:"\\"===o.charAt(0)&&(o=+o.substring(1))&&(o<=k?d[o]=-1:a[g]=h(o))}for(g=
+1;g<d.length;++g)-1===d[g]&&(d[g]=++j);for(k=g=0;g<c;++g)o=a[g],o==="("?(++k,d[k]||(a[g]="(?:")):"\\"===o.charAt(0)&&(o=+o.substring(1))&&o<=k&&(a[g]="\\"+d[o]);for(g=0;g<c;++g)"^"===a[g]&&"^"!==a[g+1]&&(a[g]="");if(f.ignoreCase&&F)for(g=0;g<c;++g)o=a[g],f=o.charAt(0),o.length>=2&&f==="["?a[g]=b(o):f!=="\\"&&(a[g]=o.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var j=0,F=!1,l=!1,I=0,c=a.length;I<c;++I){var p=a[I];if(p.ignoreCase)l=
+!0;else if(/[a-z]/i.test(p.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){F=!0;l=!1;break}}for(var i={b:8,t:9,n:10,v:11,f:12,r:13},q=[],I=0,c=a.length;I<c;++I){p=a[I];if(p.global||p.multiline)throw Error(""+p);q.push("(?:"+e(p)+")")}return RegExp(q.join("|"),l?"gi":"g")}function m(a,d){function h(a){var c=a.nodeType;if(c==1){if(!b.test(a.className)){for(c=a.firstChild;c;c=c.nextSibling)h(c);c=a.nodeName.toLowerCase();if("br"===c||"li"===c)e[l]="\n",F[l<<1]=j++,F[l++<<1|1]=a}}else if(c==
+3||c==4)c=a.nodeValue,c.length&&(c=d?c.replace(/\r\n?/g,"\n"):c.replace(/[\t\n\r ]+/g," "),e[l]=c,F[l<<1]=j,j+=c.length,F[l++<<1|1]=a)}var b=/(?:^|\s)nocode(?:\s|$)/,e=[],j=0,F=[],l=0;h(a);return{a:e.join("").replace(/\n$/,""),d:F}}function n(a,d,h,b){d&&(a={a:d,e:a},h(a),b.push.apply(b,a.g))}function x(a){for(var d=void 0,h=a.firstChild;h;h=h.nextSibling)var b=h.nodeType,d=b===1?d?a:h:b===3?S.test(h.nodeValue)?a:d:d;return d===a?void 0:d}function C(a,d){function h(a){for(var l=a.e,j=[l,"pln"],c=
+0,p=a.a.match(e)||[],m={},q=0,f=p.length;q<f;++q){var B=p[q],y=m[B],u=void 0,g;if(typeof y==="string")g=!1;else{var k=b[B.charAt(0)];if(k)u=B.match(k[1]),y=k[0];else{for(g=0;g<i;++g)if(k=d[g],u=B.match(k[1])){y=k[0];break}u||(y="pln")}if((g=y.length>=5&&"lang-"===y.substring(0,5))&&!(u&&typeof u[1]==="string"))g=!1,y="src";g||(m[B]=y)}k=c;c+=B.length;if(g){g=u[1];var o=B.indexOf(g),H=o+g.length;u[2]&&(H=B.length-u[2].length,o=H-g.length);y=y.substring(5);n(l+k,B.substring(0,o),h,j);n(l+k+o,g,A(y,
+g),j);n(l+k+H,B.substring(H),h,j)}else j.push(l+k,y)}a.g=j}var b={},e;(function(){for(var h=a.concat(d),l=[],i={},c=0,p=h.length;c<p;++c){var m=h[c],q=m[3];if(q)for(var f=q.length;--f>=0;)b[q.charAt(f)]=m;m=m[1];q=""+m;i.hasOwnProperty(q)||(l.push(m),i[q]=r)}l.push(/[\S\s]/);e=j(l)})();var i=d.length;return h}function t(a){var d=[],h=[];a.tripleQuotedStrings?d.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,
+r,"'\""]):a.multiLineStrings?d.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,r,"'\"`"]):d.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,r,"\"'"]);a.verbatimStrings&&h.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,r]);var b=a.hashComments;b&&(a.cStyleComments?(b>1?d.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,r,"#"]):d.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\n\r]*)/,
+r,"#"]),h.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,r])):d.push(["com",/^#[^\n\r]*/,r,"#"]));a.cStyleComments&&(h.push(["com",/^\/\/[^\n\r]*/,r]),h.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,r]));if(b=a.regexLiterals){var e=(b=b>1?"":"\n\r")?".":"[\\S\\s]";h.push(["lang-regex",RegExp("^(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+
+("/(?=[^/*"+b+"])(?:[^/\\x5B\\x5C"+b+"]|\\x5C"+e+"|\\x5B(?:[^\\x5C\\x5D"+b+"]|\\x5C"+e+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&h.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&h.push(["kwd",RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),r]);d.push(["pln",/^\s+/,r," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");h.push(["lit",/^@[$_a-z][\w$@]*/i,r],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,r],["pln",/^[$_a-z][\w$@]*/i,r],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,
+r,"0123456789"],["pln",/^\\[\S\s]?/,r],["pun",RegExp(b),r]);return C(d,h)}function z(a,d,h){function b(a){var c=a.nodeType;if(c==1&&!j.test(a.className))if("br"===a.nodeName)e(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((c==3||c==4)&&h){var d=a.nodeValue,i=d.match(m);if(i)c=d.substring(0,i.index),a.nodeValue=c,(d=d.substring(i.index+i[0].length))&&a.parentNode.insertBefore(l.createTextNode(d),a.nextSibling),e(a),c||a.parentNode.removeChild(a)}}
+function e(a){function b(a,c){var d=c?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),h=a.nextSibling;f.appendChild(d);for(var e=h;e;e=h)h=e.nextSibling,f.appendChild(e)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),d;(d=a.parentNode)&&d.nodeType===1;)a=d;c.push(a)}for(var j=/(?:^|\s)nocode(?:\s|$)/,m=/\r\n?|\n/,l=a.ownerDocument,i=l.createElement("li");a.firstChild;)i.appendChild(a.firstChild);for(var c=[i],p=0;p<c.length;++p)b(c[p]);d===(d|0)&&c[0].setAttribute("value",
+d);var n=l.createElement("ol");n.className="linenums";for(var d=Math.max(0,d-1|0)||0,p=0,q=c.length;p<q;++p)i=c[p],i.className="L"+(p+d)%10,i.firstChild||i.appendChild(l.createTextNode("\u00a0")),n.appendChild(i);a.appendChild(n)}function i(a,d){for(var h=d.length;--h>=0;){var b=d[h];U.hasOwnProperty(b)?V.console&&console.warn("cannot override language handler %s",b):U[b]=a}}function A(a,d){if(!a||!U.hasOwnProperty(a))a=/^\s*</.test(d)?"default-markup":"default-code";return U[a]}function D(a){var d=
+a.h;try{var h=m(a.c,a.i),b=h.a;a.a=b;a.d=h.d;a.e=0;A(d,b)(a);var e=/\bMSIE\s(\d+)/.exec(navigator.userAgent),e=e&&+e[1]<=8,d=/\n/g,i=a.a,j=i.length,h=0,l=a.d,n=l.length,b=0,c=a.g,p=c.length,t=0;c[p]=j;var q,f;for(f=q=0;f<p;)c[f]!==c[f+2]?(c[q++]=c[f++],c[q++]=c[f++]):f+=2;p=q;for(f=q=0;f<p;){for(var x=c[f],y=c[f+1],u=f+2;u+2<=p&&c[u+1]===y;)u+=2;c[q++]=x;c[q++]=y;f=u}c.length=q;var g=a.c,k;if(g)k=g.style.display,g.style.display="none";try{for(;b<n;){var o=l[b+2]||j,H=c[t+2]||j,u=Math.min(o,H),E=l[b+
+1],W;if(E.nodeType!==1&&(W=i.substring(h,u))){e&&(W=W.replace(d,"\r"));E.nodeValue=W;var Z=E.ownerDocument,s=Z.createElement("span");s.className=c[t+1];var z=E.parentNode;z.replaceChild(s,E);s.appendChild(E);h<o&&(l[b+1]=E=Z.createTextNode(i.substring(u,o)),z.insertBefore(E,s.nextSibling))}h=u;h>=o&&(b+=2);h>=H&&(t+=2)}}finally{if(g)g.style.display=k}}catch(v){V.console&&console.log(v&&v.stack||v)}}var V=window,G=["break,continue,do,else,for,if,return,while"],O=[[G,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
+"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],J=[O,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],K=[O,"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"],
+L=[K,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],O=[O,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],M=[G,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
+N=[G,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],R=[G,"as,assert,const,copy,drop,enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv,pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"],G=[G,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],Q=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/,
+S=/\S/,T=t({keywords:[J,L,O,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",M,N,G],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),U={};i(T,["default-code"]);i(C([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",
+/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);i(C([["pln",/^\s+/,r," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,r,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],
+["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);i(C([],[["atv",/^[\S\s]+/]]),["uq.val"]);i(t({keywords:J,hashComments:!0,cStyleComments:!0,types:Q}),["c","cc","cpp","cxx","cyc","m"]);i(t({keywords:"null,true,false"}),["json"]);i(t({keywords:L,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:Q}),
+["cs"]);i(t({keywords:K,cStyleComments:!0}),["java"]);i(t({keywords:G,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);i(t({keywords:M,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);i(t({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:2}),["perl","pl","pm"]);i(t({keywords:N,
+hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);i(t({keywords:O,cStyleComments:!0,regexLiterals:!0}),["javascript","js"]);i(t({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);i(t({keywords:R,cStyleComments:!0,multilineStrings:!0}),["rc","rs","rust"]);
+i(C([],[["str",/^[\S\s]+/]]),["regex"]);var X=V.PR={createSimpleLexer:C,registerLangHandler:i,sourceDecorator:t,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:function(a,d,e){var b=document.createElement("div");b.innerHTML="<pre>"+a+"</pre>";b=b.firstChild;e&&z(b,e,!0);D({h:d,j:e,c:b,i:1});return b.innerHTML},
+prettyPrint:e=e=function(a,d){function e(){for(var b=V.PR_SHOULD_USE_CONTINUATION?c.now()+250:Infinity;p<j.length&&c.now()<b;p++){for(var d=j[p],m=k,l=d;l=l.previousSibling;){var n=l.nodeType,s=(n===7||n===8)&&l.nodeValue;if(s?!/^\??prettify\b/.test(s):n!==3||/\S/.test(l.nodeValue))break;if(s){m={};s.replace(/\b(\w+)=([\w%+\-.:]+)/g,function(a,b,c){m[b]=c});break}}l=d.className;if((m!==k||f.test(l))&&!w.test(l)){n=!1;for(s=d.parentNode;s;s=s.parentNode)if(g.test(s.tagName)&&s.className&&f.test(s.className)){n=
+!0;break}if(!n){d.className+=" prettyprinted";n=m.lang;if(!n){var n=l.match(q),A;if(!n&&(A=x(d))&&u.test(A.tagName))n=A.className.match(q);n&&(n=n[1])}if(y.test(d.tagName))s=1;else var s=d.currentStyle,v=i.defaultView,s=(s=s?s.whiteSpace:v&&v.getComputedStyle?v.getComputedStyle(d,r).getPropertyValue("white-space"):0)&&"pre"===s.substring(0,3);v=m.linenums;if(!(v=v==="true"||+v))v=(v=l.match(/\blinenums\b(?::(\d+))?/))?v[1]&&v[1].length?+v[1]:!0:!1;v&&z(d,v,s);t={h:n,c:d,j:v,i:s};D(t)}}}p<j.length?
+P(e,250):"function"===typeof a&&a()}for(var b=d||document.body,i=b.ownerDocument||document,b=[b.getElementsByTagName("pre"),b.getElementsByTagName("code"),b.getElementsByTagName("xmp")],j=[],m=0;m<b.length;++m)for(var l=0,n=b[m].length;l<n;++l)j.push(b[m][l]);var b=r,c=Date;c.now||(c={now:function(){return+new Date}});var p=0,t,q=/\blang(?:uage)?-([\w.]+)(?!\S)/,f=/\bprettyprint\b/,w=/\bprettyprinted\b/,y=/pre|xmp/i,u=/^code$/i,g=/^(?:pre|code|xmp)$/i,k={};e()}};typeof define==="function"&&define.amd&&
+define("google-code-prettify",[],function(){return X})})();return e}();R||P(Q,0)})();}()
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/jquery.hotkeys.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/jquery.hotkeys.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/external/jquery.hotkeys.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,100 @@
+/*
+ * jQuery Hotkeys Plugin
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ *
+ * Based upon the plugin by Tzury Bar Yochay:
+ * http://github.com/tzuryby/hotkeys
+ *
+ * Original idea by:
+ * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
+*/
+
+(function(jQuery){
+
+ jQuery.hotkeys = {
+ version: "0.8",
+
+ specialKeys: {
+ 8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
+ 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
+ 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
+ 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
+ 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
+ 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
+ 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta"
+ },
+
+ shiftNums: {
+ "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
+ "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
+ ".": ">", "/": "?", "\\": "|"
+ }
+ };
+
+ function keyHandler( handleObj ) {
+ // Only care when a possible input has been specified
+ if ( typeof handleObj.data !== "string" ) {
+ return;
+ }
+
+ var origHandler = handleObj.handler,
+ keys = handleObj.data.toLowerCase().split(" "),
+ textAcceptingInputTypes = ["text", "password", "number", "email", "url", "range", "date", "month", "week", "time", "datetime", "datetime-local", "search", "color"];
+
+ handleObj.handler = function( event ) {
+ // Don't fire in text-accepting inputs that we didn't directly bind to
+ if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) ||
+ jQuery.inArray(event.target.type, textAcceptingInputTypes) > -1 ) ) {
+ return;
+ }
+
+ // Keypress represents characters, not special keys
+ var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ],
+ character = String.fromCharCode( event.which ).toLowerCase(),
+ key, modif = "", possible = {};
+
+ // check combinations (alt|ctrl|shift+anything)
+ if ( event.altKey && special !== "alt" ) {
+ modif += "alt+";
+ }
+
+ if ( event.ctrlKey && special !== "ctrl" ) {
+ modif += "ctrl+";
+ }
+
+ // TODO: Need to make sure this works consistently across platforms
+ if ( event.metaKey && !event.ctrlKey && special !== "meta" ) {
+ modif += "meta+";
+ }
+
+ if ( event.shiftKey && special !== "shift" ) {
+ modif += "shift+";
+ }
+
+ if ( special ) {
+ possible[ modif + special ] = true;
+
+ } else {
+ possible[ modif + character ] = true;
+ possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true;
+
+ // "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
+ if ( modif === "shift+" ) {
+ possible[ jQuery.hotkeys.shiftNums[ character ] ] = true;
+ }
+ }
+
+ for ( var i = 0, l = keys.length; i < l; i++ ) {
+ if ( possible[ keys[i] ] ) {
+ return origHandler.apply( this, arguments );
+ }
+ }
+ };
+ }
+
+ jQuery.each([ "keydown", "keyup", "keypress" ], function() {
+ jQuery.event.special[ this ] = { add: keyHandler };
+ });
+
+})( jQuery );
\ No newline at end of file
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/index.css
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/index.css (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/index.css 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,42 @@
+/* this CSS is not part of the widget, it is here just as an example of the demo page styling.... Don't copy this one, roll your own. One
+ * of the key things about the widget is that it allows you to do your own styling!
+ */
+
+#editor {
+ max-height: 250px;
+ height: 250px;
+ background-color: white;
+ border-collapse: separate;
+ border: 1px solid rgb(204, 204, 204);
+ padding: 4px;
+ box-sizing: content-box;
+ -webkit-box-shadow: rgba(0, 0, 0, 0.0745098) 0px 1px 1px 0px inset;
+ box-shadow: rgba(0, 0, 0, 0.0745098) 0px 1px 1px 0px inset;
+ border-top-right-radius: 3px; border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px; border-top-left-radius: 3px;
+ overflow: scroll;
+ outline: none;
+}
+#voiceBtn {
+ width: 20px;
+ color: transparent;
+ background-color: transparent;
+ transform: scale(2.0, 2.0);
+ -webkit-transform: scale(2.0, 2.0);
+ -moz-transform: scale(2.0, 2.0);
+ border: transparent;
+ cursor: pointer;
+ box-shadow: none;
+ -webkit-box-shadow: none;
+}
+
+div[data-role="editor-toolbar"] {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.dropdown-menu a {
+ cursor: pointer;
+}
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/index.html
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/index.html (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/index.html 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,252 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Tiny, opensource, Bootstrap WYSIWYG rich text editor from MindMup</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta name="keywords" content="opensource rich wysiwyg text editor jquery bootstrap execCommand html5" />
+ <meta name="description" content="This tiny jQuery Bootstrap WYSIWYG plugin turns any DIV into a HTML5 rich text editor" />
+ <link rel="apple-touch-icon" href="//mindmup.s3.amazonaws.com/lib/img/apple-touch-icon.png" />
+ <link rel="shortcut icon" href="http://mindmup.s3.amazonaws.com/lib/img/favicon.ico" >
+ <link href="external/google-code-prettify/prettify.css" rel="stylesheet">
+ <link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combin…" rel="stylesheet">
+ <link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-respon…" rel="stylesheet">
+ <link href="http://netdna.bootstrapcdn.com/font-awesome/3.0.2/css/font-awesome.css" rel="stylesheet">
+ <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
+ <script src="external/jquery.hotkeys.js"></script>
+ <script src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/js/bootstrap.min.js"></script>
+ <script src="external/google-code-prettify/prettify.js"></script>
+ <link href="index.css" rel="stylesheet">
+ <script src="bootstrap-wysiwyg.js"></script>
+ </head>
+ <body>
+
+<div class="container">
+ <div class="hero-unit">
+ <div class="pull-right">
+ <div class="fb-like" data-href="http://facebook.com/mindmupapp" data-send="false" data-layout="button_count" data-width="100" data-show-faces="false"></div><br/>
+ <a href="https://twitter.com/mindmup" class="twitter-follow-button" data-show-count="true" data-show-screen-name="true" data-lang="en">Follow @mindmup</a>
+ </div>
+ <h1>bootstrap-wysiwyg <br/> <small>tiny wysiwyg rich text editor for Bootstrap</small></h1>
+ <hr/>
+ <!---
+ Please read this before copying the toolbar:
+
+ * One of the best things about this widget is that it does not impose any styling on you, and that it allows you
+ * to create a custom toolbar with the options and functions that are good for your particular use. This toolbar
+ * is just an example - don't just copy it and force yourself to use the demo styling. Create your own. Read
+ * this page to understand how to customise it:
+ * https://github.com/mindmup/bootstrap-wysiwyg/blob/master/README.md#customis…
+ --->
+ <div id="alerts"></div>
+ <div class="btn-toolbar" data-role="editor-toolbar" data-target="#editor">
+ <div class="btn-group">
+ <a class="btn dropdown-toggle" data-toggle="dropdown" title="Font"><i class="icon-font"></i><b class="caret"></b></a>
+ <ul class="dropdown-menu">
+ </ul>
+ </div>
+ <div class="btn-group">
+ <a class="btn dropdown-toggle" data-toggle="dropdown" title="Font Size"><i class="icon-text-height"></i> <b class="caret"></b></a>
+ <ul class="dropdown-menu">
+ <li><a data-edit="fontSize 5"><font size="5">Huge</font></a></li>
+ <li><a data-edit="fontSize 3"><font size="3">Normal</font></a></li>
+ <li><a data-edit="fontSize 1"><font size="1">Small</font></a></li>
+ </ul>
+ </div>
+ <div class="btn-group">
+ <a class="btn" data-edit="bold" title="Bold (Ctrl/Cmd+B)"><i class="icon-bold"></i></a>
+ <a class="btn" data-edit="italic" title="Italic (Ctrl/Cmd+I)"><i class="icon-italic"></i></a>
+ <a class="btn" data-edit="strikethrough" title="Strikethrough"><i class="icon-strikethrough"></i></a>
+ <a class="btn" data-edit="underline" title="Underline (Ctrl/Cmd+U)"><i class="icon-underline"></i></a>
+ </div>
+ <div class="btn-group">
+ <a class="btn" data-edit="insertunorderedlist" title="Bullet list"><i class="icon-list-ul"></i></a>
+ <a class="btn" data-edit="insertorderedlist" title="Number list"><i class="icon-list-ol"></i></a>
+ <a class="btn" data-edit="outdent" title="Reduce indent (Shift+Tab)"><i class="icon-indent-left"></i></a>
+ <a class="btn" data-edit="indent" title="Indent (Tab)"><i class="icon-indent-right"></i></a>
+ </div>
+ <div class="btn-group">
+ <a class="btn" data-edit="justifyleft" title="Align Left (Ctrl/Cmd+L)"><i class="icon-align-left"></i></a>
+ <a class="btn" data-edit="justifycenter" title="Center (Ctrl/Cmd+E)"><i class="icon-align-center"></i></a>
+ <a class="btn" data-edit="justifyright" title="Align Right (Ctrl/Cmd+R)"><i class="icon-align-right"></i></a>
+ <a class="btn" data-edit="justifyfull" title="Justify (Ctrl/Cmd+J)"><i class="icon-align-justify"></i></a>
+ </div>
+ <div class="btn-group">
+ <a class="btn dropdown-toggle" data-toggle="dropdown" title="Hyperlink"><i class="icon-link"></i></a>
+ <div class="dropdown-menu input-append">
+ <input class="span2" placeholder="URL" type="text" data-edit="createLink"/>
+ <button class="btn" type="button">Add</button>
+ </div>
+ <a class="btn" data-edit="unlink" title="Remove Hyperlink"><i class="icon-cut"></i></a>
+
+ </div>
+
+ <div class="btn-group">
+ <a class="btn" title="Insert picture (or just drag & drop)" id="pictureBtn"><i class="icon-picture"></i></a>
+ <input type="file" data-role="magic-overlay" data-target="#pictureBtn" data-edit="insertImage" />
+ </div>
+ <div class="btn-group">
+ <a class="btn" data-edit="undo" title="Undo (Ctrl/Cmd+Z)"><i class="icon-undo"></i></a>
+ <a class="btn" data-edit="redo" title="Redo (Ctrl/Cmd+Y)"><i class="icon-repeat"></i></a>
+ </div>
+ <input type="text" data-edit="inserttext" id="voiceBtn" x-webkit-speech="">
+ </div>
+
+ <div id="editor">
+ Go ahead…
+ </div>
+ </div>
+
+<div class="row">
+ <div class="span6">
+ <h2>About</h2>
+ <p>
+ This <a href="https://github.com/mindmup/bootstrap-wysiwyg/blob/master/bootstrap-wysiwyg.…">tiny (5KB, < 200 lines) jQuery Bootstrap plugin</a> turns any DIV into a WYSIWYG
+ rich-content editor, inspired by <a href="http://premiumsoftware.net/cleditor/">CLEditor</a> and
+ <a href="http://jhollingworth.github.com/bootstrap-wysihtml5/">bootstrap-wysihtml5</a>.
+ Here are the key features:
+ </p>
+ <ul>
+ <li>Automatically binds standard hotkeys for common operations on Mac and Windows</li>
+ <li>Drag and drop files to insert images, support image upload (also taking photos on mobile devices)</li>
+ <li>Voice dictation input (only in Chrome)</li>
+ <li>Allows a custom built toolbar, no magic markup generators, enabling the web site to use all the goodness of Bootstrap, Font Awesome and so on...</li>
+ <li>Does not force any styling - it's all up to you</li>
+ <li>Uses standard browser features, no magic non-standard code, toolbar and keyboard configurable to execute any <a href="https://developer.mozilla.org/en/docs/Rich-Text_Editing_in_Mozilla">supported browser command</a></li>
+ <li>Does not create a separate frame, backup text areas etc - instead keeps it simple and runs everything inline in a DIV</li>
+ <li>(Optionally) cleans up trailing whitespace and empty divs and spans</li>
+ <li>Requires a modern browser (tested in Chrome 26, Firefox 19, Safari 6, reported by users to work in IE10)</li>
+ <li>Supports mobile devices (tested on IOS 6 Ipad/Iphone and Android 4.1.1 Chrome)</li>
+ </ul>
+
+
+ <h2>Why?</h2>
+
+ <a href="http://www.mindmup.com"><img class="span5" src="promo-868x350.png"></a>
+ <br clear="all" />
+ <p>
+ While building a content editor for <a href="http://www.mindmup.com">MindMup</a> we found plenty of good HTML5 WYSWYG editors online, but most were duplicating
+ functionality that already exists in jQuery and Bootstrap, implementing things that were meanwhile added to HTML5 and supported in major browsers,
+ or doing too much magic such as inserting IFrames with backup text-areas. Most of this was to work around quirks in older browsers (which we didn't need) and
+ caused focus problems on touch devices (which was an issue for us). Too much magic caused problems with
+ bootstrap modals (a must for us). Most editors also build their own toolbars, which embed additional CSS and images and always
+ turn out to be much worse than standard Bootstrap buttons. </p>
+ <p>It turns out, with HTML5, everything we need can fit into less than 5K. We built this tiny tiny editor that does everything we need, does not impose
+ any particular additional CSS and uses modern browser functionality without reimplementing jQuery and Bootstrap. </p>
+ <p>It's released under the MIT license, so fork and enjoy! </p>
+
+
+
+ </div>
+ <div class="span6" >
+ <h2>Usage</h2>
+ <pre class="prettyprint linenums">$('#editor').wysiwyg();</pre>
+<p>Don't forget to style your editor div:</p>
+<pre class="prettyprint linenums">
+ #editor {overflow:scroll; max-height:300px}
+</pre>
+<p>If you want to use this for a mobile web site, make sure to read about
+<a href="https://github.com/mindmup/bootstrap-wysiwyg#styling-for-mobile-devices">how to style it</a> to optimise mobile screen usage and experience (please note that this
+demo page isn't optimised for mobile access).</p>
+
+ <p>Optionally, also create a toolbar (see the source of this page for an example):</p>
+<pre class="prettyprint linenums">
+<div class="btn-toolbar" data-role="editor-toolbar"
+ data-target="#editor">
+ ...
+</div>
+</pre>
+<p>In the toolbar, execute simple commands by adding a data-edit attribute to a link.<p>
+<pre class="prettyprint linenums">
+ <a data-edit="bold">...</a>
+</pre>
+<p>execute more complex commands by adding an argument after a blank or providing an input with a data-edit command (the input value is used as an argument). In case of
+file inputs, the file contents are read in using the FileReader API and used as the command value.<p>
+<pre class="prettyprint linenums">
+<a data-edit="fontName Arial">...</a>
+...
+<input type="text" data-edit="createLink"/>
+...
+<input type="file" data-edit="insertImage" />
+</pre>
+<p>Use standard jQuery methods to access and set content and focus. You can also ask for cleaned up HTML content:</p>
+<pre class="prettyprint linenums">
+ $('#editor').cleanHtml()
+</pre>
+
+ <p style="text-align:center;">
+ <a class="btn btn-large btn-primary jumbo" href="https://github.com/mindmup/bootstrap-wysiwyg/">View project on Github</a>
+ </p>
+ <p style="text-align:center; margin-top:20px">
+ <a class="btn" href="mailto:contact@mindmup.com"><i class="icon-envelope"></i></a>
+ <a class="btn" href="http://facebook.com/mindmupapp"><i class="icon-facebook"></i></a>
+ <a class="btn" href="http://twitter.com/mindmup"><i class="icon-twitter"></i></a>
+ <a class="btn" href="https://plus.google.com/u/0/communities/112831595986131146219"><i class="icon-google-plus"></i></a>
+ </p>
+
+
+ </div>
+ </div>
+</div>
+<script>
+ $(function(){
+ function initToolbarBootstrapBindings() {
+ var fonts = ['Serif', 'Sans', 'Arial', 'Arial Black', 'Courier',
+ 'Courier New', 'Comic Sans MS', 'Helvetica', 'Impact', 'Lucida Grande', 'Lucida Sans', 'Tahoma', 'Times',
+ 'Times New Roman', 'Verdana'],
+ fontTarget = $('[title=Font]').siblings('.dropdown-menu');
+ $.each(fonts, function (idx, fontName) {
+ fontTarget.append($('<li><a data-edit="fontName ' + fontName +'" style="font-family:\''+ fontName +'\'">'+fontName + '</a></li>'));
+ });
+ $('a[title]').tooltip({container:'body'});
+ $('.dropdown-menu input').click(function() {return false;})
+ .change(function () {$(this).parent('.dropdown-menu').siblings('.dropdown-toggle').dropdown('toggle');})
+ .keydown('esc', function () {this.value='';$(this).change();});
+
+ $('[data-role=magic-overlay]').each(function () {
+ var overlay = $(this), target = $(overlay.data('target'));
+ overlay.css('opacity', 0).css('position', 'absolute').offset(target.offset()).width(target.outerWidth()).height(target.outerHeight());
+ });
+ if ("onwebkitspeechchange" in document.createElement("input")) {
+ var editorOffset = $('#editor').offset();
+ $('#voiceBtn').css('position','absolute').offset({top: editorOffset.top, left: editorOffset.left+$('#editor').innerWidth()-35});
+ } else {
+ $('#voiceBtn').hide();
+ }
+ };
+ function showErrorAlert (reason, detail) {
+ var msg='';
+ if (reason==='unsupported-file-type') { msg = "Unsupported format " +detail; }
+ else {
+ console.log("error uploading file", reason, detail);
+ }
+ $('<div class="alert"> <button type="button" class="close" data-dismiss="alert">×</button>'+
+ '<strong>File upload error</strong> '+msg+' </div>').prependTo('#alerts');
+ };
+ initToolbarBootstrapBindings();
+ $('#editor').wysiwyg({ fileUploadError: showErrorAlert} );
+ window.prettyPrint && prettyPrint();
+ });
+</script>
+<a href="https://github.com/mindmup/bootstrap-wysiwyg/"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png" alt="Fork me on GitHub"></a>
+<div id="fb-root"></div>
+<script>
+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+ })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+ ga('create', 'UA-37452180-6', 'github.io');
+ ga('send', 'pageview');
+</script>
+<script>(function(d, s, id) {
+ var js, fjs = d.getElementsByTagName(s)[0];
+ if (d.getElementById(id)) return;
+ js = d.createElement(s); js.id = id;
+ js.src = "http://connect.facebook.net/en_GB/all.js#xfbml=1";
+ fjs.parentNode.insertBefore(js, fjs);
+ }(document, 'script', 'facebook-jssdk'));
+ </script>
+
+<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="http://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
+
+</html>
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/promo-868x350.png
===================================================================
(Binary files differ)
Property changes on: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/promo-868x350.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/republish.sh
===================================================================
--- trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/republish.sh (rev 0)
+++ trunk/wao-web/src/main/webapp/js/bootstrap-wysiwyg/republish.sh 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1 @@
+git checkout gh-pages && git merge master && git checkout master && git push
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/.gitignore
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/.gitignore (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/.gitignore 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,3 @@
+node_modules
+.idea
+.grunt
\ No newline at end of file
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/.travis.yml
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/.travis.yml (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/.travis.yml 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,5 @@
+language: node_js
+node_js:
+ - "0.10"
+before_script:
+ - npm install -g grunt-cli
\ No newline at end of file
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/Gruntfile.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/Gruntfile.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/Gruntfile.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,47 @@
+module.exports = function(grunt) {
+
+ // Configuration.
+ grunt.initConfig({
+ jshint: {
+ options: {
+ "undef": true,
+ "unused": true
+ },
+
+ files: 'jquery.hotkeys.js'
+ },
+ jasmine: {
+ pivotal: {
+ src: 'jquery.hotkeys.js',
+ options: {
+ vendor: ['jquery-1.4.2.js', 'test/lib/**.js'],
+ outfile: 'test/SpecRunner.html',
+ keepRunner: true,
+ specs: 'test/spec/*Spec.js'
+ }
+ }
+ },
+ watch: {
+ scripts: {
+ files: ['**/*.js'],
+ tasks: ['default'],
+ options: {
+ spawn: false,
+ interrupt: true,
+ debounceDelay: 1000
+ }
+ }
+ }
+ });
+
+ grunt.loadNpmTasks('grunt-contrib-watch');
+
+ // Task loading.
+ grunt.loadNpmTasks("grunt-contrib-jshint");
+
+ // tests
+ grunt.loadNpmTasks('grunt-contrib-jasmine');
+
+ // Task registration.
+ grunt.registerTask("default", ["jshint", "jasmine"]);
+};
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/README.md
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/README.md (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/README.md 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,68 @@
+# jQuery.Hotkeys [](http://tr…
+
+#About
+**jQuery Hotkeys** is a plug-in that lets you easily add and remove handlers for keyboard events anywhere in your code supporting almost any key combination.
+
+This plugin is based off of the plugin by Tzury Bar Yochay: [jQuery.hotkeys](https://github.com/tzuryby/jquery.hotkeys)
+
+The syntax is as follows:
+
+ $(expression).bind(types, keys, handler);
+ $(expression).unbind(types, handler);
+
+ $(document).bind('keydown', 'ctrl+a', fn);
+
+ // e.g. replace '$' sign with 'EUR'
+ $('input.foo').bind('keyup', '$', function(){
+ this.value = this.value.replace('$', 'EUR');
+ });
+
+Syntax when wanting to use jQuery's `on()`/`off` methods:
+
+ $(expression).on(types, null, keys, handler);
+ $(expression).off(types, handler);
+
+ $(document).on('keydown', null, 'ctrl+a', fn);
+
+ // e.g. replace '$' sign with 'EUR'
+ $('input.foo').on('keyup', null, '$', function(){
+ this.value = this.value.replace('$', 'EUR');
+ });
+
+## Types
+
+Supported types are `'keydown'`, `'keyup'` and `'keypress'`
+
+## Example
+
+[Example](http://htmlpreview.github.com/?https://github.com/jeresig/jquery.hotkeys/master/test-static-05.html)
+
+## Notes
+
+Modifiers are not case sensitive (Ctrl == ctrl == cTRL)
+
+If you want to use more than one modifiers (e.g. alt+ctrl+z) you should define them by an alphabetical order e.g. alt+ctrl+shift
+
+Hotkeys aren't tracked if you're inside of an input element (unless you explicitly bind the hotkey directly to the input). This helps to avoid conflict with normal user typing.
+
+You can use namespacing by adding a suffix to the event type (e.g. 'keyup.toggle')
+
+## jQuery Compatibility
+
+Works with jQuery 1.4.2 and newer.
+
+It is known to be working with all the major browsers on all available platforms (Win/Mac/Linux)
+
+ * IE 6/7/8+
+ * FF 1.5/2/3+
+ * Opera-9+
+ * Safari-3+
+ * Chrome-0.2+
+
+### Addendum
+
+Firefox is the most liberal one in the manner of letting you capture all short-cuts even those that are built-in in the browser such as `Ctrl-t` for new tab, or `Ctrl-a` for selecting all text. You can always bubble them up to the browser by returning `true` in your handler.
+
+Others, (IE) either let you handle built-in short-cuts, but will add their functionality after your code has executed. Or (Opera/Safari) will *not* pass those events to the DOM at all.
+
+*So, if you bind `Ctrl-Q` or `Alt-F4` and your Safari/Opera window is closed don't be surprised.*
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/hotkeys.jquery.json
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/hotkeys.jquery.json (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/hotkeys.jquery.json 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,36 @@
+{
+ "name": "hotkeys",
+ "title": "jQuery Hotkeys",
+ "description": "jQuery Hotkeys lets you watch for keyboard events anywhere in your code supporting almost any key combination.",
+ "keywords": [
+ "keyboard",
+ "events"
+ ],
+ "version": "0.1.0",
+ "author": {
+ "name": "John Resig",
+ "url": "https://ejohn.org/"
+ },
+ "maintainers": [
+ {
+ "name": "John Resig",
+ "email": "jeresig(a)gmail.com",
+ "url": "http://ejohn.org/"
+ }
+ ],
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "http://opensource.org/licenses/MIT"
+ },
+ {
+ "type": "GPLv2",
+ "url": "http://www.gnu.org/licenses/gpl-2.0.html"
+ }
+ ],
+ "homepage": "https://github.com/jeresig/jquery.hotkeys",
+ "docs": "https://github.com/jeresig/jquery.hotkeys",
+ "dependencies": {
+ "jquery": ">=1.4.2"
+ }
+}
\ No newline at end of file
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/jquery-1.4.2.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/jquery-1.4.2.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/jquery-1.4.2.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,6240 @@
+/*!
+ * jQuery JavaScript Library v1.4.2
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Sat Feb 13 22:33:48 2010 -0500
+ */
+(function( window, undefined ) {
+
+// Define a local copy of jQuery
+var jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context );
+ },
+
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+
+ // Map over the $ in case of overwrite
+ _$ = window.$,
+
+ // Use the correct document accordingly with window argument (sandbox)
+ document = window.document,
+
+ // A central reference to the root jQuery(document)
+ rootjQuery,
+
+ // A simple way to check for HTML strings or ID strings
+ // (both of which we optimize for)
+ quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,
+
+ // Is it a simple selector
+ isSimple = /^.[^:#\[\.,]*$/,
+
+ // Check if a string has a non-whitespace character in it
+ rnotwhite = /\S/,
+
+ // Used for trimming whitespace
+ rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g,
+
+ // Match a standalone tag
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+
+ // Keep a UserAgent string for use with jQuery.browser
+ userAgent = navigator.userAgent,
+
+ // For matching the engine and version of the browser
+ browserMatch,
+
+ // Has the ready events already been bound?
+ readyBound = false,
+
+ // The functions to execute on DOM ready
+ readyList = [],
+
+ // The ready event handler
+ DOMContentLoaded,
+
+ // Save a reference to some core methods
+ toString = Object.prototype.toString,
+ hasOwnProperty = Object.prototype.hasOwnProperty,
+ push = Array.prototype.push,
+ slice = Array.prototype.slice,
+ indexOf = Array.prototype.indexOf;
+
+jQuery.fn = jQuery.prototype = {
+ init: function( selector, context ) {
+ var match, elem, ret, doc;
+
+ // Handle $(""), $(null), or $(undefined)
+ if ( !selector ) {
+ return this;
+ }
+
+ // Handle $(DOMElement)
+ if ( selector.nodeType ) {
+ this.context = this[0] = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // The body element only exists once, optimize finding it
+ if ( selector === "body" && !context ) {
+ this.context = document;
+ this[0] = document.body;
+ this.selector = "body";
+ this.length = 1;
+ return this;
+ }
+
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ // Are we dealing with HTML string or an ID?
+ match = quickExpr.exec( selector );
+
+ // Verify a match, and that no context was specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] ) {
+ doc = (context ? context.ownerDocument || context : document);
+
+ // If a single string is passed in and it's a single tag
+ // just do a createElement and skip the rest
+ ret = rsingleTag.exec( selector );
+
+ if ( ret ) {
+ if ( jQuery.isPlainObject( context ) ) {
+ selector = [ document.createElement( ret[1] ) ];
+ jQuery.fn.attr.call( selector, context, true );
+
+ } else {
+ selector = [ doc.createElement( ret[1] ) ];
+ }
+
+ } else {
+ ret = buildFragment( [ match[1] ], [ doc ] );
+ selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
+ }
+
+ return jQuery.merge( this, selector );
+
+ // HANDLE: $("#id")
+ } else {
+ elem = document.getElementById( match[2] );
+
+ if ( elem ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id !== match[2] ) {
+ return rootjQuery.find( selector );
+ }
+
+ // Otherwise, we inject the element directly into the jQuery object
+ this.length = 1;
+ this[0] = elem;
+ }
+
+ this.context = document;
+ this.selector = selector;
+ return this;
+ }
+
+ // HANDLE: $("TAG")
+ } else if ( !context && /^\w+$/.test( selector ) ) {
+ this.selector = selector;
+ this.context = document;
+ selector = document.getElementsByTagName( selector );
+ return jQuery.merge( this, selector );
+
+ // HANDLE: $(expr, $(...))
+ } else if ( !context || context.jquery ) {
+ return (context || rootjQuery).find( selector );
+
+ // HANDLE: $(expr, context)
+ // (which is just equivalent to: $(context).find(expr)
+ } else {
+ return jQuery( context ).find( selector );
+ }
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) ) {
+ return rootjQuery.ready( selector );
+ }
+
+ if (selector.selector !== undefined) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return jQuery.makeArray( selector, this );
+ },
+
+ // Start with an empty selector
+ selector: "",
+
+ // The current version of jQuery being used
+ jquery: "1.4.2",
+
+ // The default length of a jQuery object is 0
+ length: 0,
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ toArray: function() {
+ return slice.call( this, 0 );
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num == null ?
+
+ // Return a 'clean' array
+ this.toArray() :
+
+ // Return just the object
+ ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] );
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems, name, selector ) {
+ // Build a new jQuery matched element set
+ var ret = jQuery();
+
+ if ( jQuery.isArray( elems ) ) {
+ push.apply( ret, elems );
+
+ } else {
+ jQuery.merge( ret, elems );
+ }
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+
+ ret.context = this.context;
+
+ if ( name === "find" ) {
+ ret.selector = this.selector + (this.selector ? " " : "") + selector;
+ } else if ( name ) {
+ ret.selector = this.selector + "." + name + "(" + selector + ")";
+ }
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ ready: function( fn ) {
+ // Attach the listeners
+ jQuery.bindReady();
+
+ // If the DOM is already ready
+ if ( jQuery.isReady ) {
+ // Execute the function immediately
+ fn.call( document, jQuery );
+
+ // Otherwise, remember the function for later
+ } else if ( readyList ) {
+ // Add the function to the wait list
+ readyList.push( fn );
+ }
+
+ return this;
+ },
+
+ eq: function( i ) {
+ return i === -1 ?
+ this.slice( i ) :
+ this.slice( i, +i + 1 );
+ },
+
+ first: function() {
+ return this.eq( 0 );
+ },
+
+ last: function() {
+ return this.eq( -1 );
+ },
+
+ slice: function() {
+ return this.pushStack( slice.apply( this, arguments ),
+ "slice", slice.call(arguments).join(",") );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ end: function() {
+ return this.prevObject || jQuery(null);
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: push,
+ sort: [].sort,
+ splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+ // copy reference to target object
+ var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+ target = {};
+ }
+
+ // extend jQuery itself if only one argument is passed
+ if ( length === i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ ) {
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null ) {
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging object literal values or arrays
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) {
+ var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src
+ : jQuery.isArray(copy) ? [] : {};
+
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ window.$ = _$;
+
+ if ( deep ) {
+ window.jQuery = _jQuery;
+ }
+
+ return jQuery;
+ },
+
+ // Is the DOM ready to be used? Set to true once it occurs.
+ isReady: false,
+
+ // Handle when the DOM is ready
+ ready: function() {
+ // Make sure that the DOM is not already loaded
+ if ( !jQuery.isReady ) {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( !document.body ) {
+ return setTimeout( jQuery.ready, 13 );
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If there are functions bound, to execute
+ if ( readyList ) {
+ // Execute all of them
+ var fn, i = 0;
+ while ( (fn = readyList[ i++ ]) ) {
+ fn.call( document, jQuery );
+ }
+
+ // Reset the list of functions
+ readyList = null;
+ }
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.triggerHandler ) {
+ jQuery( document ).triggerHandler( "ready" );
+ }
+ }
+ },
+
+ bindReady: function() {
+ if ( readyBound ) {
+ return;
+ }
+
+ readyBound = true;
+
+ // Catch cases where $(document).ready() is called after the
+ // browser event has already occurred.
+ if ( document.readyState === "complete" ) {
+ return jQuery.ready();
+ }
+
+ // Mozilla, Opera and webkit nightlies currently support this event
+ if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", jQuery.ready, false );
+
+ // If IE event model is used
+ } else if ( document.attachEvent ) {
+ // ensure firing before onload,
+ // maybe late but safe also for iframes
+ document.attachEvent("onreadystatechange", DOMContentLoaded);
+
+ // A fallback to window.onload, that will always work
+ window.attachEvent( "onload", jQuery.ready );
+
+ // If IE and not a frame
+ // continually check to see if the document is ready
+ var toplevel = false;
+
+ try {
+ toplevel = window.frameElement == null;
+ } catch(e) {}
+
+ if ( document.documentElement.doScroll && toplevel ) {
+ doScrollCheck();
+ }
+ }
+ },
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return toString.call(obj) === "[object Function]";
+ },
+
+ isArray: function( obj ) {
+ return toString.call(obj) === "[object Array]";
+ },
+
+ isPlainObject: function( obj ) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) {
+ return false;
+ }
+
+ // Not own constructor property must be Object
+ if ( obj.constructor
+ && !hasOwnProperty.call(obj, "constructor")
+ && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) {
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for ( key in obj ) {}
+
+ return key === undefined || hasOwnProperty.call( obj, key );
+ },
+
+ isEmptyObject: function( obj ) {
+ for ( var name in obj ) {
+ return false;
+ }
+ return true;
+ },
+
+ error: function( msg ) {
+ throw msg;
+ },
+
+ parseJSON: function( data ) {
+ if ( typeof data !== "string" || !data ) {
+ return null;
+ }
+
+ // Make sure leading/trailing whitespace is removed (IE can't handle it)
+ data = jQuery.trim( data );
+
+ // Make sure the incoming data is actual JSON
+ // Logic borrowed from http://json.org/json2.js
+ if ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
+ .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
+ .replace(/(?:^|:|,)(?:\s*\[)+/g, "")) ) {
+
+ // Try to use the native JSON parser first
+ return window.JSON && window.JSON.parse ?
+ window.JSON.parse( data ) :
+ (new Function("return " + data))();
+
+ } else {
+ jQuery.error( "Invalid JSON: " + data );
+ }
+ },
+
+ noop: function() {},
+
+ // Evalulates a script in a global context
+ globalEval: function( data ) {
+ if ( data && rnotwhite.test(data) ) {
+ // Inspired by code by Andrea Giammarchi
+ // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.h…
+ var head = document.getElementsByTagName("head")[0] || document.documentElement,
+ script = document.createElement("script");
+
+ script.type = "text/javascript";
+
+ if ( jQuery.support.scriptEval ) {
+ script.appendChild( document.createTextNode( data ) );
+ } else {
+ script.text = data;
+ }
+
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709).
+ head.insertBefore( script, head.firstChild );
+ head.removeChild( script );
+ }
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
+ },
+
+ // args is for internal usage only
+ each: function( object, callback, args ) {
+ var name, i = 0,
+ length = object.length,
+ isObj = length === undefined || jQuery.isFunction(object);
+
+ if ( args ) {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.apply( object[ name ], args ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.apply( object[ i++ ], args ) === false ) {
+ break;
+ }
+ }
+ }
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( var value = object[0];
+ i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {}
+ }
+ }
+
+ return object;
+ },
+
+ trim: function( text ) {
+ return (text || "").replace( rtrim, "" );
+ },
+
+ // results is for internal usage only
+ makeArray: function( array, results ) {
+ var ret = results || [];
+
+ if ( array != null ) {
+ // The window, strings (and functions) also have 'length'
+ // The extra typeof function check is to prevent crashes
+ // in Safari 2 (See: #3039)
+ if ( array.length == null || typeof array === "string" || jQuery.isFunction(array) || (typeof array !== "function" && array.setInterval) ) {
+ push.call( ret, array );
+ } else {
+ jQuery.merge( ret, array );
+ }
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, array ) {
+ if ( array.indexOf ) {
+ return array.indexOf( elem );
+ }
+
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ if ( array[ i ] === elem ) {
+ return i;
+ }
+ }
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ var i = first.length, j = 0;
+
+ if ( typeof second.length === "number" ) {
+ for ( var l = second.length; j < l; j++ ) {
+ first[ i++ ] = second[ j ];
+ }
+
+ } else {
+ while ( second[j] !== undefined ) {
+ first[ i++ ] = second[ j++ ];
+ }
+ }
+
+ first.length = i;
+
+ return first;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var ret = [];
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ if ( !inv !== !callback( elems[ i ], i ) ) {
+ ret.push( elems[ i ] );
+ }
+ }
+
+ return ret;
+ },
+
+ // arg is for internal usage only
+ map: function( elems, callback, arg ) {
+ var ret = [], value;
+
+ // Go through the array, translating each of the items to their
+ // new value (or values).
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+
+ return ret.concat.apply( [], ret );
+ },
+
+ // A global GUID counter for objects
+ guid: 1,
+
+ proxy: function( fn, proxy, thisObject ) {
+ if ( arguments.length === 2 ) {
+ if ( typeof proxy === "string" ) {
+ thisObject = fn;
+ fn = thisObject[ proxy ];
+ proxy = undefined;
+
+ } else if ( proxy && !jQuery.isFunction( proxy ) ) {
+ thisObject = proxy;
+ proxy = undefined;
+ }
+ }
+
+ if ( !proxy && fn ) {
+ proxy = function() {
+ return fn.apply( thisObject || this, arguments );
+ };
+ }
+
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ if ( fn ) {
+ proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
+ }
+
+ // So proxy can be declared as an argument
+ return proxy;
+ },
+
+ // Use of jQuery.browser is frowned upon.
+ // More details: http://docs.jquery.com/Utilities/jQuery.browser
+ uaMatch: function( ua ) {
+ ua = ua.toLowerCase();
+
+ var match = /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
+ /(opera)(?:.*version)?[ \/]([\w.]+)/.exec( ua ) ||
+ /(msie) ([\w.]+)/.exec( ua ) ||
+ !/compatible/.test( ua ) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec( ua ) ||
+ [];
+
+ return { browser: match[1] || "", version: match[2] || "0" };
+ },
+
+ browser: {}
+});
+
+browserMatch = jQuery.uaMatch( userAgent );
+if ( browserMatch.browser ) {
+ jQuery.browser[ browserMatch.browser ] = true;
+ jQuery.browser.version = browserMatch.version;
+}
+
+// Deprecated, use jQuery.browser.webkit instead
+if ( jQuery.browser.webkit ) {
+ jQuery.browser.safari = true;
+}
+
+if ( indexOf ) {
+ jQuery.inArray = function( elem, array ) {
+ return indexOf.call( array, elem );
+ };
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+
+// Cleanup functions for the document ready method
+if ( document.addEventListener ) {
+ DOMContentLoaded = function() {
+ document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+ jQuery.ready();
+ };
+
+} else if ( document.attachEvent ) {
+ DOMContentLoaded = function() {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( document.readyState === "complete" ) {
+ document.detachEvent( "onreadystatechange", DOMContentLoaded );
+ jQuery.ready();
+ }
+ };
+}
+
+// The DOM ready check for Internet Explorer
+function doScrollCheck() {
+ if ( jQuery.isReady ) {
+ return;
+ }
+
+ try {
+ // If IE is used, use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ document.documentElement.doScroll("left");
+ } catch( error ) {
+ setTimeout( doScrollCheck, 1 );
+ return;
+ }
+
+ // and execute any waiting functions
+ jQuery.ready();
+}
+
+function evalScript( i, elem ) {
+ if ( elem.src ) {
+ jQuery.ajax({
+ url: elem.src,
+ async: false,
+ dataType: "script"
+ });
+ } else {
+ jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+}
+
+// Mutifunctional method to get and set values to a collection
+// The value/s can be optionally by executed if its a function
+function access( elems, key, value, exec, fn, pass ) {
+ var length = elems.length;
+
+ // Setting many attributes
+ if ( typeof key === "object" ) {
+ for ( var k in key ) {
+ access( elems, k, key[k], exec, fn, value );
+ }
+ return elems;
+ }
+
+ // Setting one attribute
+ if ( value !== undefined ) {
+ // Optionally, function values get executed if exec is true
+ exec = !pass && exec && jQuery.isFunction(value);
+
+ for ( var i = 0; i < length; i++ ) {
+ fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
+ }
+
+ return elems;
+ }
+
+ // Getting an attribute
+ return length ? fn( elems[0], key ) : undefined;
+}
+
+function now() {
+ return (new Date).getTime();
+}
+(function() {
+
+ jQuery.support = {};
+
+ var root = document.documentElement,
+ script = document.createElement("script"),
+ div = document.createElement("div"),
+ id = "script" + now();
+
+ div.style.display = "none";
+ div.innerHTML = " <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+
+ var all = div.getElementsByTagName("*"),
+ a = div.getElementsByTagName("a")[0];
+
+ // Can't get basic test support
+ if ( !all || !all.length || !a ) {
+ return;
+ }
+
+ jQuery.support = {
+ // IE strips leading whitespace when .innerHTML is used
+ leadingWhitespace: div.firstChild.nodeType === 3,
+
+ // Make sure that tbody elements aren't automatically inserted
+ // IE will insert them into empty tables
+ tbody: !div.getElementsByTagName("tbody").length,
+
+ // Make sure that link elements get serialized correctly by innerHTML
+ // This requires a wrapper element in IE
+ htmlSerialize: !!div.getElementsByTagName("link").length,
+
+ // Get the style information from getAttribute
+ // (IE uses .cssText insted)
+ style: /red/.test( a.getAttribute("style") ),
+
+ // Make sure that URLs aren't manipulated
+ // (IE normalizes it by default)
+ hrefNormalized: a.getAttribute("href") === "/a",
+
+ // Make sure that element opacity exists
+ // (IE uses filter instead)
+ // Use a regex to work around a WebKit issue. See #5145
+ opacity: /^0.55$/.test( a.style.opacity ),
+
+ // Verify style float existence
+ // (IE uses styleFloat instead of cssFloat)
+ cssFloat: !!a.style.cssFloat,
+
+ // Make sure that if no value is specified for a checkbox
+ // that it defaults to "on".
+ // (WebKit defaults to "" instead)
+ checkOn: div.getElementsByTagName("input")[0].value === "on",
+
+ // Make sure that a selected-by-default option has a working selected property.
+ // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+ optSelected: document.createElement("select").appendChild( document.createElement("option") ).selected,
+
+ parentNode: div.removeChild( div.appendChild( document.createElement("div") ) ).parentNode === null,
+
+ // Will be defined later
+ deleteExpando: true,
+ checkClone: false,
+ scriptEval: false,
+ noCloneEvent: true,
+ boxModel: null
+ };
+
+ script.type = "text/javascript";
+ try {
+ script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
+ } catch(e) {}
+
+ root.insertBefore( script, root.firstChild );
+
+ // Make sure that the execution of code works by injecting a script
+ // tag with appendChild/createTextNode
+ // (IE doesn't support this, fails, and uses .text instead)
+ if ( window[ id ] ) {
+ jQuery.support.scriptEval = true;
+ delete window[ id ];
+ }
+
+ // Test to see if it's possible to delete an expando from an element
+ // Fails in Internet Explorer
+ try {
+ delete script.test;
+
+ } catch(e) {
+ jQuery.support.deleteExpando = false;
+ }
+
+ root.removeChild( script );
+
+ if ( div.attachEvent && div.fireEvent ) {
+ div.attachEvent("onclick", function click() {
+ // Cloning a node shouldn't copy over any
+ // bound event handlers (IE does this)
+ jQuery.support.noCloneEvent = false;
+ div.detachEvent("onclick", click);
+ });
+ div.cloneNode(true).fireEvent("onclick");
+ }
+
+ div = document.createElement("div");
+ div.innerHTML = "<input type='radio' name='radiotest' checked='checked'/>";
+
+ var fragment = document.createDocumentFragment();
+ fragment.appendChild( div.firstChild );
+
+ // WebKit doesn't clone checked state correctly in fragments
+ jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked;
+
+ // Figure out if the W3C box model works as expected
+ // document.body must exist before we can do this
+ jQuery(function() {
+ var div = document.createElement("div");
+ div.style.width = div.style.paddingLeft = "1px";
+
+ document.body.appendChild( div );
+ jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
+ document.body.removeChild( div ).style.display = 'none';
+
+ div = null;
+ });
+
+ // Technique from Juriy Zaytsev
+ // http://thinkweb2.com/projects/prototype/detecting-event-support-without-bro…
+ var eventSupported = function( eventName ) {
+ var el = document.createElement("div");
+ eventName = "on" + eventName;
+
+ var isSupported = (eventName in el);
+ if ( !isSupported ) {
+ el.setAttribute(eventName, "return;");
+ isSupported = typeof el[eventName] === "function";
+ }
+ el = null;
+
+ return isSupported;
+ };
+
+ jQuery.support.submitBubbles = eventSupported("submit");
+ jQuery.support.changeBubbles = eventSupported("change");
+
+ // release memory in IE
+ root = script = div = all = a = null;
+})();
+
+jQuery.props = {
+ "for": "htmlFor",
+ "class": "className",
+ readonly: "readOnly",
+ maxlength: "maxLength",
+ cellspacing: "cellSpacing",
+ rowspan: "rowSpan",
+ colspan: "colSpan",
+ tabindex: "tabIndex",
+ usemap: "useMap",
+ frameborder: "frameBorder"
+};
+var expando = "jQuery" + now(), uuid = 0, windowData = {};
+
+jQuery.extend({
+ cache: {},
+
+ expando:expando,
+
+ // The following elements throw uncatchable exceptions if you
+ // attempt to add expando properties to them.
+ noData: {
+ "embed": true,
+ "object": true,
+ "applet": true
+ },
+
+ data: function( elem, name, data ) {
+ if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
+ return;
+ }
+
+ elem = elem == window ?
+ windowData :
+ elem;
+
+ var id = elem[ expando ], cache = jQuery.cache, thisCache;
+
+ if ( !id && typeof name === "string" && data === undefined ) {
+ return null;
+ }
+
+ // Compute a unique ID for the element
+ if ( !id ) {
+ id = ++uuid;
+ }
+
+ // Avoid generating a new cache unless none exists and we
+ // want to manipulate it.
+ if ( typeof name === "object" ) {
+ elem[ expando ] = id;
+ thisCache = cache[ id ] = jQuery.extend(true, {}, name);
+
+ } else if ( !cache[ id ] ) {
+ elem[ expando ] = id;
+ cache[ id ] = {};
+ }
+
+ thisCache = cache[ id ];
+
+ // Prevent overriding the named cache with undefined values
+ if ( data !== undefined ) {
+ thisCache[ name ] = data;
+ }
+
+ return typeof name === "string" ? thisCache[ name ] : thisCache;
+ },
+
+ removeData: function( elem, name ) {
+ if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
+ return;
+ }
+
+ elem = elem == window ?
+ windowData :
+ elem;
+
+ var id = elem[ expando ], cache = jQuery.cache, thisCache = cache[ id ];
+
+ // If we want to remove a specific section of the element's data
+ if ( name ) {
+ if ( thisCache ) {
+ // Remove the section of cache data
+ delete thisCache[ name ];
+
+ // If we've removed all the data, remove the element's cache
+ if ( jQuery.isEmptyObject(thisCache) ) {
+ jQuery.removeData( elem );
+ }
+ }
+
+ // Otherwise, we want to remove all of the element's data
+ } else {
+ if ( jQuery.support.deleteExpando ) {
+ delete elem[ jQuery.expando ];
+
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( jQuery.expando );
+ }
+
+ // Completely remove the data cache
+ delete cache[ id ];
+ }
+ }
+});
+
+jQuery.fn.extend({
+ data: function( key, value ) {
+ if ( typeof key === "undefined" && this.length ) {
+ return jQuery.data( this[0] );
+
+ } else if ( typeof key === "object" ) {
+ return this.each(function() {
+ jQuery.data( this, key );
+ });
+ }
+
+ var parts = key.split(".");
+ parts[1] = parts[1] ? "." + parts[1] : "";
+
+ if ( value === undefined ) {
+ var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
+
+ if ( data === undefined && this.length ) {
+ data = jQuery.data( this[0], key );
+ }
+ return data === undefined && parts[1] ?
+ this.data( parts[0] ) :
+ data;
+ } else {
+ return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function() {
+ jQuery.data( this, key, value );
+ });
+ }
+ },
+
+ removeData: function( key ) {
+ return this.each(function() {
+ jQuery.removeData( this, key );
+ });
+ }
+});
+jQuery.extend({
+ queue: function( elem, type, data ) {
+ if ( !elem ) {
+ return;
+ }
+
+ type = (type || "fx") + "queue";
+ var q = jQuery.data( elem, type );
+
+ // Speed up dequeue by getting out quickly if this is just a lookup
+ if ( !data ) {
+ return q || [];
+ }
+
+ if ( !q || jQuery.isArray(data) ) {
+ q = jQuery.data( elem, type, jQuery.makeArray(data) );
+
+ } else {
+ q.push( data );
+ }
+
+ return q;
+ },
+
+ dequeue: function( elem, type ) {
+ type = type || "fx";
+
+ var queue = jQuery.queue( elem, type ), fn = queue.shift();
+
+ // If the fx queue is dequeued, always remove the progress sentinel
+ if ( fn === "inprogress" ) {
+ fn = queue.shift();
+ }
+
+ if ( fn ) {
+ // Add a progress sentinel to prevent the fx queue from being
+ // automatically dequeued
+ if ( type === "fx" ) {
+ queue.unshift("inprogress");
+ }
+
+ fn.call(elem, function() {
+ jQuery.dequeue(elem, type);
+ });
+ }
+ }
+});
+
+jQuery.fn.extend({
+ queue: function( type, data ) {
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ }
+
+ if ( data === undefined ) {
+ return jQuery.queue( this[0], type );
+ }
+ return this.each(function( i, elem ) {
+ var queue = jQuery.queue( this, type, data );
+
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ dequeue: function( type ) {
+ return this.each(function() {
+ jQuery.dequeue( this, type );
+ });
+ },
+
+ // Based off of the plugin by Clint Helfers, with permission.
+ // http://blindsignals.com/index.php/2009/07/jquery-delay/
+ delay: function( time, type ) {
+ time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
+ type = type || "fx";
+
+ return this.queue( type, function() {
+ var elem = this;
+ setTimeout(function() {
+ jQuery.dequeue( elem, type );
+ }, time );
+ });
+ },
+
+ clearQueue: function( type ) {
+ return this.queue( type || "fx", [] );
+ }
+});
+var rclass = /[\n\t]/g,
+ rspace = /\s+/,
+ rreturn = /\r/g,
+ rspecialurl = /href|src|style/,
+ rtype = /(button|input)/i,
+ rfocusable = /(button|input|object|select|textarea)/i,
+ rclickable = /^(a|area)$/i,
+ rradiocheck = /radio|checkbox/;
+
+jQuery.fn.extend({
+ attr: function( name, value ) {
+ return access( this, name, value, true, jQuery.attr );
+ },
+
+ removeAttr: function( name, fn ) {
+ return this.each(function(){
+ jQuery.attr( this, name, "" );
+ if ( this.nodeType === 1 ) {
+ this.removeAttribute( name );
+ }
+ });
+ },
+
+ addClass: function( value ) {
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ self.addClass( value.call(this, i, self.attr("class")) );
+ });
+ }
+
+ if ( value && typeof value === "string" ) {
+ var classNames = (value || "").split( rspace );
+
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ var elem = this[i];
+
+ if ( elem.nodeType === 1 ) {
+ if ( !elem.className ) {
+ elem.className = value;
+
+ } else {
+ var className = " " + elem.className + " ", setClass = elem.className;
+ for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
+ if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
+ setClass += " " + classNames[c];
+ }
+ }
+ elem.className = jQuery.trim( setClass );
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ removeClass: function( value ) {
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ self.removeClass( value.call(this, i, self.attr("class")) );
+ });
+ }
+
+ if ( (value && typeof value === "string") || value === undefined ) {
+ var classNames = (value || "").split(rspace);
+
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ var elem = this[i];
+
+ if ( elem.nodeType === 1 && elem.className ) {
+ if ( value ) {
+ var className = (" " + elem.className + " ").replace(rclass, " ");
+ for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
+ className = className.replace(" " + classNames[c] + " ", " ");
+ }
+ elem.className = jQuery.trim( className );
+
+ } else {
+ elem.className = "";
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ toggleClass: function( value, stateVal ) {
+ var type = typeof value, isBool = typeof stateVal === "boolean";
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal );
+ });
+ }
+
+ return this.each(function() {
+ if ( type === "string" ) {
+ // toggle individual class names
+ var className, i = 0, self = jQuery(this),
+ state = stateVal,
+ classNames = value.split( rspace );
+
+ while ( (className = classNames[ i++ ]) ) {
+ // check each className given, space seperated list
+ state = isBool ? state : !self.hasClass( className );
+ self[ state ? "addClass" : "removeClass" ]( className );
+ }
+
+ } else if ( type === "undefined" || type === "boolean" ) {
+ if ( this.className ) {
+ // store className if set
+ jQuery.data( this, "__className__", this.className );
+ }
+
+ // toggle whole className
+ this.className = this.className || value === false ? "" : jQuery.data( this, "__className__" ) || "";
+ }
+ });
+ },
+
+ hasClass: function( selector ) {
+ var className = " " + selector + " ";
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ val: function( value ) {
+ if ( value === undefined ) {
+ var elem = this[0];
+
+ if ( elem ) {
+ if ( jQuery.nodeName( elem, "option" ) ) {
+ return (elem.attributes.value || {}).specified ? elem.value : elem.text;
+ }
+
+ // We need to handle select boxes special
+ if ( jQuery.nodeName( elem, "select" ) ) {
+ var index = elem.selectedIndex,
+ values = [],
+ options = elem.options,
+ one = elem.type === "select-one";
+
+ // Nothing was selected
+ if ( index < 0 ) {
+ return null;
+ }
+
+ // Loop through all the selected options
+ for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
+ var option = options[ i ];
+
+ if ( option.selected ) {
+ // Get the specifc value for the option
+ value = jQuery(option).val();
+
+ // We don't need an array for one selects
+ if ( one ) {
+ return value;
+ }
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ return values;
+ }
+
+ // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
+ if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {
+ return elem.getAttribute("value") === null ? "on" : elem.value;
+ }
+
+
+ // Everything else, we just grab the value
+ return (elem.value || "").replace(rreturn, "");
+
+ }
+
+ return undefined;
+ }
+
+ var isFunction = jQuery.isFunction(value);
+
+ return this.each(function(i) {
+ var self = jQuery(this), val = value;
+
+ if ( this.nodeType !== 1 ) {
+ return;
+ }
+
+ if ( isFunction ) {
+ val = value.call(this, i, self.val());
+ }
+
+ // Typecast each time if the value is a Function and the appended
+ // value is therefore different each time.
+ if ( typeof val === "number" ) {
+ val += "";
+ }
+
+ if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) {
+ this.checked = jQuery.inArray( self.val(), val ) >= 0;
+
+ } else if ( jQuery.nodeName( this, "select" ) ) {
+ var values = jQuery.makeArray(val);
+
+ jQuery( "option", this ).each(function() {
+ this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+ });
+
+ if ( !values.length ) {
+ this.selectedIndex = -1;
+ }
+
+ } else {
+ this.value = val;
+ }
+ });
+ }
+});
+
+jQuery.extend({
+ attrFn: {
+ val: true,
+ css: true,
+ html: true,
+ text: true,
+ data: true,
+ width: true,
+ height: true,
+ offset: true
+ },
+
+ attr: function( elem, name, value, pass ) {
+ // don't set attributes on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return undefined;
+ }
+
+ if ( pass && name in jQuery.attrFn ) {
+ return jQuery(elem)[name](value);
+ }
+
+ var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ),
+ // Whether we are setting (or getting)
+ set = value !== undefined;
+
+ // Try to normalize/fix the name
+ name = notxml && jQuery.props[ name ] || name;
+
+ // Only do all the following if this is a node (faster for style)
+ if ( elem.nodeType === 1 ) {
+ // These attributes require special treatment
+ var special = rspecialurl.test( name );
+
+ // Safari mis-reports the default selected property of an option
+ // Accessing the parent's selectedIndex property fixes it
+ if ( name === "selected" && !jQuery.support.optSelected ) {
+ var parent = elem.parentNode;
+ if ( parent ) {
+ parent.selectedIndex;
+
+ // Make sure that it also works with optgroups, see #5701
+ if ( parent.parentNode ) {
+ parent.parentNode.selectedIndex;
+ }
+ }
+ }
+
+ // If applicable, access the attribute via the DOM 0 way
+ if ( name in elem && notxml && !special ) {
+ if ( set ) {
+ // We can't allow the type property to be changed (since it causes problems in IE)
+ if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) {
+ jQuery.error( "type property can't be changed" );
+ }
+
+ elem[ name ] = value;
+ }
+
+ // browsers index elements by id/name on forms, give priority to attributes.
+ if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) {
+ return elem.getAttributeNode( name ).nodeValue;
+ }
+
+ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabind…
+ if ( name === "tabIndex" ) {
+ var attributeNode = elem.getAttributeNode( "tabIndex" );
+
+ return attributeNode && attributeNode.specified ?
+ attributeNode.value :
+ rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+ 0 :
+ undefined;
+ }
+
+ return elem[ name ];
+ }
+
+ if ( !jQuery.support.style && notxml && name === "style" ) {
+ if ( set ) {
+ elem.style.cssText = "" + value;
+ }
+
+ return elem.style.cssText;
+ }
+
+ if ( set ) {
+ // convert the value to a string (all browsers do this but IE) see #1070
+ elem.setAttribute( name, "" + value );
+ }
+
+ var attr = !jQuery.support.hrefNormalized && notxml && special ?
+ // Some attributes require a special call on IE
+ elem.getAttribute( name, 2 ) :
+ elem.getAttribute( name );
+
+ // Non-existent attributes return null, we normalize to undefined
+ return attr === null ? undefined : attr;
+ }
+
+ // elem is actually elem.style ... set the style
+ // Using attr for specific style information is now deprecated. Use style instead.
+ return jQuery.style( elem, name, value );
+ }
+});
+var rnamespaces = /\.(.*)$/,
+ fcleanup = function( nm ) {
+ return nm.replace(/[^\w\s\.\|`]/g, function( ch ) {
+ return "\\" + ch;
+ });
+ };
+
+/*
+ * A number of helper functions used for managing events.
+ * Many of the ideas behind this code originated from
+ * Dean Edwards' addEvent library.
+ */
+jQuery.event = {
+
+ // Bind an event to an element
+ // Original by Dean Edwards
+ add: function( elem, types, handler, data ) {
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ // For whatever reason, IE has trouble passing the window object
+ // around, causing it to be cloned in the process
+ if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) {
+ elem = window;
+ }
+
+ var handleObjIn, handleObj;
+
+ if ( handler.handler ) {
+ handleObjIn = handler;
+ handler = handleObjIn.handler;
+ }
+
+ // Make sure that the function being executed has a unique ID
+ if ( !handler.guid ) {
+ handler.guid = jQuery.guid++;
+ }
+
+ // Init the element's event structure
+ var elemData = jQuery.data( elem );
+
+ // If no elemData is found then we must be trying to bind to one of the
+ // banned noData elements
+ if ( !elemData ) {
+ return;
+ }
+
+ var events = elemData.events = elemData.events || {},
+ eventHandle = elemData.handle, eventHandle;
+
+ if ( !eventHandle ) {
+ elemData.handle = eventHandle = function() {
+ // Handle the second event of a trigger and when
+ // an event is called after a page has unloaded
+ return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
+ jQuery.event.handle.apply( eventHandle.elem, arguments ) :
+ undefined;
+ };
+ }
+
+ // Add elem as a property of the handle function
+ // This is to prevent a memory leak with non-native events in IE.
+ eventHandle.elem = elem;
+
+ // Handle multiple events separated by a space
+ // jQuery(...).bind("mouseover mouseout", fn);
+ types = types.split(" ");
+
+ var type, i = 0, namespaces;
+
+ while ( (type = types[ i++ ]) ) {
+ handleObj = handleObjIn ?
+ jQuery.extend({}, handleObjIn) :
+ { handler: handler, data: data };
+
+ // Namespaced event handlers
+ if ( type.indexOf(".") > -1 ) {
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ handleObj.namespace = namespaces.slice(0).sort().join(".");
+
+ } else {
+ namespaces = [];
+ handleObj.namespace = "";
+ }
+
+ handleObj.type = type;
+ handleObj.guid = handler.guid;
+
+ // Get the current list of functions bound to this event
+ var handlers = events[ type ],
+ special = jQuery.event.special[ type ] || {};
+
+ // Init the event handler queue
+ if ( !handlers ) {
+ handlers = events[ type ] = [];
+
+ // Check for a special event handler
+ // Only use addEventListener/attachEvent if the special
+ // events handler returns false
+ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+ // Bind the global event handler to the element
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, eventHandle, false );
+
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, eventHandle );
+ }
+ }
+ }
+
+ if ( special.add ) {
+ special.add.call( elem, handleObj );
+
+ if ( !handleObj.handler.guid ) {
+ handleObj.handler.guid = handler.guid;
+ }
+ }
+
+ // Add the function to the element's handler list
+ handlers.push( handleObj );
+
+ // Keep track of which events have been used, for global triggering
+ jQuery.event.global[ type ] = true;
+ }
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ global: {},
+
+ // Detach an event or set of events from an element
+ remove: function( elem, types, handler, pos ) {
+ // don't do events on text and comment nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ var ret, type, fn, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
+ elemData = jQuery.data( elem ),
+ events = elemData && elemData.events;
+
+ if ( !elemData || !events ) {
+ return;
+ }
+
+ // types is actually an event object here
+ if ( types && types.type ) {
+ handler = types.handler;
+ types = types.type;
+ }
+
+ // Unbind all events for the element
+ if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
+ types = types || "";
+
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types );
+ }
+
+ return;
+ }
+
+ // Handle multiple events separated by a space
+ // jQuery(...).unbind("mouseover mouseout", fn);
+ types = types.split(" ");
+
+ while ( (type = types[ i++ ]) ) {
+ origType = type;
+ handleObj = null;
+ all = type.indexOf(".") < 0;
+ namespaces = [];
+
+ if ( !all ) {
+ // Namespaced event handlers
+ namespaces = type.split(".");
+ type = namespaces.shift();
+
+ namespace = new RegExp("(^|\\.)" +
+ jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)")
+ }
+
+ eventType = events[ type ];
+
+ if ( !eventType ) {
+ continue;
+ }
+
+ if ( !handler ) {
+ for ( var j = 0; j < eventType.length; j++ ) {
+ handleObj = eventType[ j ];
+
+ if ( all || namespace.test( handleObj.namespace ) ) {
+ jQuery.event.remove( elem, origType, handleObj.handler, j );
+ eventType.splice( j--, 1 );
+ }
+ }
+
+ continue;
+ }
+
+ special = jQuery.event.special[ type ] || {};
+
+ for ( var j = pos || 0; j < eventType.length; j++ ) {
+ handleObj = eventType[ j ];
+
+ if ( handler.guid === handleObj.guid ) {
+ // remove the given handler for the given type
+ if ( all || namespace.test( handleObj.namespace ) ) {
+ if ( pos == null ) {
+ eventType.splice( j--, 1 );
+ }
+
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
+ }
+ }
+
+ if ( pos != null ) {
+ break;
+ }
+ }
+ }
+
+ // remove generic event handler if no more handlers exist
+ if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
+ if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
+ removeEvent( elem, type, elemData.handle );
+ }
+
+ ret = null;
+ delete events[ type ];
+ }
+ }
+
+ // Remove the expando if it's no longer used
+ if ( jQuery.isEmptyObject( events ) ) {
+ var handle = elemData.handle;
+ if ( handle ) {
+ handle.elem = null;
+ }
+
+ delete elemData.events;
+ delete elemData.handle;
+
+ if ( jQuery.isEmptyObject( elemData ) ) {
+ jQuery.removeData( elem );
+ }
+ }
+ },
+
+ // bubbling is internal
+ trigger: function( event, data, elem /*, bubbling */ ) {
+ // Event object or event type
+ var type = event.type || event,
+ bubbling = arguments[3];
+
+ if ( !bubbling ) {
+ event = typeof event === "object" ?
+ // jQuery.Event object
+ event[expando] ? event :
+ // Object literal
+ jQuery.extend( jQuery.Event(type), event ) :
+ // Just the event type (string)
+ jQuery.Event(type);
+
+ if ( type.indexOf("!") >= 0 ) {
+ event.type = type = type.slice(0, -1);
+ event.exclusive = true;
+ }
+
+ // Handle a global trigger
+ if ( !elem ) {
+ // Don't bubble custom events when global (to avoid too much overhead)
+ event.stopPropagation();
+
+ // Only trigger if we've ever bound an event for it
+ if ( jQuery.event.global[ type ] ) {
+ jQuery.each( jQuery.cache, function() {
+ if ( this.events && this.events[type] ) {
+ jQuery.event.trigger( event, data, this.handle.elem );
+ }
+ });
+ }
+ }
+
+ // Handle triggering a single element
+
+ // don't do events on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return undefined;
+ }
+
+ // Clean up in case it is reused
+ event.result = undefined;
+ event.target = elem;
+
+ // Clone the incoming data, if any
+ data = jQuery.makeArray( data );
+ data.unshift( event );
+ }
+
+ event.currentTarget = elem;
+
+ // Trigger the event, it is assumed that "handle" is a function
+ var handle = jQuery.data( elem, "handle" );
+ if ( handle ) {
+ handle.apply( elem, data );
+ }
+
+ var parent = elem.parentNode || elem.ownerDocument;
+
+ // Trigger an inline bound script
+ try {
+ if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
+ if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
+ event.result = false;
+ }
+ }
+
+ // prevent IE from throwing an error for some elements with some event types, see #3533
+ } catch (e) {}
+
+ if ( !event.isPropagationStopped() && parent ) {
+ jQuery.event.trigger( event, data, parent, true );
+
+ } else if ( !event.isDefaultPrevented() ) {
+ var target = event.target, old,
+ isClick = jQuery.nodeName(target, "a") && type === "click",
+ special = jQuery.event.special[ type ] || {};
+
+ if ( (!special._default || special._default.call( elem, event ) === false) &&
+ !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
+
+ try {
+ if ( target[ type ] ) {
+ // Make sure that we don't accidentally re-trigger the onFOO events
+ old = target[ "on" + type ];
+
+ if ( old ) {
+ target[ "on" + type ] = null;
+ }
+
+ jQuery.event.triggered = true;
+ target[ type ]();
+ }
+
+ // prevent IE from throwing an error for some elements with some event types, see #3533
+ } catch (e) {}
+
+ if ( old ) {
+ target[ "on" + type ] = old;
+ }
+
+ jQuery.event.triggered = false;
+ }
+ }
+ },
+
+ handle: function( event ) {
+ var all, handlers, namespaces, namespace, events;
+
+ event = arguments[0] = jQuery.event.fix( event || window.event );
+ event.currentTarget = this;
+
+ // Namespaced event handlers
+ all = event.type.indexOf(".") < 0 && !event.exclusive;
+
+ if ( !all ) {
+ namespaces = event.type.split(".");
+ event.type = namespaces.shift();
+ namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
+ }
+
+ var events = jQuery.data(this, "events"), handlers = events[ event.type ];
+
+ if ( events && handlers ) {
+ // Clone the handlers to prevent manipulation
+ handlers = handlers.slice(0);
+
+ for ( var j = 0, l = handlers.length; j < l; j++ ) {
+ var handleObj = handlers[ j ];
+
+ // Filter the functions by class
+ if ( all || namespace.test( handleObj.namespace ) ) {
+ // Pass in a reference to the handler function itself
+ // So that we can later remove it
+ event.handler = handleObj.handler;
+ event.data = handleObj.data;
+ event.handleObj = handleObj;
+
+ var ret = handleObj.handler.apply( this, arguments );
+
+ if ( ret !== undefined ) {
+ event.result = ret;
+ if ( ret === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+
+ if ( event.isImmediatePropagationStopped() ) {
+ break;
+ }
+ }
+ }
+ }
+
+ return event.result;
+ },
+
+ props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+
+ fix: function( event ) {
+ if ( event[ expando ] ) {
+ return event;
+ }
+
+ // store a copy of the original event object
+ // and "clone" to set read-only properties
+ var originalEvent = event;
+ event = jQuery.Event( originalEvent );
+
+ for ( var i = this.props.length, prop; i; ) {
+ prop = this.props[ --i ];
+ event[ prop ] = originalEvent[ prop ];
+ }
+
+ // Fix target property, if necessary
+ if ( !event.target ) {
+ event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
+ }
+
+ // check if target is a textnode (safari)
+ if ( event.target.nodeType === 3 ) {
+ event.target = event.target.parentNode;
+ }
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && event.fromElement ) {
+ event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
+ }
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && event.clientX != null ) {
+ var doc = document.documentElement, body = document.body;
+ event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
+ event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
+ }
+
+ // Add which for key events
+ if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
+ event.which = event.charCode || event.keyCode;
+ }
+
+ // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
+ if ( !event.metaKey && event.ctrlKey ) {
+ event.metaKey = event.ctrlKey;
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && event.button !== undefined ) {
+ event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
+ }
+
+ return event;
+ },
+
+ // Deprecated, use jQuery.guid instead
+ guid: 1E8,
+
+ // Deprecated, use jQuery.proxy instead
+ proxy: jQuery.proxy,
+
+ special: {
+ ready: {
+ // Make sure the ready event is setup
+ setup: jQuery.bindReady,
+ teardown: jQuery.noop
+ },
+
+ live: {
+ add: function( handleObj ) {
+ jQuery.event.add( this, handleObj.origType, jQuery.extend({}, handleObj, {handler: liveHandler}) );
+ },
+
+ remove: function( handleObj ) {
+ var remove = true,
+ type = handleObj.origType.replace(rnamespaces, "");
+
+ jQuery.each( jQuery.data(this, "events").live || [], function() {
+ if ( type === this.origType.replace(rnamespaces, "") ) {
+ remove = false;
+ return false;
+ }
+ });
+
+ if ( remove ) {
+ jQuery.event.remove( this, handleObj.origType, liveHandler );
+ }
+ }
+
+ },
+
+ beforeunload: {
+ setup: function( data, namespaces, eventHandle ) {
+ // We only want to do this special case on windows
+ if ( this.setInterval ) {
+ this.onbeforeunload = eventHandle;
+ }
+
+ return false;
+ },
+ teardown: function( namespaces, eventHandle ) {
+ if ( this.onbeforeunload === eventHandle ) {
+ this.onbeforeunload = null;
+ }
+ }
+ }
+ }
+};
+
+var removeEvent = document.removeEventListener ?
+ function( elem, type, handle ) {
+ elem.removeEventListener( type, handle, false );
+ } :
+ function( elem, type, handle ) {
+ elem.detachEvent( "on" + type, handle );
+ };
+
+jQuery.Event = function( src ) {
+ // Allow instantiation without the 'new' keyword
+ if ( !this.preventDefault ) {
+ return new jQuery.Event( src );
+ }
+
+ // Event object
+ if ( src && src.type ) {
+ this.originalEvent = src;
+ this.type = src.type;
+ // Event type
+ } else {
+ this.type = src;
+ }
+
+ // timeStamp is buggy for some events on Firefox(#3843)
+ // So we won't rely on the native value
+ this.timeStamp = now();
+
+ // Mark it as fixed
+ this[ expando ] = true;
+};
+
+function returnFalse() {
+ return false;
+}
+function returnTrue() {
+ return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-bindin…
+jQuery.Event.prototype = {
+ preventDefault: function() {
+ this.isDefaultPrevented = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+
+ // if preventDefault exists run it on the original event
+ if ( e.preventDefault ) {
+ e.preventDefault();
+ }
+ // otherwise set the returnValue property of the original event to false (IE)
+ e.returnValue = false;
+ },
+ stopPropagation: function() {
+ this.isPropagationStopped = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+ // if stopPropagation exists run it on the original event
+ if ( e.stopPropagation ) {
+ e.stopPropagation();
+ }
+ // otherwise set the cancelBubble property of the original event to true (IE)
+ e.cancelBubble = true;
+ },
+ stopImmediatePropagation: function() {
+ this.isImmediatePropagationStopped = returnTrue;
+ this.stopPropagation();
+ },
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse
+};
+
+// Checks if an event happened on an element within another element
+// Used in jQuery.event.special.mouseenter and mouseleave handlers
+var withinElement = function( event ) {
+ // Check if mouse(over|out) are still within the same parent element
+ var parent = event.relatedTarget;
+
+ // Firefox sometimes assigns relatedTarget a XUL element
+ // which we cannot access the parentNode property of
+ try {
+ // Traverse up the tree
+ while ( parent && parent !== this ) {
+ parent = parent.parentNode;
+ }
+
+ if ( parent !== this ) {
+ // set the correct event type
+ event.type = event.data;
+
+ // handle event if we actually just moused on to a non sub-element
+ jQuery.event.handle.apply( this, arguments );
+ }
+
+ // assuming we've left the element since we most likely mousedover a xul element
+ } catch(e) { }
+},
+
+// In case of event delegation, we only need to rename the event.type,
+// liveHandler will take care of the rest.
+delegate = function( event ) {
+ event.type = event.data;
+ jQuery.event.handle.apply( this, arguments );
+};
+
+// Create mouseenter and mouseleave events
+jQuery.each({
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+}, function( orig, fix ) {
+ jQuery.event.special[ orig ] = {
+ setup: function( data ) {
+ jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
+ },
+ teardown: function( data ) {
+ jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
+ }
+ };
+});
+
+// submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+ jQuery.event.special.submit = {
+ setup: function( data, namespaces ) {
+ if ( this.nodeName.toLowerCase() !== "form" ) {
+ jQuery.event.add(this, "click.specialSubmit", function( e ) {
+ var elem = e.target, type = elem.type;
+
+ if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
+ return trigger( "submit", this, arguments );
+ }
+ });
+
+ jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
+ var elem = e.target, type = elem.type;
+
+ if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
+ return trigger( "submit", this, arguments );
+ }
+ });
+
+ } else {
+ return false;
+ }
+ },
+
+ teardown: function( namespaces ) {
+ jQuery.event.remove( this, ".specialSubmit" );
+ }
+ };
+
+}
+
+// change delegation, happens here so we have bind.
+if ( !jQuery.support.changeBubbles ) {
+
+ var formElems = /textarea|input|select/i,
+
+ changeFilters,
+
+ getVal = function( elem ) {
+ var type = elem.type, val = elem.value;
+
+ if ( type === "radio" || type === "checkbox" ) {
+ val = elem.checked;
+
+ } else if ( type === "select-multiple" ) {
+ val = elem.selectedIndex > -1 ?
+ jQuery.map( elem.options, function( elem ) {
+ return elem.selected;
+ }).join("-") :
+ "";
+
+ } else if ( elem.nodeName.toLowerCase() === "select" ) {
+ val = elem.selectedIndex;
+ }
+
+ return val;
+ },
+
+ testChange = function testChange( e ) {
+ var elem = e.target, data, val;
+
+ if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
+ return;
+ }
+
+ data = jQuery.data( elem, "_change_data" );
+ val = getVal(elem);
+
+ // the current data will be also retrieved by beforeactivate
+ if ( e.type !== "focusout" || elem.type !== "radio" ) {
+ jQuery.data( elem, "_change_data", val );
+ }
+
+ if ( data === undefined || val === data ) {
+ return;
+ }
+
+ if ( data != null || val ) {
+ e.type = "change";
+ return jQuery.event.trigger( e, arguments[1], elem );
+ }
+ };
+
+ jQuery.event.special.change = {
+ filters: {
+ focusout: testChange,
+
+ click: function( e ) {
+ var elem = e.target, type = elem.type;
+
+ if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
+ return testChange.call( this, e );
+ }
+ },
+
+ // Change has to be called before submit
+ // Keydown will be called before keypress, which is used in submit-event delegation
+ keydown: function( e ) {
+ var elem = e.target, type = elem.type;
+
+ if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
+ (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
+ type === "select-multiple" ) {
+ return testChange.call( this, e );
+ }
+ },
+
+ // Beforeactivate happens also before the previous element is blurred
+ // with this event you can't trigger a change event, but you can store
+ // information/focus[in] is not needed anymore
+ beforeactivate: function( e ) {
+ var elem = e.target;
+ jQuery.data( elem, "_change_data", getVal(elem) );
+ }
+ },
+
+ setup: function( data, namespaces ) {
+ if ( this.type === "file" ) {
+ return false;
+ }
+
+ for ( var type in changeFilters ) {
+ jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
+ }
+
+ return formElems.test( this.nodeName );
+ },
+
+ teardown: function( namespaces ) {
+ jQuery.event.remove( this, ".specialChange" );
+
+ return formElems.test( this.nodeName );
+ }
+ };
+
+ changeFilters = jQuery.event.special.change.filters;
+}
+
+function trigger( type, elem, args ) {
+ args[0].type = type;
+ return jQuery.event.handle.apply( elem, args );
+}
+
+// Create "bubbling" focus and blur events
+if ( document.addEventListener ) {
+ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+ jQuery.event.special[ fix ] = {
+ setup: function() {
+ this.addEventListener( orig, handler, true );
+ },
+ teardown: function() {
+ this.removeEventListener( orig, handler, true );
+ }
+ };
+
+ function handler( e ) {
+ e = jQuery.event.fix( e );
+ e.type = fix;
+ return jQuery.event.handle.call( this, e );
+ }
+ });
+}
+
+jQuery.each(["bind", "one"], function( i, name ) {
+ jQuery.fn[ name ] = function( type, data, fn ) {
+ // Handle object literals
+ if ( typeof type === "object" ) {
+ for ( var key in type ) {
+ this[ name ](key, data, type[key], fn);
+ }
+ return this;
+ }
+
+ if ( jQuery.isFunction( data ) ) {
+ fn = data;
+ data = undefined;
+ }
+
+ var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
+ jQuery( this ).unbind( event, handler );
+ return fn.apply( this, arguments );
+ }) : fn;
+
+ if ( type === "unload" && name !== "one" ) {
+ this.one( type, data, fn );
+
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ jQuery.event.add( this[i], type, handler, data );
+ }
+ }
+
+ return this;
+ };
+});
+
+jQuery.fn.extend({
+ unbind: function( type, fn ) {
+ // Handle object literals
+ if ( typeof type === "object" && !type.preventDefault ) {
+ for ( var key in type ) {
+ this.unbind(key, type[key]);
+ }
+
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ jQuery.event.remove( this[i], type, fn );
+ }
+ }
+
+ return this;
+ },
+
+ delegate: function( selector, types, data, fn ) {
+ return this.live( types, data, fn, selector );
+ },
+
+ undelegate: function( selector, types, fn ) {
+ if ( arguments.length === 0 ) {
+ return this.unbind( "live" );
+
+ } else {
+ return this.die( types, null, fn, selector );
+ }
+ },
+
+ trigger: function( type, data ) {
+ return this.each(function() {
+ jQuery.event.trigger( type, data, this );
+ });
+ },
+
+ triggerHandler: function( type, data ) {
+ if ( this[0] ) {
+ var event = jQuery.Event( type );
+ event.preventDefault();
+ event.stopPropagation();
+ jQuery.event.trigger( event, data, this[0] );
+ return event.result;
+ }
+ },
+
+ toggle: function( fn ) {
+ // Save reference to arguments for access in closure
+ var args = arguments, i = 1;
+
+ // link all the functions, so any of them can unbind this click handler
+ while ( i < args.length ) {
+ jQuery.proxy( fn, args[ i++ ] );
+ }
+
+ return this.click( jQuery.proxy( fn, function( event ) {
+ // Figure out which function to execute
+ var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+ jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+ // Make sure that clicks stop
+ event.preventDefault();
+
+ // and execute the function
+ return args[ lastToggle ].apply( this, arguments ) || false;
+ }));
+ },
+
+ hover: function( fnOver, fnOut ) {
+ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+ }
+});
+
+var liveMap = {
+ focus: "focusin",
+ blur: "focusout",
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+};
+
+jQuery.each(["live", "die"], function( i, name ) {
+ jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
+ var type, i = 0, match, namespaces, preType,
+ selector = origSelector || this.selector,
+ context = origSelector ? this : jQuery( this.context );
+
+ if ( jQuery.isFunction( data ) ) {
+ fn = data;
+ data = undefined;
+ }
+
+ types = (types || "").split(" ");
+
+ while ( (type = types[ i++ ]) != null ) {
+ match = rnamespaces.exec( type );
+ namespaces = "";
+
+ if ( match ) {
+ namespaces = match[0];
+ type = type.replace( rnamespaces, "" );
+ }
+
+ if ( type === "hover" ) {
+ types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
+ continue;
+ }
+
+ preType = type;
+
+ if ( type === "focus" || type === "blur" ) {
+ types.push( liveMap[ type ] + namespaces );
+ type = type + namespaces;
+
+ } else {
+ type = (liveMap[ type ] || type) + namespaces;
+ }
+
+ if ( name === "live" ) {
+ // bind live handler
+ context.each(function(){
+ jQuery.event.add( this, liveConvert( type, selector ),
+ { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
+ });
+
+ } else {
+ // unbind live handler
+ context.unbind( liveConvert( type, selector ), fn );
+ }
+ }
+
+ return this;
+ }
+});
+
+function liveHandler( event ) {
+ var stop, elems = [], selectors = [], args = arguments,
+ related, match, handleObj, elem, j, i, l, data,
+ events = jQuery.data( this, "events" );
+
+ // Make sure we avoid non-left-click bubbling in Firefox (#3861)
+ if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
+ return;
+ }
+
+ event.liveFired = this;
+
+ var live = events.live.slice(0);
+
+ for ( j = 0; j < live.length; j++ ) {
+ handleObj = live[j];
+
+ if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
+ selectors.push( handleObj.selector );
+
+ } else {
+ live.splice( j--, 1 );
+ }
+ }
+
+ match = jQuery( event.target ).closest( selectors, event.currentTarget );
+
+ for ( i = 0, l = match.length; i < l; i++ ) {
+ for ( j = 0; j < live.length; j++ ) {
+ handleObj = live[j];
+
+ if ( match[i].selector === handleObj.selector ) {
+ elem = match[i].elem;
+ related = null;
+
+ // Those two events require additional checking
+ if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
+ related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
+ }
+
+ if ( !related || related !== elem ) {
+ elems.push({ elem: elem, handleObj: handleObj });
+ }
+ }
+ }
+ }
+
+ for ( i = 0, l = elems.length; i < l; i++ ) {
+ match = elems[i];
+ event.currentTarget = match.elem;
+ event.data = match.handleObj.data;
+ event.handleObj = match.handleObj;
+
+ if ( match.handleObj.origHandler.apply( match.elem, args ) === false ) {
+ stop = false;
+ break;
+ }
+ }
+
+ return stop;
+}
+
+function liveConvert( type, selector ) {
+ return "live." + (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
+}
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+ "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
+
+ // Handle event binding
+ jQuery.fn[ name ] = function( fn ) {
+ return fn ? this.bind( name, fn ) : this.trigger( name );
+ };
+
+ if ( jQuery.attrFn ) {
+ jQuery.attrFn[ name ] = true;
+ }
+});
+
+// Prevent memory leaks in IE
+// Window isn't included so as not to unbind existing unload events
+// More info:
+// - http://isaacschlueter.com/2006/10/msie-memory-leaks/
+if ( window.attachEvent && !window.addEventListener ) {
+ window.attachEvent("onunload", function() {
+ for ( var id in jQuery.cache ) {
+ if ( jQuery.cache[ id ].handle ) {
+ // Try/Catch is to handle iframes being unloaded, see #4280
+ try {
+ jQuery.event.remove( jQuery.cache[ id ].handle.elem );
+ } catch(e) {}
+ }
+ }
+ });
+}
+/*!
+ * Sizzle CSS Selector Engine - v1.0
+ * Copyright 2009, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ * More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+ done = 0,
+ toString = Object.prototype.toString,
+ hasDuplicate = false,
+ baseHasDuplicate = true;
+
+// Here we check if the JavaScript engine is using some sort of
+// optimization where it does not always call our comparision
+// function. If that is the case, discard the hasDuplicate value.
+// Thus far that includes Google Chrome.
+[0, 0].sort(function(){
+ baseHasDuplicate = false;
+ return 0;
+});
+
+var Sizzle = function(selector, context, results, seed) {
+ results = results || [];
+ var origContext = context = context || document;
+
+ if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
+ return [];
+ }
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context),
+ soFar = selector;
+
+ // Reset the position of the chunker regexp (start from head)
+ while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
+ soFar = m[3];
+
+ parts.push( m[1] );
+
+ if ( m[2] ) {
+ extra = m[3];
+ break;
+ }
+ }
+
+ if ( parts.length > 1 && origPOS.exec( selector ) ) {
+ if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+ set = posProcess( parts[0] + parts[1], context );
+ } else {
+ set = Expr.relative[ parts[0] ] ?
+ [ context ] :
+ Sizzle( parts.shift(), context );
+
+ while ( parts.length ) {
+ selector = parts.shift();
+
+ if ( Expr.relative[ selector ] ) {
+ selector += parts.shift();
+ }
+
+ set = posProcess( selector, set );
+ }
+ }
+ } else {
+ // Take a shortcut and set the context if the root selector is an ID
+ // (but not if it'll be faster if the inner selector is an ID)
+ if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
+ Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
+ var ret = Sizzle.find( parts.shift(), context, contextXML );
+ context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
+ }
+
+ if ( context ) {
+ var ret = seed ?
+ { expr: parts.pop(), set: makeArray(seed) } :
+ Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
+ set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
+
+ if ( parts.length > 0 ) {
+ checkSet = makeArray(set);
+ } else {
+ prune = false;
+ }
+
+ while ( parts.length ) {
+ var cur = parts.pop(), pop = cur;
+
+ if ( !Expr.relative[ cur ] ) {
+ cur = "";
+ } else {
+ pop = parts.pop();
+ }
+
+ if ( pop == null ) {
+ pop = context;
+ }
+
+ Expr.relative[ cur ]( checkSet, pop, contextXML );
+ }
+ } else {
+ checkSet = parts = [];
+ }
+ }
+
+ if ( !checkSet ) {
+ checkSet = set;
+ }
+
+ if ( !checkSet ) {
+ Sizzle.error( cur || selector );
+ }
+
+ if ( toString.call(checkSet) === "[object Array]" ) {
+ if ( !prune ) {
+ results.push.apply( results, checkSet );
+ } else if ( context && context.nodeType === 1 ) {
+ for ( var i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
+ results.push( set[i] );
+ }
+ }
+ } else {
+ for ( var i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+ results.push( set[i] );
+ }
+ }
+ }
+ } else {
+ makeArray( checkSet, results );
+ }
+
+ if ( extra ) {
+ Sizzle( extra, origContext, results, seed );
+ Sizzle.uniqueSort( results );
+ }
+
+ return results;
+};
+
+Sizzle.uniqueSort = function(results){
+ if ( sortOrder ) {
+ hasDuplicate = baseHasDuplicate;
+ results.sort(sortOrder);
+
+ if ( hasDuplicate ) {
+ for ( var i = 1; i < results.length; i++ ) {
+ if ( results[i] === results[i-1] ) {
+ results.splice(i--, 1);
+ }
+ }
+ }
+ }
+
+ return results;
+};
+
+Sizzle.matches = function(expr, set){
+ return Sizzle(expr, null, null, set);
+};
+
+Sizzle.find = function(expr, context, isXML){
+ var set, match;
+
+ if ( !expr ) {
+ return [];
+ }
+
+ for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
+ var type = Expr.order[i], match;
+
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
+ var left = match[1];
+ match.splice(1,1);
+
+ if ( left.substr( left.length - 1 ) !== "\\" ) {
+ match[1] = (match[1] || "").replace(/\\/g, "");
+ set = Expr.find[ type ]( match, context, isXML );
+ if ( set != null ) {
+ expr = expr.replace( Expr.match[ type ], "" );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !set ) {
+ set = context.getElementsByTagName("*");
+ }
+
+ return {set: set, expr: expr};
+};
+
+Sizzle.filter = function(expr, set, inplace, not){
+ var old = expr, result = [], curLoop = set, match, anyFound,
+ isXMLFilter = set && set[0] && isXML(set[0]);
+
+ while ( expr && set.length ) {
+ for ( var type in Expr.filter ) {
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
+ var filter = Expr.filter[ type ], found, item, left = match[1];
+ anyFound = false;
+
+ match.splice(1,1);
+
+ if ( left.substr( left.length - 1 ) === "\\" ) {
+ continue;
+ }
+
+ if ( curLoop === result ) {
+ result = [];
+ }
+
+ if ( Expr.preFilter[ type ] ) {
+ match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+ if ( !match ) {
+ anyFound = found = true;
+ } else if ( match === true ) {
+ continue;
+ }
+ }
+
+ if ( match ) {
+ for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
+ if ( item ) {
+ found = filter( item, match, i, curLoop );
+ var pass = not ^ !!found;
+
+ if ( inplace && found != null ) {
+ if ( pass ) {
+ anyFound = true;
+ } else {
+ curLoop[i] = false;
+ }
+ } else if ( pass ) {
+ result.push( item );
+ anyFound = true;
+ }
+ }
+ }
+ }
+
+ if ( found !== undefined ) {
+ if ( !inplace ) {
+ curLoop = result;
+ }
+
+ expr = expr.replace( Expr.match[ type ], "" );
+
+ if ( !anyFound ) {
+ return [];
+ }
+
+ break;
+ }
+ }
+ }
+
+ // Improper expression
+ if ( expr === old ) {
+ if ( anyFound == null ) {
+ Sizzle.error( expr );
+ } else {
+ break;
+ }
+ }
+
+ old = expr;
+ }
+
+ return curLoop;
+};
+
+Sizzle.error = function( msg ) {
+ throw "Syntax error, unrecognized expression: " + msg;
+};
+
+var Expr = Sizzle.selectors = {
+ order: [ "ID", "NAME", "TAG" ],
+ match: {
+ ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+ CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+ NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
+ ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
+ TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
+ CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
+ PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
+ },
+ leftMatch: {},
+ attrMap: {
+ "class": "className",
+ "for": "htmlFor"
+ },
+ attrHandle: {
+ href: function(elem){
+ return elem.getAttribute("href");
+ }
+ },
+ relative: {
+ "+": function(checkSet, part){
+ var isPartStr = typeof part === "string",
+ isTag = isPartStr && !/\W/.test(part),
+ isPartStrNotTag = isPartStr && !isTag;
+
+ if ( isTag ) {
+ part = part.toLowerCase();
+ }
+
+ for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+ if ( (elem = checkSet[i]) ) {
+ while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
+ elem || false :
+ elem === part;
+ }
+ }
+
+ if ( isPartStrNotTag ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ },
+ ">": function(checkSet, part){
+ var isPartStr = typeof part === "string";
+
+ if ( isPartStr && !/\W/.test(part) ) {
+ part = part.toLowerCase();
+
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ var parent = elem.parentNode;
+ checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
+ }
+ }
+ } else {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ checkSet[i] = isPartStr ?
+ elem.parentNode :
+ elem.parentNode === part;
+ }
+ }
+
+ if ( isPartStr ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ }
+ },
+ "": function(checkSet, part, isXML){
+ var doneName = done++, checkFn = dirCheck;
+
+ if ( typeof part === "string" && !/\W/.test(part) ) {
+ var nodeCheck = part = part.toLowerCase();
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
+ },
+ "~": function(checkSet, part, isXML){
+ var doneName = done++, checkFn = dirCheck;
+
+ if ( typeof part === "string" && !/\W/.test(part) ) {
+ var nodeCheck = part = part.toLowerCase();
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
+ }
+ },
+ find: {
+ ID: function(match, context, isXML){
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+ return m ? [m] : [];
+ }
+ },
+ NAME: function(match, context){
+ if ( typeof context.getElementsByName !== "undefined" ) {
+ var ret = [], results = context.getElementsByName(match[1]);
+
+ for ( var i = 0, l = results.length; i < l; i++ ) {
+ if ( results[i].getAttribute("name") === match[1] ) {
+ ret.push( results[i] );
+ }
+ }
+
+ return ret.length === 0 ? null : ret;
+ }
+ },
+ TAG: function(match, context){
+ return context.getElementsByTagName(match[1]);
+ }
+ },
+ preFilter: {
+ CLASS: function(match, curLoop, inplace, result, not, isXML){
+ match = " " + match[1].replace(/\\/g, "") + " ";
+
+ if ( isXML ) {
+ return match;
+ }
+
+ for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+ if ( elem ) {
+ if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {
+ if ( !inplace ) {
+ result.push( elem );
+ }
+ } else if ( inplace ) {
+ curLoop[i] = false;
+ }
+ }
+ }
+
+ return false;
+ },
+ ID: function(match){
+ return match[1].replace(/\\/g, "");
+ },
+ TAG: function(match, curLoop){
+ return match[1].toLowerCase();
+ },
+ CHILD: function(match){
+ if ( match[1] === "nth" ) {
+ // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+ var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
+ match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
+ !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+ // calculate the numbers (first)n+(last) including if they are negative
+ match[2] = (test[1] + (test[2] || 1)) - 0;
+ match[3] = test[3] - 0;
+ }
+
+ // TODO: Move to normal caching system
+ match[0] = done++;
+
+ return match;
+ },
+ ATTR: function(match, curLoop, inplace, result, not, isXML){
+ var name = match[1].replace(/\\/g, "");
+
+ if ( !isXML && Expr.attrMap[name] ) {
+ match[1] = Expr.attrMap[name];
+ }
+
+ if ( match[2] === "~=" ) {
+ match[4] = " " + match[4] + " ";
+ }
+
+ return match;
+ },
+ PSEUDO: function(match, curLoop, inplace, result, not){
+ if ( match[1] === "not" ) {
+ // If we're dealing with a complex expression, or a simple one
+ if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
+ match[3] = Sizzle(match[3], null, null, curLoop);
+ } else {
+ var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+ if ( !inplace ) {
+ result.push.apply( result, ret );
+ }
+ return false;
+ }
+ } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+ return true;
+ }
+
+ return match;
+ },
+ POS: function(match){
+ match.unshift( true );
+ return match;
+ }
+ },
+ filters: {
+ enabled: function(elem){
+ return elem.disabled === false && elem.type !== "hidden";
+ },
+ disabled: function(elem){
+ return elem.disabled === true;
+ },
+ checked: function(elem){
+ return elem.checked === true;
+ },
+ selected: function(elem){
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ elem.parentNode.selectedIndex;
+ return elem.selected === true;
+ },
+ parent: function(elem){
+ return !!elem.firstChild;
+ },
+ empty: function(elem){
+ return !elem.firstChild;
+ },
+ has: function(elem, i, match){
+ return !!Sizzle( match[3], elem ).length;
+ },
+ header: function(elem){
+ return /h\d/i.test( elem.nodeName );
+ },
+ text: function(elem){
+ return "text" === elem.type;
+ },
+ radio: function(elem){
+ return "radio" === elem.type;
+ },
+ checkbox: function(elem){
+ return "checkbox" === elem.type;
+ },
+ file: function(elem){
+ return "file" === elem.type;
+ },
+ password: function(elem){
+ return "password" === elem.type;
+ },
+ submit: function(elem){
+ return "submit" === elem.type;
+ },
+ image: function(elem){
+ return "image" === elem.type;
+ },
+ reset: function(elem){
+ return "reset" === elem.type;
+ },
+ button: function(elem){
+ return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
+ },
+ input: function(elem){
+ return /input|select|textarea|button/i.test(elem.nodeName);
+ }
+ },
+ setFilters: {
+ first: function(elem, i){
+ return i === 0;
+ },
+ last: function(elem, i, match, array){
+ return i === array.length - 1;
+ },
+ even: function(elem, i){
+ return i % 2 === 0;
+ },
+ odd: function(elem, i){
+ return i % 2 === 1;
+ },
+ lt: function(elem, i, match){
+ return i < match[3] - 0;
+ },
+ gt: function(elem, i, match){
+ return i > match[3] - 0;
+ },
+ nth: function(elem, i, match){
+ return match[3] - 0 === i;
+ },
+ eq: function(elem, i, match){
+ return match[3] - 0 === i;
+ }
+ },
+ filter: {
+ PSEUDO: function(elem, match, i, array){
+ var name = match[1], filter = Expr.filters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+ } else if ( name === "contains" ) {
+ return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
+ } else if ( name === "not" ) {
+ var not = match[3];
+
+ for ( var i = 0, l = not.length; i < l; i++ ) {
+ if ( not[i] === elem ) {
+ return false;
+ }
+ }
+
+ return true;
+ } else {
+ Sizzle.error( "Syntax error, unrecognized expression: " + name );
+ }
+ },
+ CHILD: function(elem, match){
+ var type = match[1], node = elem;
+ switch (type) {
+ case 'only':
+ case 'first':
+ while ( (node = node.previousSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+ if ( type === "first" ) {
+ return true;
+ }
+ node = elem;
+ case 'last':
+ while ( (node = node.nextSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+ return true;
+ case 'nth':
+ var first = match[2], last = match[3];
+
+ if ( first === 1 && last === 0 ) {
+ return true;
+ }
+
+ var doneName = match[0],
+ parent = elem.parentNode;
+
+ if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
+ var count = 0;
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
+ if ( node.nodeType === 1 ) {
+ node.nodeIndex = ++count;
+ }
+ }
+ parent.sizcache = doneName;
+ }
+
+ var diff = elem.nodeIndex - last;
+ if ( first === 0 ) {
+ return diff === 0;
+ } else {
+ return ( diff % first === 0 && diff / first >= 0 );
+ }
+ }
+ },
+ ID: function(elem, match){
+ return elem.nodeType === 1 && elem.getAttribute("id") === match;
+ },
+ TAG: function(elem, match){
+ return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
+ },
+ CLASS: function(elem, match){
+ return (" " + (elem.className || elem.getAttribute("class")) + " ")
+ .indexOf( match ) > -1;
+ },
+ ATTR: function(elem, match){
+ var name = match[1],
+ result = Expr.attrHandle[ name ] ?
+ Expr.attrHandle[ name ]( elem ) :
+ elem[ name ] != null ?
+ elem[ name ] :
+ elem.getAttribute( name ),
+ value = result + "",
+ type = match[2],
+ check = match[4];
+
+ return result == null ?
+ type === "!=" :
+ type === "=" ?
+ value === check :
+ type === "*=" ?
+ value.indexOf(check) >= 0 :
+ type === "~=" ?
+ (" " + value + " ").indexOf(check) >= 0 :
+ !check ?
+ value && result !== false :
+ type === "!=" ?
+ value !== check :
+ type === "^=" ?
+ value.indexOf(check) === 0 :
+ type === "$=" ?
+ value.substr(value.length - check.length) === check :
+ type === "|=" ?
+ value === check || value.substr(0, check.length + 1) === check + "-" :
+ false;
+ },
+ POS: function(elem, match, i, array){
+ var name = match[2], filter = Expr.setFilters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+ }
+ }
+ }
+};
+
+var origPOS = Expr.match.POS;
+
+for ( var type in Expr.match ) {
+ Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
+ Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, function(all, num){
+ return "\\" + (num - 0 + 1);
+ }));
+}
+
+var makeArray = function(array, results) {
+ array = Array.prototype.slice.call( array, 0 );
+
+ if ( results ) {
+ results.push.apply( results, array );
+ return results;
+ }
+
+ return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+// Also verifies that the returned array holds DOM nodes
+// (which is not the case in the Blackberry browser)
+try {
+ Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
+
+// Provide a fallback method if it does not work
+} catch(e){
+ makeArray = function(array, results) {
+ var ret = results || [];
+
+ if ( toString.call(array) === "[object Array]" ) {
+ Array.prototype.push.apply( ret, array );
+ } else {
+ if ( typeof array.length === "number" ) {
+ for ( var i = 0, l = array.length; i < l; i++ ) {
+ ret.push( array[i] );
+ }
+ } else {
+ for ( var i = 0; array[i]; i++ ) {
+ ret.push( array[i] );
+ }
+ }
+ }
+
+ return ret;
+ };
+}
+
+var sortOrder;
+
+if ( document.documentElement.compareDocumentPosition ) {
+ sortOrder = function( a, b ) {
+ if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
+ if ( a == b ) {
+ hasDuplicate = true;
+ }
+ return a.compareDocumentPosition ? -1 : 1;
+ }
+
+ var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
+ if ( ret === 0 ) {
+ hasDuplicate = true;
+ }
+ return ret;
+ };
+} else if ( "sourceIndex" in document.documentElement ) {
+ sortOrder = function( a, b ) {
+ if ( !a.sourceIndex || !b.sourceIndex ) {
+ if ( a == b ) {
+ hasDuplicate = true;
+ }
+ return a.sourceIndex ? -1 : 1;
+ }
+
+ var ret = a.sourceIndex - b.sourceIndex;
+ if ( ret === 0 ) {
+ hasDuplicate = true;
+ }
+ return ret;
+ };
+} else if ( document.createRange ) {
+ sortOrder = function( a, b ) {
+ if ( !a.ownerDocument || !b.ownerDocument ) {
+ if ( a == b ) {
+ hasDuplicate = true;
+ }
+ return a.ownerDocument ? -1 : 1;
+ }
+
+ var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
+ aRange.setStart(a, 0);
+ aRange.setEnd(a, 0);
+ bRange.setStart(b, 0);
+ bRange.setEnd(b, 0);
+ var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
+ if ( ret === 0 ) {
+ hasDuplicate = true;
+ }
+ return ret;
+ };
+}
+
+// Utility function for retreiving the text value of an array of DOM nodes
+function getText( elems ) {
+ var ret = "", elem;
+
+ for ( var i = 0; elems[i]; i++ ) {
+ elem = elems[i];
+
+ // Get the text from text nodes and CDATA nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
+ ret += elem.nodeValue;
+
+ // Traverse everything else, except comment nodes
+ } else if ( elem.nodeType !== 8 ) {
+ ret += getText( elem.childNodes );
+ }
+ }
+
+ return ret;
+}
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+ // We're going to inject a fake input element with a specified name
+ var form = document.createElement("div"),
+ id = "script" + (new Date).getTime();
+ form.innerHTML = "<a name='" + id + "'/>";
+
+ // Inject it into the root element, check its status, and remove it quickly
+ var root = document.documentElement;
+ root.insertBefore( form, root.firstChild );
+
+ // The workaround has to do additional checks after a getElementById
+ // Which slows things down for other browsers (hence the branching)
+ if ( document.getElementById( id ) ) {
+ Expr.find.ID = function(match, context, isXML){
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+ return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
+ }
+ };
+
+ Expr.filter.ID = function(elem, match){
+ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+ return elem.nodeType === 1 && node && node.nodeValue === match;
+ };
+ }
+
+ root.removeChild( form );
+ root = form = null; // release memory in IE
+})();
+
+(function(){
+ // Check to see if the browser returns only elements
+ // when doing getElementsByTagName("*")
+
+ // Create a fake element
+ var div = document.createElement("div");
+ div.appendChild( document.createComment("") );
+
+ // Make sure no comments are found
+ if ( div.getElementsByTagName("*").length > 0 ) {
+ Expr.find.TAG = function(match, context){
+ var results = context.getElementsByTagName(match[1]);
+
+ // Filter out possible comments
+ if ( match[1] === "*" ) {
+ var tmp = [];
+
+ for ( var i = 0; results[i]; i++ ) {
+ if ( results[i].nodeType === 1 ) {
+ tmp.push( results[i] );
+ }
+ }
+
+ results = tmp;
+ }
+
+ return results;
+ };
+ }
+
+ // Check to see if an attribute returns normalized href attributes
+ div.innerHTML = "<a href='#'></a>";
+ if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+ div.firstChild.getAttribute("href") !== "#" ) {
+ Expr.attrHandle.href = function(elem){
+ return elem.getAttribute("href", 2);
+ };
+ }
+
+ div = null; // release memory in IE
+})();
+
+if ( document.querySelectorAll ) {
+ (function(){
+ var oldSizzle = Sizzle, div = document.createElement("div");
+ div.innerHTML = "<p class='TEST'></p>";
+
+ // Safari can't handle uppercase or unicode characters when
+ // in quirks mode.
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+ return;
+ }
+
+ Sizzle = function(query, context, extra, seed){
+ context = context || document;
+
+ // Only use querySelectorAll on non-XML documents
+ // (ID selectors don't work in non-HTML documents)
+ if ( !seed && context.nodeType === 9 && !isXML(context) ) {
+ try {
+ return makeArray( context.querySelectorAll(query), extra );
+ } catch(e){}
+ }
+
+ return oldSizzle(query, context, extra, seed);
+ };
+
+ for ( var prop in oldSizzle ) {
+ Sizzle[ prop ] = oldSizzle[ prop ];
+ }
+
+ div = null; // release memory in IE
+ })();
+}
+
+(function(){
+ var div = document.createElement("div");
+
+ div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+ // Opera can't find a second classname (in 9.6)
+ // Also, make sure that getElementsByClassName actually exists
+ if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
+ return;
+ }
+
+ // Safari caches class attributes, doesn't catch changes (in 3.2)
+ div.lastChild.className = "e";
+
+ if ( div.getElementsByClassName("e").length === 1 ) {
+ return;
+ }
+
+ Expr.order.splice(1, 0, "CLASS");
+ Expr.find.CLASS = function(match, context, isXML) {
+ if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+ return context.getElementsByClassName(match[1]);
+ }
+ };
+
+ div = null; // release memory in IE
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ elem = elem[dir];
+ var match = false;
+
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 && !isXML ){
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+
+ if ( elem.nodeName.toLowerCase() === cur ) {
+ match = elem;
+ break;
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+ if ( elem ) {
+ elem = elem[dir];
+ var match = false;
+
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 ) {
+ if ( !isXML ) {
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+ if ( typeof cur !== "string" ) {
+ if ( elem === cur ) {
+ match = true;
+ break;
+ }
+
+ } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+ match = elem;
+ break;
+ }
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+var contains = document.compareDocumentPosition ? function(a, b){
+ return !!(a.compareDocumentPosition(b) & 16);
+} : function(a, b){
+ return a !== b && (a.contains ? a.contains(b) : true);
+};
+
+var isXML = function(elem){
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+var posProcess = function(selector, context){
+ var tmpSet = [], later = "", match,
+ root = context.nodeType ? [context] : context;
+
+ // Position selectors must be done after the filter
+ // And so must :not(positional) so we move all PSEUDOs to the end
+ while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+ later += match[0];
+ selector = selector.replace( Expr.match.PSEUDO, "" );
+ }
+
+ selector = Expr.relative[selector] ? selector + "*" : selector;
+
+ for ( var i = 0, l = root.length; i < l; i++ ) {
+ Sizzle( selector, root[i], tmpSet );
+ }
+
+ return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = getText;
+jQuery.isXMLDoc = isXML;
+jQuery.contains = contains;
+
+return;
+
+window.Sizzle = Sizzle;
+
+})();
+var runtil = /Until$/,
+ rparentsprev = /^(?:parents|prevUntil|prevAll)/,
+ // Note: This RegExp should be improved, or likely pulled from Sizzle
+ rmultiselector = /,/,
+ slice = Array.prototype.slice;
+
+// Implement the identical functionality for filter and not
+var winnow = function( elements, qualifier, keep ) {
+ if ( jQuery.isFunction( qualifier ) ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ return !!qualifier.call( elem, i, elem ) === keep;
+ });
+
+ } else if ( qualifier.nodeType ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ return (elem === qualifier) === keep;
+ });
+
+ } else if ( typeof qualifier === "string" ) {
+ var filtered = jQuery.grep(elements, function( elem ) {
+ return elem.nodeType === 1;
+ });
+
+ if ( isSimple.test( qualifier ) ) {
+ return jQuery.filter(qualifier, filtered, !keep);
+ } else {
+ qualifier = jQuery.filter( qualifier, filtered );
+ }
+ }
+
+ return jQuery.grep(elements, function( elem, i ) {
+ return (jQuery.inArray( elem, qualifier ) >= 0) === keep;
+ });
+};
+
+jQuery.fn.extend({
+ find: function( selector ) {
+ var ret = this.pushStack( "", "find", selector ), length = 0;
+
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ length = ret.length;
+ jQuery.find( selector, this[i], ret );
+
+ if ( i > 0 ) {
+ // Make sure that the results are unique
+ for ( var n = length; n < ret.length; n++ ) {
+ for ( var r = 0; r < length; r++ ) {
+ if ( ret[r] === ret[n] ) {
+ ret.splice(n--, 1);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ has: function( target ) {
+ var targets = jQuery( target );
+ return this.filter(function() {
+ for ( var i = 0, l = targets.length; i < l; i++ ) {
+ if ( jQuery.contains( this, targets[i] ) ) {
+ return true;
+ }
+ }
+ });
+ },
+
+ not: function( selector ) {
+ return this.pushStack( winnow(this, selector, false), "not", selector);
+ },
+
+ filter: function( selector ) {
+ return this.pushStack( winnow(this, selector, true), "filter", selector );
+ },
+
+ is: function( selector ) {
+ return !!selector && jQuery.filter( selector, this ).length > 0;
+ },
+
+ closest: function( selectors, context ) {
+ if ( jQuery.isArray( selectors ) ) {
+ var ret = [], cur = this[0], match, matches = {}, selector;
+
+ if ( cur && selectors.length ) {
+ for ( var i = 0, l = selectors.length; i < l; i++ ) {
+ selector = selectors[i];
+
+ if ( !matches[selector] ) {
+ matches[selector] = jQuery.expr.match.POS.test( selector ) ?
+ jQuery( selector, context || this.context ) :
+ selector;
+ }
+ }
+
+ while ( cur && cur.ownerDocument && cur !== context ) {
+ for ( selector in matches ) {
+ match = matches[selector];
+
+ if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) {
+ ret.push({ selector: selector, elem: cur });
+ delete matches[selector];
+ }
+ }
+ cur = cur.parentNode;
+ }
+ }
+
+ return ret;
+ }
+
+ var pos = jQuery.expr.match.POS.test( selectors ) ?
+ jQuery( selectors, context || this.context ) : null;
+
+ return this.map(function( i, cur ) {
+ while ( cur && cur.ownerDocument && cur !== context ) {
+ if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selectors) ) {
+ return cur;
+ }
+ cur = cur.parentNode;
+ }
+ return null;
+ });
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+ if ( !elem || typeof elem === "string" ) {
+ return jQuery.inArray( this[0],
+ // If it receives a string, the selector is used
+ // If it receives nothing, the siblings are used
+ elem ? jQuery( elem ) : this.parent().children() );
+ }
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem.jquery ? elem[0] : elem, this );
+ },
+
+ add: function( selector, context ) {
+ var set = typeof selector === "string" ?
+ jQuery( selector, context || this.context ) :
+ jQuery.makeArray( selector ),
+ all = jQuery.merge( this.get(), set );
+
+ return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
+ all :
+ jQuery.unique( all ) );
+ },
+
+ andSelf: function() {
+ return this.add( this.prevObject );
+ }
+});
+
+// A painfully simple check to see if an element is disconnected
+// from a document (should be improved, where feasible).
+function isDisconnected( node ) {
+ return !node || !node.parentNode || node.parentNode.nodeType === 11;
+}
+
+jQuery.each({
+ parent: function( elem ) {
+ var parent = elem.parentNode;
+ return parent && parent.nodeType !== 11 ? parent : null;
+ },
+ parents: function( elem ) {
+ return jQuery.dir( elem, "parentNode" );
+ },
+ parentsUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "parentNode", until );
+ },
+ next: function( elem ) {
+ return jQuery.nth( elem, 2, "nextSibling" );
+ },
+ prev: function( elem ) {
+ return jQuery.nth( elem, 2, "previousSibling" );
+ },
+ nextAll: function( elem ) {
+ return jQuery.dir( elem, "nextSibling" );
+ },
+ prevAll: function( elem ) {
+ return jQuery.dir( elem, "previousSibling" );
+ },
+ nextUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "nextSibling", until );
+ },
+ prevUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "previousSibling", until );
+ },
+ siblings: function( elem ) {
+ return jQuery.sibling( elem.parentNode.firstChild, elem );
+ },
+ children: function( elem ) {
+ return jQuery.sibling( elem.firstChild );
+ },
+ contents: function( elem ) {
+ return jQuery.nodeName( elem, "iframe" ) ?
+ elem.contentDocument || elem.contentWindow.document :
+ jQuery.makeArray( elem.childNodes );
+ }
+}, function( name, fn ) {
+ jQuery.fn[ name ] = function( until, selector ) {
+ var ret = jQuery.map( this, fn, until );
+
+ if ( !runtil.test( name ) ) {
+ selector = until;
+ }
+
+ if ( selector && typeof selector === "string" ) {
+ ret = jQuery.filter( selector, ret );
+ }
+
+ ret = this.length > 1 ? jQuery.unique( ret ) : ret;
+
+ if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
+ ret = ret.reverse();
+ }
+
+ return this.pushStack( ret, name, slice.call(arguments).join(",") );
+ };
+});
+
+jQuery.extend({
+ filter: function( expr, elems, not ) {
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return jQuery.find.matches(expr, elems);
+ },
+
+ dir: function( elem, dir, until ) {
+ var matched = [], cur = elem[dir];
+ while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+ if ( cur.nodeType === 1 ) {
+ matched.push( cur );
+ }
+ cur = cur[dir];
+ }
+ return matched;
+ },
+
+ nth: function( cur, result, dir, elem ) {
+ result = result || 1;
+ var num = 0;
+
+ for ( ; cur; cur = cur[dir] ) {
+ if ( cur.nodeType === 1 && ++num === result ) {
+ break;
+ }
+ }
+
+ return cur;
+ },
+
+ sibling: function( n, elem ) {
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType === 1 && n !== elem ) {
+ r.push( n );
+ }
+ }
+
+ return r;
+ }
+});
+var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
+ rleadingWhitespace = /^\s+/,
+ rxhtmlTag = /(<([\w:]+)[^>]*?)\/>/g,
+ rselfClosing = /^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,
+ rtagName = /<([\w:]+)/,
+ rtbody = /<tbody/i,
+ rhtml = /<|&#?\w+;/,
+ rnocache = /<script|<object|<embed|<option|<style/i,
+ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, // checked="checked" or checked (html5)
+ fcloseTag = function( all, front, tag ) {
+ return rselfClosing.test( tag ) ?
+ all :
+ front + "></" + tag + ">";
+ },
+ wrapMap = {
+ option: [ 1, "<select multiple='multiple'>", "</select>" ],
+ legend: [ 1, "<fieldset>", "</fieldset>" ],
+ thead: [ 1, "<table>", "</table>" ],
+ tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+ td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+ col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
+ area: [ 1, "<map>", "</map>" ],
+ _default: [ 0, "", "" ]
+ };
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// IE can't serialize <link> and <script> tags normally
+if ( !jQuery.support.htmlSerialize ) {
+ wrapMap._default = [ 1, "div<div>", "</div>" ];
+}
+
+jQuery.fn.extend({
+ text: function( text ) {
+ if ( jQuery.isFunction(text) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ self.text( text.call(this, i, self.text()) );
+ });
+ }
+
+ if ( typeof text !== "object" && text !== undefined ) {
+ return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
+ }
+
+ return jQuery.text( this );
+ },
+
+ wrapAll: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapAll( html.call(this, i) );
+ });
+ }
+
+ if ( this[0] ) {
+ // The elements to wrap the target around
+ var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+ if ( this[0].parentNode ) {
+ wrap.insertBefore( this[0] );
+ }
+
+ wrap.map(function() {
+ var elem = this;
+
+ while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+ elem = elem.firstChild;
+ }
+
+ return elem;
+ }).append(this);
+ }
+
+ return this;
+ },
+
+ wrapInner: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapInner( html.call(this, i) );
+ });
+ }
+
+ return this.each(function() {
+ var self = jQuery( this ), contents = self.contents();
+
+ if ( contents.length ) {
+ contents.wrapAll( html );
+
+ } else {
+ self.append( html );
+ }
+ });
+ },
+
+ wrap: function( html ) {
+ return this.each(function() {
+ jQuery( this ).wrapAll( html );
+ });
+ },
+
+ unwrap: function() {
+ return this.parent().each(function() {
+ if ( !jQuery.nodeName( this, "body" ) ) {
+ jQuery( this ).replaceWith( this.childNodes );
+ }
+ }).end();
+ },
+
+ append: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 ) {
+ this.appendChild( elem );
+ }
+ });
+ },
+
+ prepend: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 ) {
+ this.insertBefore( elem, this.firstChild );
+ }
+ });
+ },
+
+ before: function() {
+ if ( this[0] && this[0].parentNode ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this );
+ });
+ } else if ( arguments.length ) {
+ var set = jQuery(arguments[0]);
+ set.push.apply( set, this.toArray() );
+ return this.pushStack( set, "before", arguments );
+ }
+ },
+
+ after: function() {
+ if ( this[0] && this[0].parentNode ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this.nextSibling );
+ });
+ } else if ( arguments.length ) {
+ var set = this.pushStack( this, "after", arguments );
+ set.push.apply( set, jQuery(arguments[0]).toArray() );
+ return set;
+ }
+ },
+
+ // keepData is for internal use only--do not document
+ remove: function( selector, keepData ) {
+ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
+ if ( !keepData && elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ jQuery.cleanData( [ elem ] );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+ }
+ }
+
+ return this;
+ },
+
+ empty: function() {
+ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ }
+
+ // Remove any remaining nodes
+ while ( elem.firstChild ) {
+ elem.removeChild( elem.firstChild );
+ }
+ }
+
+ return this;
+ },
+
+ clone: function( events ) {
+ // Do the clone
+ var ret = this.map(function() {
+ if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
+ // IE copies events bound via attachEvent when
+ // using cloneNode. Calling detachEvent on the
+ // clone will also remove the events from the orignal
+ // In order to get around this, we use innerHTML.
+ // Unfortunately, this means some modifications to
+ // attributes in IE that are actually only stored
+ // as properties will not be copied (such as the
+ // the name attribute on an input).
+ var html = this.outerHTML, ownerDocument = this.ownerDocument;
+ if ( !html ) {
+ var div = ownerDocument.createElement("div");
+ div.appendChild( this.cloneNode(true) );
+ html = div.innerHTML;
+ }
+
+ return jQuery.clean([html.replace(rinlinejQuery, "")
+ // Handle the case in IE 8 where action=/test/> self-closes a tag
+ .replace(/=([^="'>\s]+\/)>/g, '="$1">')
+ .replace(rleadingWhitespace, "")], ownerDocument)[0];
+ } else {
+ return this.cloneNode(true);
+ }
+ });
+
+ // Copy the events from the original to the clone
+ if ( events === true ) {
+ cloneCopyEvent( this, ret );
+ cloneCopyEvent( this.find("*"), ret.find("*") );
+ }
+
+ // Return the cloned set
+ return ret;
+ },
+
+ html: function( value ) {
+ if ( value === undefined ) {
+ return this[0] && this[0].nodeType === 1 ?
+ this[0].innerHTML.replace(rinlinejQuery, "") :
+ null;
+
+ // See if we can take a shortcut and just use innerHTML
+ } else if ( typeof value === "string" && !rnocache.test( value ) &&
+ (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
+ !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
+
+ value = value.replace(rxhtmlTag, fcloseTag);
+
+ try {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( this[i].nodeType === 1 ) {
+ jQuery.cleanData( this[i].getElementsByTagName("*") );
+ this[i].innerHTML = value;
+ }
+ }
+
+ // If using innerHTML throws an exception, use the fallback method
+ } catch(e) {
+ this.empty().append( value );
+ }
+
+ } else if ( jQuery.isFunction( value ) ) {
+ this.each(function(i){
+ var self = jQuery(this), old = self.html();
+ self.empty().append(function(){
+ return value.call( this, i, old );
+ });
+ });
+
+ } else {
+ this.empty().append( value );
+ }
+
+ return this;
+ },
+
+ replaceWith: function( value ) {
+ if ( this[0] && this[0].parentNode ) {
+ // Make sure that the elements are removed from the DOM before they are inserted
+ // this can help fix replacing a parent with child elements
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function(i) {
+ var self = jQuery(this), old = self.html();
+ self.replaceWith( value.call( this, i, old ) );
+ });
+ }
+
+ if ( typeof value !== "string" ) {
+ value = jQuery(value).detach();
+ }
+
+ return this.each(function() {
+ var next = this.nextSibling, parent = this.parentNode;
+
+ jQuery(this).remove();
+
+ if ( next ) {
+ jQuery(next).before( value );
+ } else {
+ jQuery(parent).append( value );
+ }
+ });
+ } else {
+ return this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value );
+ }
+ },
+
+ detach: function( selector ) {
+ return this.remove( selector, true );
+ },
+
+ domManip: function( args, table, callback ) {
+ var results, first, value = args[0], scripts = [], fragment, parent;
+
+ // We can't cloneNode fragments that contain checked, in WebKit
+ if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
+ return this.each(function() {
+ jQuery(this).domManip( args, table, callback, true );
+ });
+ }
+
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ args[0] = value.call(this, i, table ? self.html() : undefined);
+ self.domManip( args, table, callback );
+ });
+ }
+
+ if ( this[0] ) {
+ parent = value && value.parentNode;
+
+ // If we're in a fragment, just use that instead of building a new one
+ if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
+ results = { fragment: parent };
+
+ } else {
+ results = buildFragment( args, this, scripts );
+ }
+
+ fragment = results.fragment;
+
+ if ( fragment.childNodes.length === 1 ) {
+ first = fragment = fragment.firstChild;
+ } else {
+ first = fragment.firstChild;
+ }
+
+ if ( first ) {
+ table = table && jQuery.nodeName( first, "tr" );
+
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ callback.call(
+ table ?
+ root(this[i], first) :
+ this[i],
+ i > 0 || results.cacheable || this.length > 1 ?
+ fragment.cloneNode(true) :
+ fragment
+ );
+ }
+ }
+
+ if ( scripts.length ) {
+ jQuery.each( scripts, evalScript );
+ }
+ }
+
+ return this;
+
+ function root( elem, cur ) {
+ return jQuery.nodeName(elem, "table") ?
+ (elem.getElementsByTagName("tbody")[0] ||
+ elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
+ elem;
+ }
+ }
+});
+
+function cloneCopyEvent(orig, ret) {
+ var i = 0;
+
+ ret.each(function() {
+ if ( this.nodeName !== (orig[i] && orig[i].nodeName) ) {
+ return;
+ }
+
+ var oldData = jQuery.data( orig[i++] ), curData = jQuery.data( this, oldData ), events = oldData && oldData.events;
+
+ if ( events ) {
+ delete curData.handle;
+ curData.events = {};
+
+ for ( var type in events ) {
+ for ( var handler in events[ type ] ) {
+ jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
+ }
+ }
+ }
+ });
+}
+
+function buildFragment( args, nodes, scripts ) {
+ var fragment, cacheable, cacheresults,
+ doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);
+
+ // Only cache "small" (1/2 KB) strings that are associated with the main document
+ // Cloning options loses the selected state, so don't cache them
+ // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
+ // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
+ if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
+ !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
+
+ cacheable = true;
+ cacheresults = jQuery.fragments[ args[0] ];
+ if ( cacheresults ) {
+ if ( cacheresults !== 1 ) {
+ fragment = cacheresults;
+ }
+ }
+ }
+
+ if ( !fragment ) {
+ fragment = doc.createDocumentFragment();
+ jQuery.clean( args, doc, fragment, scripts );
+ }
+
+ if ( cacheable ) {
+ jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
+ }
+
+ return { fragment: fragment, cacheable: cacheable };
+}
+
+jQuery.fragments = {};
+
+jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+}, function( name, original ) {
+ jQuery.fn[ name ] = function( selector ) {
+ var ret = [], insert = jQuery( selector ),
+ parent = this.length === 1 && this[0].parentNode;
+
+ if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
+ insert[ original ]( this[0] );
+ return this;
+
+ } else {
+ for ( var i = 0, l = insert.length; i < l; i++ ) {
+ var elems = (i > 0 ? this.clone(true) : this).get();
+ jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
+ ret = ret.concat( elems );
+ }
+
+ return this.pushStack( ret, name, insert.selector );
+ }
+ };
+});
+
+jQuery.extend({
+ clean: function( elems, context, fragment, scripts ) {
+ context = context || document;
+
+ // !context.createElement fails in IE with an error but returns typeof 'object'
+ if ( typeof context.createElement === "undefined" ) {
+ context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+ }
+
+ var ret = [];
+
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ if ( typeof elem === "number" ) {
+ elem += "";
+ }
+
+ if ( !elem ) {
+ continue;
+ }
+
+ // Convert html string into DOM nodes
+ if ( typeof elem === "string" && !rhtml.test( elem ) ) {
+ elem = context.createTextNode( elem );
+
+ } else if ( typeof elem === "string" ) {
+ // Fix "XHTML"-style tags in all browsers
+ elem = elem.replace(rxhtmlTag, fcloseTag);
+
+ // Trim whitespace, otherwise indexOf won't work as expected
+ var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
+ wrap = wrapMap[ tag ] || wrapMap._default,
+ depth = wrap[0],
+ div = context.createElement("div");
+
+ // Go to html and back, then peel off extra wrappers
+ div.innerHTML = wrap[1] + elem + wrap[2];
+
+ // Move to the right depth
+ while ( depth-- ) {
+ div = div.lastChild;
+ }
+
+ // Remove IE's autoinserted <tbody> from table fragments
+ if ( !jQuery.support.tbody ) {
+
+ // String was a <table>, *may* have spurious <tbody>
+ var hasBody = rtbody.test(elem),
+ tbody = tag === "table" && !hasBody ?
+ div.firstChild && div.firstChild.childNodes :
+
+ // String was a bare <thead> or <tfoot>
+ wrap[1] === "<table>" && !hasBody ?
+ div.childNodes :
+ [];
+
+ for ( var j = tbody.length - 1; j >= 0 ; --j ) {
+ if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
+ tbody[ j ].parentNode.removeChild( tbody[ j ] );
+ }
+ }
+
+ }
+
+ // IE completely kills leading whitespace when innerHTML is used
+ if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+ div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
+ }
+
+ elem = div.childNodes;
+ }
+
+ if ( elem.nodeType ) {
+ ret.push( elem );
+ } else {
+ ret = jQuery.merge( ret, elem );
+ }
+ }
+
+ if ( fragment ) {
+ for ( var i = 0; ret[i]; i++ ) {
+ if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
+ scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
+
+ } else {
+ if ( ret[i].nodeType === 1 ) {
+ ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
+ }
+ fragment.appendChild( ret[i] );
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ cleanData: function( elems ) {
+ var data, id, cache = jQuery.cache,
+ special = jQuery.event.special,
+ deleteExpando = jQuery.support.deleteExpando;
+
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ id = elem[ jQuery.expando ];
+
+ if ( id ) {
+ data = cache[ id ];
+
+ if ( data.events ) {
+ for ( var type in data.events ) {
+ if ( special[ type ] ) {
+ jQuery.event.remove( elem, type );
+
+ } else {
+ removeEvent( elem, type, data.handle );
+ }
+ }
+ }
+
+ if ( deleteExpando ) {
+ delete elem[ jQuery.expando ];
+
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( jQuery.expando );
+ }
+
+ delete cache[ id ];
+ }
+ }
+ }
+});
+// exclude the following css properties to add px
+var rexclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
+ ralpha = /alpha\([^)]*\)/,
+ ropacity = /opacity=([^)]*)/,
+ rfloat = /float/i,
+ rdashAlpha = /-([a-z])/ig,
+ rupper = /([A-Z])/g,
+ rnumpx = /^-?\d+(?:px)?$/i,
+ rnum = /^-?\d/,
+
+ cssShow = { position: "absolute", visibility: "hidden", display:"block" },
+ cssWidth = [ "Left", "Right" ],
+ cssHeight = [ "Top", "Bottom" ],
+
+ // cache check for defaultView.getComputedStyle
+ getComputedStyle = document.defaultView && document.defaultView.getComputedStyle,
+ // normalize float css property
+ styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat",
+ fcamelCase = function( all, letter ) {
+ return letter.toUpperCase();
+ };
+
+jQuery.fn.css = function( name, value ) {
+ return access( this, name, value, true, function( elem, name, value ) {
+ if ( value === undefined ) {
+ return jQuery.curCSS( elem, name );
+ }
+
+ if ( typeof value === "number" && !rexclude.test(name) ) {
+ value += "px";
+ }
+
+ jQuery.style( elem, name, value );
+ });
+};
+
+jQuery.extend({
+ style: function( elem, name, value ) {
+ // don't set styles on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return undefined;
+ }
+
+ // ignore negative width and height values #1599
+ if ( (name === "width" || name === "height") && parseFloat(value) < 0 ) {
+ value = undefined;
+ }
+
+ var style = elem.style || elem, set = value !== undefined;
+
+ // IE uses filters for opacity
+ if ( !jQuery.support.opacity && name === "opacity" ) {
+ if ( set ) {
+ // IE has trouble with opacity if it does not have layout
+ // Force it by setting the zoom level
+ style.zoom = 1;
+
+ // Set the alpha filter to set the opacity
+ var opacity = parseInt( value, 10 ) + "" === "NaN" ? "" : "alpha(opacity=" + value * 100 + ")";
+ var filter = style.filter || jQuery.curCSS( elem, "filter" ) || "";
+ style.filter = ralpha.test(filter) ? filter.replace(ralpha, opacity) : opacity;
+ }
+
+ return style.filter && style.filter.indexOf("opacity=") >= 0 ?
+ (parseFloat( ropacity.exec(style.filter)[1] ) / 100) + "":
+ "";
+ }
+
+ // Make sure we're using the right name for getting the float value
+ if ( rfloat.test( name ) ) {
+ name = styleFloat;
+ }
+
+ name = name.replace(rdashAlpha, fcamelCase);
+
+ if ( set ) {
+ style[ name ] = value;
+ }
+
+ return style[ name ];
+ },
+
+ css: function( elem, name, force, extra ) {
+ if ( name === "width" || name === "height" ) {
+ var val, props = cssShow, which = name === "width" ? cssWidth : cssHeight;
+
+ function getWH() {
+ val = name === "width" ? elem.offsetWidth : elem.offsetHeight;
+
+ if ( extra === "border" ) {
+ return;
+ }
+
+ jQuery.each( which, function() {
+ if ( !extra ) {
+ val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
+ }
+
+ if ( extra === "margin" ) {
+ val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
+ } else {
+ val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
+ }
+ });
+ }
+
+ if ( elem.offsetWidth !== 0 ) {
+ getWH();
+ } else {
+ jQuery.swap( elem, props, getWH );
+ }
+
+ return Math.max(0, Math.round(val));
+ }
+
+ return jQuery.curCSS( elem, name, force );
+ },
+
+ curCSS: function( elem, name, force ) {
+ var ret, style = elem.style, filter;
+
+ // IE uses filters for opacity
+ if ( !jQuery.support.opacity && name === "opacity" && elem.currentStyle ) {
+ ret = ropacity.test(elem.currentStyle.filter || "") ?
+ (parseFloat(RegExp.$1) / 100) + "" :
+ "";
+
+ return ret === "" ?
+ "1" :
+ ret;
+ }
+
+ // Make sure we're using the right name for getting the float value
+ if ( rfloat.test( name ) ) {
+ name = styleFloat;
+ }
+
+ if ( !force && style && style[ name ] ) {
+ ret = style[ name ];
+
+ } else if ( getComputedStyle ) {
+
+ // Only "float" is needed here
+ if ( rfloat.test( name ) ) {
+ name = "float";
+ }
+
+ name = name.replace( rupper, "-$1" ).toLowerCase();
+
+ var defaultView = elem.ownerDocument.defaultView;
+
+ if ( !defaultView ) {
+ return null;
+ }
+
+ var computedStyle = defaultView.getComputedStyle( elem, null );
+
+ if ( computedStyle ) {
+ ret = computedStyle.getPropertyValue( name );
+ }
+
+ // We should always get a number back from opacity
+ if ( name === "opacity" && ret === "" ) {
+ ret = "1";
+ }
+
+ } else if ( elem.currentStyle ) {
+ var camelCase = name.replace(rdashAlpha, fcamelCase);
+
+ ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
+
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
+ if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
+ // Remember the original values
+ var left = style.left, rsLeft = elem.runtimeStyle.left;
+
+ // Put in the new values to get a computed value out
+ elem.runtimeStyle.left = elem.currentStyle.left;
+ style.left = camelCase === "fontSize" ? "1em" : (ret || 0);
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ elem.runtimeStyle.left = rsLeft;
+ }
+ }
+
+ return ret;
+ },
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations
+ swap: function( elem, options, callback ) {
+ var old = {};
+
+ // Remember the old values, and insert the new ones
+ for ( var name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ callback.call( elem );
+
+ // Revert the old values
+ for ( var name in options ) {
+ elem.style[ name ] = old[ name ];
+ }
+ }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.hidden = function( elem ) {
+ var width = elem.offsetWidth, height = elem.offsetHeight,
+ skip = elem.nodeName.toLowerCase() === "tr";
+
+ return width === 0 && height === 0 && !skip ?
+ true :
+ width > 0 && height > 0 && !skip ?
+ false :
+ jQuery.curCSS(elem, "display") === "none";
+ };
+
+ jQuery.expr.filters.visible = function( elem ) {
+ return !jQuery.expr.filters.hidden( elem );
+ };
+}
+var jsc = now(),
+ rscript = /<script(.|\s)*?\/script>/gi,
+ rselectTextarea = /select|textarea/i,
+ rinput = /color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,
+ jsre = /=\?(&|$)/,
+ rquery = /\?/,
+ rts = /(\?|&)_=.*?(&|$)/,
+ rurl = /^(\w+:)?\/\/([^\/?#]+)/,
+ r20 = /%20/g,
+
+ // Keep a copy of the old load method
+ _load = jQuery.fn.load;
+
+jQuery.fn.extend({
+ load: function( url, params, callback ) {
+ if ( typeof url !== "string" ) {
+ return _load.call( this, url );
+
+ // Don't do a request if no elements are being requested
+ } else if ( !this.length ) {
+ return this;
+ }
+
+ var off = url.indexOf(" ");
+ if ( off >= 0 ) {
+ var selector = url.slice(off, url.length);
+ url = url.slice(0, off);
+ }
+
+ // Default to a GET request
+ var type = "GET";
+
+ // If the second parameter was provided
+ if ( params ) {
+ // If it's a function
+ if ( jQuery.isFunction( params ) ) {
+ // We assume that it's the callback
+ callback = params;
+ params = null;
+
+ // Otherwise, build a param string
+ } else if ( typeof params === "object" ) {
+ params = jQuery.param( params, jQuery.ajaxSettings.traditional );
+ type = "POST";
+ }
+ }
+
+ var self = this;
+
+ // Request the remote document
+ jQuery.ajax({
+ url: url,
+ type: type,
+ dataType: "html",
+ data: params,
+ complete: function( res, status ) {
+ // If successful, inject the HTML into all the matched elements
+ if ( status === "success" || status === "notmodified" ) {
+ // See if a selector was specified
+ self.html( selector ?
+ // Create a dummy div to hold the results
+ jQuery("<div />")
+ // inject the contents of the document in, removing the scripts
+ // to avoid any 'Permission Denied' errors in IE
+ .append(res.responseText.replace(rscript, ""))
+
+ // Locate the specified elements
+ .find(selector) :
+
+ // If not, just inject the full result
+ res.responseText );
+ }
+
+ if ( callback ) {
+ self.each( callback, [res.responseText, status, res] );
+ }
+ }
+ });
+
+ return this;
+ },
+
+ serialize: function() {
+ return jQuery.param(this.serializeArray());
+ },
+ serializeArray: function() {
+ return this.map(function() {
+ return this.elements ? jQuery.makeArray(this.elements) : this;
+ })
+ .filter(function() {
+ return this.name && !this.disabled &&
+ (this.checked || rselectTextarea.test(this.nodeName) ||
+ rinput.test(this.type));
+ })
+ .map(function( i, elem ) {
+ var val = jQuery(this).val();
+
+ return val == null ?
+ null :
+ jQuery.isArray(val) ?
+ jQuery.map( val, function( val, i ) {
+ return { name: elem.name, value: val };
+ }) :
+ { name: elem.name, value: val };
+ }).get();
+ }
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), function( i, o ) {
+ jQuery.fn[o] = function( f ) {
+ return this.bind(o, f);
+ };
+});
+
+jQuery.extend({
+
+ get: function( url, data, callback, type ) {
+ // shift arguments if data argument was omited
+ if ( jQuery.isFunction( data ) ) {
+ type = type || callback;
+ callback = data;
+ data = null;
+ }
+
+ return jQuery.ajax({
+ type: "GET",
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ },
+
+ getScript: function( url, callback ) {
+ return jQuery.get(url, null, callback, "script");
+ },
+
+ getJSON: function( url, data, callback ) {
+ return jQuery.get(url, data, callback, "json");
+ },
+
+ post: function( url, data, callback, type ) {
+ // shift arguments if data argument was omited
+ if ( jQuery.isFunction( data ) ) {
+ type = type || callback;
+ callback = data;
+ data = {};
+ }
+
+ return jQuery.ajax({
+ type: "POST",
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ },
+
+ ajaxSetup: function( settings ) {
+ jQuery.extend( jQuery.ajaxSettings, settings );
+ },
+
+ ajaxSettings: {
+ url: location.href,
+ global: true,
+ type: "GET",
+ contentType: "application/x-www-form-urlencoded",
+ processData: true,
+ async: true,
+ /*
+ timeout: 0,
+ data: null,
+ username: null,
+ password: null,
+ traditional: false,
+ */
+ // Create the request object; Microsoft failed to properly
+ // implement the XMLHttpRequest in IE7 (can't request local files),
+ // so we use the ActiveXObject when it is available
+ // This function can be overriden by calling jQuery.ajaxSetup
+ xhr: window.XMLHttpRequest && (window.location.protocol !== "file:" || !window.ActiveXObject) ?
+ function() {
+ return new window.XMLHttpRequest();
+ } :
+ function() {
+ try {
+ return new window.ActiveXObject("Microsoft.XMLHTTP");
+ } catch(e) {}
+ },
+ accepts: {
+ xml: "application/xml, text/xml",
+ html: "text/html",
+ script: "text/javascript, application/javascript",
+ json: "application/json, text/javascript",
+ text: "text/plain",
+ _default: "*/*"
+ }
+ },
+
+ // Last-Modified header cache for next request
+ lastModified: {},
+ etag: {},
+
+ ajax: function( origSettings ) {
+ var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings);
+
+ var jsonp, status, data,
+ callbackContext = origSettings && origSettings.context || s,
+ type = s.type.toUpperCase();
+
+ // convert data if not already a string
+ if ( s.data && s.processData && typeof s.data !== "string" ) {
+ s.data = jQuery.param( s.data, s.traditional );
+ }
+
+ // Handle JSONP Parameter Callbacks
+ if ( s.dataType === "jsonp" ) {
+ if ( type === "GET" ) {
+ if ( !jsre.test( s.url ) ) {
+ s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?";
+ }
+ } else if ( !s.data || !jsre.test(s.data) ) {
+ s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
+ }
+ s.dataType = "json";
+ }
+
+ // Build temporary JSONP function
+ if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
+ jsonp = s.jsonpCallback || ("jsonp" + jsc++);
+
+ // Replace the =? sequence both in the query string and the data
+ if ( s.data ) {
+ s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
+ }
+
+ s.url = s.url.replace(jsre, "=" + jsonp + "$1");
+
+ // We need to make sure
+ // that a JSONP style response is executed properly
+ s.dataType = "script";
+
+ // Handle JSONP-style loading
+ window[ jsonp ] = window[ jsonp ] || function( tmp ) {
+ data = tmp;
+ success();
+ complete();
+ // Garbage collect
+ window[ jsonp ] = undefined;
+
+ try {
+ delete window[ jsonp ];
+ } catch(e) {}
+
+ if ( head ) {
+ head.removeChild( script );
+ }
+ };
+ }
+
+ if ( s.dataType === "script" && s.cache === null ) {
+ s.cache = false;
+ }
+
+ if ( s.cache === false && type === "GET" ) {
+ var ts = now();
+
+ // try replacing _= if it is there
+ var ret = s.url.replace(rts, "$1_=" + ts + "$2");
+
+ // if nothing was replaced, add timestamp to the end
+ s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : "");
+ }
+
+ // If data is available, append data to url for get requests
+ if ( s.data && type === "GET" ) {
+ s.url += (rquery.test(s.url) ? "&" : "?") + s.data;
+ }
+
+ // Watch for a new set of requests
+ if ( s.global && ! jQuery.active++ ) {
+ jQuery.event.trigger( "ajaxStart" );
+ }
+
+ // Matches an absolute URL, and saves the domain
+ var parts = rurl.exec( s.url ),
+ remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);
+
+ // If we're requesting a remote document
+ // and trying to load JSON or Script with a GET
+ if ( s.dataType === "script" && type === "GET" && remote ) {
+ var head = document.getElementsByTagName("head")[0] || document.documentElement;
+ var script = document.createElement("script");
+ script.src = s.url;
+ if ( s.scriptCharset ) {
+ script.charset = s.scriptCharset;
+ }
+
+ // Handle Script loading
+ if ( !jsonp ) {
+ var done = false;
+
+ // Attach handlers for all browsers
+ script.onload = script.onreadystatechange = function() {
+ if ( !done && (!this.readyState ||
+ this.readyState === "loaded" || this.readyState === "complete") ) {
+ done = true;
+ success();
+ complete();
+
+ // Handle memory leak in IE
+ script.onload = script.onreadystatechange = null;
+ if ( head && script.parentNode ) {
+ head.removeChild( script );
+ }
+ }
+ };
+ }
+
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709 and #4378).
+ head.insertBefore( script, head.firstChild );
+
+ // We handle everything using the script element injection
+ return undefined;
+ }
+
+ var requestDone = false;
+
+ // Create the request object
+ var xhr = s.xhr();
+
+ if ( !xhr ) {
+ return;
+ }
+
+ // Open the socket
+ // Passing null username, generates a login popup on Opera (#2865)
+ if ( s.username ) {
+ xhr.open(type, s.url, s.async, s.username, s.password);
+ } else {
+ xhr.open(type, s.url, s.async);
+ }
+
+ // Need an extra try/catch for cross domain requests in Firefox 3
+ try {
+ // Set the correct header, if data is being sent
+ if ( s.data || origSettings && origSettings.contentType ) {
+ xhr.setRequestHeader("Content-Type", s.contentType);
+ }
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+ if ( jQuery.lastModified[s.url] ) {
+ xhr.setRequestHeader("If-Modified-Since", jQuery.lastModified[s.url]);
+ }
+
+ if ( jQuery.etag[s.url] ) {
+ xhr.setRequestHeader("If-None-Match", jQuery.etag[s.url]);
+ }
+ }
+
+ // Set header so the called script knows that it's an XMLHttpRequest
+ // Only send the header if it's not a remote XHR
+ if ( !remote ) {
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+ }
+
+ // Set the Accepts header for the server, depending on the dataType
+ xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
+ s.accepts[ s.dataType ] + ", */*" :
+ s.accepts._default );
+ } catch(e) {}
+
+ // Allow custom headers/mimetypes and early abort
+ if ( s.beforeSend && s.beforeSend.call(callbackContext, xhr, s) === false ) {
+ // Handle the global AJAX counter
+ if ( s.global && ! --jQuery.active ) {
+ jQuery.event.trigger( "ajaxStop" );
+ }
+
+ // close opended socket
+ xhr.abort();
+ return false;
+ }
+
+ if ( s.global ) {
+ trigger("ajaxSend", [xhr, s]);
+ }
+
+ // Wait for a response to come back
+ var onreadystatechange = xhr.onreadystatechange = function( isTimeout ) {
+ // The request was aborted
+ if ( !xhr || xhr.readyState === 0 || isTimeout === "abort" ) {
+ // Opera doesn't call onreadystatechange before this point
+ // so we simulate the call
+ if ( !requestDone ) {
+ complete();
+ }
+
+ requestDone = true;
+ if ( xhr ) {
+ xhr.onreadystatechange = jQuery.noop;
+ }
+
+ // The transfer is complete and the data is available, or the request timed out
+ } else if ( !requestDone && xhr && (xhr.readyState === 4 || isTimeout === "timeout") ) {
+ requestDone = true;
+ xhr.onreadystatechange = jQuery.noop;
+
+ status = isTimeout === "timeout" ?
+ "timeout" :
+ !jQuery.httpSuccess( xhr ) ?
+ "error" :
+ s.ifModified && jQuery.httpNotModified( xhr, s.url ) ?
+ "notmodified" :
+ "success";
+
+ var errMsg;
+
+ if ( status === "success" ) {
+ // Watch for, and catch, XML document parse errors
+ try {
+ // process the data (runs the xml through httpData regardless of callback)
+ data = jQuery.httpData( xhr, s.dataType, s );
+ } catch(err) {
+ status = "parsererror";
+ errMsg = err;
+ }
+ }
+
+ // Make sure that the request was successful or notmodified
+ if ( status === "success" || status === "notmodified" ) {
+ // JSONP handles its own success callback
+ if ( !jsonp ) {
+ success();
+ }
+ } else {
+ jQuery.handleError(s, xhr, status, errMsg);
+ }
+
+ // Fire the complete handlers
+ complete();
+
+ if ( isTimeout === "timeout" ) {
+ xhr.abort();
+ }
+
+ // Stop memory leaks
+ if ( s.async ) {
+ xhr = null;
+ }
+ }
+ };
+
+ // Override the abort handler, if we can (IE doesn't allow it, but that's OK)
+ // Opera doesn't fire onreadystatechange at all on abort
+ try {
+ var oldAbort = xhr.abort;
+ xhr.abort = function() {
+ if ( xhr ) {
+ oldAbort.call( xhr );
+ }
+
+ onreadystatechange( "abort" );
+ };
+ } catch(e) { }
+
+ // Timeout checker
+ if ( s.async && s.timeout > 0 ) {
+ setTimeout(function() {
+ // Check to see if the request is still happening
+ if ( xhr && !requestDone ) {
+ onreadystatechange( "timeout" );
+ }
+ }, s.timeout);
+ }
+
+ // Send the data
+ try {
+ xhr.send( type === "POST" || type === "PUT" || type === "DELETE" ? s.data : null );
+ } catch(e) {
+ jQuery.handleError(s, xhr, null, e);
+ // Fire the complete handlers
+ complete();
+ }
+
+ // firefox 1.5 doesn't fire statechange for sync requests
+ if ( !s.async ) {
+ onreadystatechange();
+ }
+
+ function success() {
+ // If a local callback was specified, fire it and pass it the data
+ if ( s.success ) {
+ s.success.call( callbackContext, data, status, xhr );
+ }
+
+ // Fire the global callback
+ if ( s.global ) {
+ trigger( "ajaxSuccess", [xhr, s] );
+ }
+ }
+
+ function complete() {
+ // Process result
+ if ( s.complete ) {
+ s.complete.call( callbackContext, xhr, status);
+ }
+
+ // The request was completed
+ if ( s.global ) {
+ trigger( "ajaxComplete", [xhr, s] );
+ }
+
+ // Handle the global AJAX counter
+ if ( s.global && ! --jQuery.active ) {
+ jQuery.event.trigger( "ajaxStop" );
+ }
+ }
+
+ function trigger(type, args) {
+ (s.context ? jQuery(s.context) : jQuery.event).trigger(type, args);
+ }
+
+ // return XMLHttpRequest to allow aborting the request etc.
+ return xhr;
+ },
+
+ handleError: function( s, xhr, status, e ) {
+ // If a local callback was specified, fire it
+ if ( s.error ) {
+ s.error.call( s.context || s, xhr, status, e );
+ }
+
+ // Fire the global callback
+ if ( s.global ) {
+ (s.context ? jQuery(s.context) : jQuery.event).trigger( "ajaxError", [xhr, s, e] );
+ }
+ },
+
+ // Counter for holding the number of active queries
+ active: 0,
+
+ // Determines if an XMLHttpRequest was successful or not
+ httpSuccess: function( xhr ) {
+ try {
+ // IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
+ return !xhr.status && location.protocol === "file:" ||
+ // Opera returns 0 when status is 304
+ ( xhr.status >= 200 && xhr.status < 300 ) ||
+ xhr.status === 304 || xhr.status === 1223 || xhr.status === 0;
+ } catch(e) {}
+
+ return false;
+ },
+
+ // Determines if an XMLHttpRequest returns NotModified
+ httpNotModified: function( xhr, url ) {
+ var lastModified = xhr.getResponseHeader("Last-Modified"),
+ etag = xhr.getResponseHeader("Etag");
+
+ if ( lastModified ) {
+ jQuery.lastModified[url] = lastModified;
+ }
+
+ if ( etag ) {
+ jQuery.etag[url] = etag;
+ }
+
+ // Opera returns 0 when status is 304
+ return xhr.status === 304 || xhr.status === 0;
+ },
+
+ httpData: function( xhr, type, s ) {
+ var ct = xhr.getResponseHeader("content-type") || "",
+ xml = type === "xml" || !type && ct.indexOf("xml") >= 0,
+ data = xml ? xhr.responseXML : xhr.responseText;
+
+ if ( xml && data.documentElement.nodeName === "parsererror" ) {
+ jQuery.error( "parsererror" );
+ }
+
+ // Allow a pre-filtering function to sanitize the response
+ // s is checked to keep backwards compatibility
+ if ( s && s.dataFilter ) {
+ data = s.dataFilter( data, type );
+ }
+
+ // The filter can actually parse the response
+ if ( typeof data === "string" ) {
+ // Get the JavaScript object, if JSON is used.
+ if ( type === "json" || !type && ct.indexOf("json") >= 0 ) {
+ data = jQuery.parseJSON( data );
+
+ // If the type is "script", eval it in global context
+ } else if ( type === "script" || !type && ct.indexOf("javascript") >= 0 ) {
+ jQuery.globalEval( data );
+ }
+ }
+
+ return data;
+ },
+
+ // Serialize an array of form elements or a set of
+ // key/values into a query string
+ param: function( a, traditional ) {
+ var s = [];
+
+ // Set traditional to true for jQuery <= 1.3.2 behavior.
+ if ( traditional === undefined ) {
+ traditional = jQuery.ajaxSettings.traditional;
+ }
+
+ // If an array was passed in, assume that it is an array of form elements.
+ if ( jQuery.isArray(a) || a.jquery ) {
+ // Serialize the form elements
+ jQuery.each( a, function() {
+ add( this.name, this.value );
+ });
+
+ } else {
+ // If traditional, encode the "old" way (the way 1.3.2 or older
+ // did it), otherwise encode params recursively.
+ for ( var prefix in a ) {
+ buildParams( prefix, a[prefix] );
+ }
+ }
+
+ // Return the resulting serialization
+ return s.join("&").replace(r20, "+");
+
+ function buildParams( prefix, obj ) {
+ if ( jQuery.isArray(obj) ) {
+ // Serialize array item.
+ jQuery.each( obj, function( i, v ) {
+ if ( traditional || /\[\]$/.test( prefix ) ) {
+ // Treat each array item as a scalar.
+ add( prefix, v );
+ } else {
+ // If array item is non-scalar (array or object), encode its
+ // numeric index to resolve deserialization ambiguity issues.
+ // Note that rack (as of 1.0.0) can't currently deserialize
+ // nested arrays properly, and attempting to do so may cause
+ // a server error. Possible fixes are to modify rack's
+ // deserialization algorithm or to provide an option or flag
+ // to force array serialization to be shallow.
+ buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v );
+ }
+ });
+
+ } else if ( !traditional && obj != null && typeof obj === "object" ) {
+ // Serialize object item.
+ jQuery.each( obj, function( k, v ) {
+ buildParams( prefix + "[" + k + "]", v );
+ });
+
+ } else {
+ // Serialize scalar item.
+ add( prefix, obj );
+ }
+ }
+
+ function add( key, value ) {
+ // If value is a function, invoke it and return its value
+ value = jQuery.isFunction(value) ? value() : value;
+ s[ s.length ] = encodeURIComponent(key) + "=" + encodeURIComponent(value);
+ }
+ }
+});
+var elemdisplay = {},
+ rfxtypes = /toggle|show|hide/,
+ rfxnum = /^([+-]=)?([\d+-.]+)(.*)$/,
+ timerId,
+ fxAttrs = [
+ // height animations
+ [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
+ // width animations
+ [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
+ // opacity animations
+ [ "opacity" ]
+ ];
+
+jQuery.fn.extend({
+ show: function( speed, callback ) {
+ if ( speed || speed === 0) {
+ return this.animate( genFx("show", 3), speed, callback);
+
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ var old = jQuery.data(this[i], "olddisplay");
+
+ this[i].style.display = old || "";
+
+ if ( jQuery.css(this[i], "display") === "none" ) {
+ var nodeName = this[i].nodeName, display;
+
+ if ( elemdisplay[ nodeName ] ) {
+ display = elemdisplay[ nodeName ];
+
+ } else {
+ var elem = jQuery("<" + nodeName + " />").appendTo("body");
+
+ display = elem.css("display");
+
+ if ( display === "none" ) {
+ display = "block";
+ }
+
+ elem.remove();
+
+ elemdisplay[ nodeName ] = display;
+ }
+
+ jQuery.data(this[i], "olddisplay", display);
+ }
+ }
+
+ // Set the display of the elements in a second loop
+ // to avoid the constant reflow
+ for ( var j = 0, k = this.length; j < k; j++ ) {
+ this[j].style.display = jQuery.data(this[j], "olddisplay") || "";
+ }
+
+ return this;
+ }
+ },
+
+ hide: function( speed, callback ) {
+ if ( speed || speed === 0 ) {
+ return this.animate( genFx("hide", 3), speed, callback);
+
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ var old = jQuery.data(this[i], "olddisplay");
+ if ( !old && old !== "none" ) {
+ jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
+ }
+ }
+
+ // Set the display of the elements in a second loop
+ // to avoid the constant reflow
+ for ( var j = 0, k = this.length; j < k; j++ ) {
+ this[j].style.display = "none";
+ }
+
+ return this;
+ }
+ },
+
+ // Save the old toggle function
+ _toggle: jQuery.fn.toggle,
+
+ toggle: function( fn, fn2 ) {
+ var bool = typeof fn === "boolean";
+
+ if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
+ this._toggle.apply( this, arguments );
+
+ } else if ( fn == null || bool ) {
+ this.each(function() {
+ var state = bool ? fn : jQuery(this).is(":hidden");
+ jQuery(this)[ state ? "show" : "hide" ]();
+ });
+
+ } else {
+ this.animate(genFx("toggle", 3), fn, fn2);
+ }
+
+ return this;
+ },
+
+ fadeTo: function( speed, to, callback ) {
+ return this.filter(":hidden").css("opacity", 0).show().end()
+ .animate({opacity: to}, speed, callback);
+ },
+
+ animate: function( prop, speed, easing, callback ) {
+ var optall = jQuery.speed(speed, easing, callback);
+
+ if ( jQuery.isEmptyObject( prop ) ) {
+ return this.each( optall.complete );
+ }
+
+ return this[ optall.queue === false ? "each" : "queue" ](function() {
+ var opt = jQuery.extend({}, optall), p,
+ hidden = this.nodeType === 1 && jQuery(this).is(":hidden"),
+ self = this;
+
+ for ( p in prop ) {
+ var name = p.replace(rdashAlpha, fcamelCase);
+
+ if ( p !== name ) {
+ prop[ name ] = prop[ p ];
+ delete prop[ p ];
+ p = name;
+ }
+
+ if ( prop[p] === "hide" && hidden || prop[p] === "show" && !hidden ) {
+ return opt.complete.call(this);
+ }
+
+ if ( ( p === "height" || p === "width" ) && this.style ) {
+ // Store display property
+ opt.display = jQuery.css(this, "display");
+
+ // Make sure that nothing sneaks out
+ opt.overflow = this.style.overflow;
+ }
+
+ if ( jQuery.isArray( prop[p] ) ) {
+ // Create (if needed) and add to specialEasing
+ (opt.specialEasing = opt.specialEasing || {})[p] = prop[p][1];
+ prop[p] = prop[p][0];
+ }
+ }
+
+ if ( opt.overflow != null ) {
+ this.style.overflow = "hidden";
+ }
+
+ opt.curAnim = jQuery.extend({}, prop);
+
+ jQuery.each( prop, function( name, val ) {
+ var e = new jQuery.fx( self, opt, name );
+
+ if ( rfxtypes.test(val) ) {
+ e[ val === "toggle" ? hidden ? "show" : "hide" : val ]( prop );
+
+ } else {
+ var parts = rfxnum.exec(val),
+ start = e.cur(true) || 0;
+
+ if ( parts ) {
+ var end = parseFloat( parts[2] ),
+ unit = parts[3] || "px";
+
+ // We need to compute starting value
+ if ( unit !== "px" ) {
+ self.style[ name ] = (end || 1) + unit;
+ start = ((end || 1) / e.cur(true)) * start;
+ self.style[ name ] = start + unit;
+ }
+
+ // If a +=/-= token was provided, we're doing a relative animation
+ if ( parts[1] ) {
+ end = ((parts[1] === "-=" ? -1 : 1) * end) + start;
+ }
+
+ e.custom( start, end, unit );
+
+ } else {
+ e.custom( start, val, "" );
+ }
+ }
+ });
+
+ // For JS strict compliance
+ return true;
+ });
+ },
+
+ stop: function( clearQueue, gotoEnd ) {
+ var timers = jQuery.timers;
+
+ if ( clearQueue ) {
+ this.queue([]);
+ }
+
+ this.each(function() {
+ // go in reverse order so anything added to the queue during the loop is ignored
+ for ( var i = timers.length - 1; i >= 0; i-- ) {
+ if ( timers[i].elem === this ) {
+ if (gotoEnd) {
+ // force the next step to be the last
+ timers[i](true);
+ }
+
+ timers.splice(i, 1);
+ }
+ }
+ });
+
+ // start the next in the queue if the last step wasn't forced
+ if ( !gotoEnd ) {
+ this.dequeue();
+ }
+
+ return this;
+ }
+
+});
+
+// Generate shortcuts for custom animations
+jQuery.each({
+ slideDown: genFx("show", 1),
+ slideUp: genFx("hide", 1),
+ slideToggle: genFx("toggle", 1),
+ fadeIn: { opacity: "show" },
+ fadeOut: { opacity: "hide" }
+}, function( name, props ) {
+ jQuery.fn[ name ] = function( speed, callback ) {
+ return this.animate( props, speed, callback );
+ };
+});
+
+jQuery.extend({
+ speed: function( speed, easing, fn ) {
+ var opt = speed && typeof speed === "object" ? speed : {
+ complete: fn || !fn && easing ||
+ jQuery.isFunction( speed ) && speed,
+ duration: speed,
+ easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
+ };
+
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+ jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;
+
+ // Queueing
+ opt.old = opt.complete;
+ opt.complete = function() {
+ if ( opt.queue !== false ) {
+ jQuery(this).dequeue();
+ }
+ if ( jQuery.isFunction( opt.old ) ) {
+ opt.old.call( this );
+ }
+ };
+
+ return opt;
+ },
+
+ easing: {
+ linear: function( p, n, firstNum, diff ) {
+ return firstNum + diff * p;
+ },
+ swing: function( p, n, firstNum, diff ) {
+ return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
+ }
+ },
+
+ timers: [],
+
+ fx: function( elem, options, prop ) {
+ this.options = options;
+ this.elem = elem;
+ this.prop = prop;
+
+ if ( !options.orig ) {
+ options.orig = {};
+ }
+ }
+
+});
+
+jQuery.fx.prototype = {
+ // Simple function for setting a style value
+ update: function() {
+ if ( this.options.step ) {
+ this.options.step.call( this.elem, this.now, this );
+ }
+
+ (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
+
+ // Set display property to block for height/width animations
+ if ( ( this.prop === "height" || this.prop === "width" ) && this.elem.style ) {
+ this.elem.style.display = "block";
+ }
+ },
+
+ // Get the current size
+ cur: function( force ) {
+ if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
+ return this.elem[ this.prop ];
+ }
+
+ var r = parseFloat(jQuery.css(this.elem, this.prop, force));
+ return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
+ },
+
+ // Start an animation from one number to another
+ custom: function( from, to, unit ) {
+ this.startTime = now();
+ this.start = from;
+ this.end = to;
+ this.unit = unit || this.unit || "px";
+ this.now = this.start;
+ this.pos = this.state = 0;
+
+ var self = this;
+ function t( gotoEnd ) {
+ return self.step(gotoEnd);
+ }
+
+ t.elem = this.elem;
+
+ if ( t() && jQuery.timers.push(t) && !timerId ) {
+ timerId = setInterval(jQuery.fx.tick, 13);
+ }
+ },
+
+ // Simple 'show' function
+ show: function() {
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+ this.options.show = true;
+
+ // Begin the animation
+ // Make sure that we start at a small width/height to avoid any
+ // flash of content
+ this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur());
+
+ // Start by showing the element
+ jQuery( this.elem ).show();
+ },
+
+ // Simple 'hide' function
+ hide: function() {
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+ this.options.hide = true;
+
+ // Begin the animation
+ this.custom(this.cur(), 0);
+ },
+
+ // Each step of an animation
+ step: function( gotoEnd ) {
+ var t = now(), done = true;
+
+ if ( gotoEnd || t >= this.options.duration + this.startTime ) {
+ this.now = this.end;
+ this.pos = this.state = 1;
+ this.update();
+
+ this.options.curAnim[ this.prop ] = true;
+
+ for ( var i in this.options.curAnim ) {
+ if ( this.options.curAnim[i] !== true ) {
+ done = false;
+ }
+ }
+
+ if ( done ) {
+ if ( this.options.display != null ) {
+ // Reset the overflow
+ this.elem.style.overflow = this.options.overflow;
+
+ // Reset the display
+ var old = jQuery.data(this.elem, "olddisplay");
+ this.elem.style.display = old ? old : this.options.display;
+
+ if ( jQuery.css(this.elem, "display") === "none" ) {
+ this.elem.style.display = "block";
+ }
+ }
+
+ // Hide the element if the "hide" operation was done
+ if ( this.options.hide ) {
+ jQuery(this.elem).hide();
+ }
+
+ // Reset the properties, if the item has been hidden or shown
+ if ( this.options.hide || this.options.show ) {
+ for ( var p in this.options.curAnim ) {
+ jQuery.style(this.elem, p, this.options.orig[p]);
+ }
+ }
+
+ // Execute the complete function
+ this.options.complete.call( this.elem );
+ }
+
+ return false;
+
+ } else {
+ var n = t - this.startTime;
+ this.state = n / this.options.duration;
+
+ // Perform the easing function, defaults to swing
+ var specialEasing = this.options.specialEasing && this.options.specialEasing[this.prop];
+ var defaultEasing = this.options.easing || (jQuery.easing.swing ? "swing" : "linear");
+ this.pos = jQuery.easing[specialEasing || defaultEasing](this.state, n, 0, 1, this.options.duration);
+ this.now = this.start + ((this.end - this.start) * this.pos);
+
+ // Perform the next step of the animation
+ this.update();
+ }
+
+ return true;
+ }
+};
+
+jQuery.extend( jQuery.fx, {
+ tick: function() {
+ var timers = jQuery.timers;
+
+ for ( var i = 0; i < timers.length; i++ ) {
+ if ( !timers[i]() ) {
+ timers.splice(i--, 1);
+ }
+ }
+
+ if ( !timers.length ) {
+ jQuery.fx.stop();
+ }
+ },
+
+ stop: function() {
+ clearInterval( timerId );
+ timerId = null;
+ },
+
+ speeds: {
+ slow: 600,
+ fast: 200,
+ // Default speed
+ _default: 400
+ },
+
+ step: {
+ opacity: function( fx ) {
+ jQuery.style(fx.elem, "opacity", fx.now);
+ },
+
+ _default: function( fx ) {
+ if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
+ fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit;
+ } else {
+ fx.elem[ fx.prop ] = fx.now;
+ }
+ }
+ }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.animated = function( elem ) {
+ return jQuery.grep(jQuery.timers, function( fn ) {
+ return elem === fn.elem;
+ }).length;
+ };
+}
+
+function genFx( type, num ) {
+ var obj = {};
+
+ jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
+ obj[ this ] = type;
+ });
+
+ return obj;
+}
+if ( "getBoundingClientRect" in document.documentElement ) {
+ jQuery.fn.offset = function( options ) {
+ var elem = this[0];
+
+ if ( options ) {
+ return this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ if ( !elem || !elem.ownerDocument ) {
+ return null;
+ }
+
+ if ( elem === elem.ownerDocument.body ) {
+ return jQuery.offset.bodyOffset( elem );
+ }
+
+ var box = elem.getBoundingClientRect(), doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement,
+ clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
+ top = box.top + (self.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop ) - clientTop,
+ left = box.left + (self.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;
+
+ return { top: top, left: left };
+ };
+
+} else {
+ jQuery.fn.offset = function( options ) {
+ var elem = this[0];
+
+ if ( options ) {
+ return this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ if ( !elem || !elem.ownerDocument ) {
+ return null;
+ }
+
+ if ( elem === elem.ownerDocument.body ) {
+ return jQuery.offset.bodyOffset( elem );
+ }
+
+ jQuery.offset.initialize();
+
+ var offsetParent = elem.offsetParent, prevOffsetParent = elem,
+ doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
+ body = doc.body, defaultView = doc.defaultView,
+ prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
+ top = elem.offsetTop, left = elem.offsetLeft;
+
+ while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
+ if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+ break;
+ }
+
+ computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
+ top -= elem.scrollTop;
+ left -= elem.scrollLeft;
+
+ if ( elem === offsetParent ) {
+ top += elem.offsetTop;
+ left += elem.offsetLeft;
+
+ if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.nodeName)) ) {
+ top += parseFloat( computedStyle.borderTopWidth ) || 0;
+ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+ }
+
+ prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
+ }
+
+ if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
+ top += parseFloat( computedStyle.borderTopWidth ) || 0;
+ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+ }
+
+ prevComputedStyle = computedStyle;
+ }
+
+ if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
+ top += body.offsetTop;
+ left += body.offsetLeft;
+ }
+
+ if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+ top += Math.max( docElem.scrollTop, body.scrollTop );
+ left += Math.max( docElem.scrollLeft, body.scrollLeft );
+ }
+
+ return { top: top, left: left };
+ };
+}
+
+jQuery.offset = {
+ initialize: function() {
+ var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.curCSS(body, "marginTop", true) ) || 0,
+ html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+
+ jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );
+
+ container.innerHTML = html;
+ body.insertBefore( container, body.firstChild );
+ innerDiv = container.firstChild;
+ checkDiv = innerDiv.firstChild;
+ td = innerDiv.nextSibling.firstChild.firstChild;
+
+ this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
+ this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
+
+ checkDiv.style.position = "fixed", checkDiv.style.top = "20px";
+ // safari subtracts parent border width here which is 5px
+ this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
+ checkDiv.style.position = checkDiv.style.top = "";
+
+ innerDiv.style.overflow = "hidden", innerDiv.style.position = "relative";
+ this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
+
+ this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);
+
+ body.removeChild( container );
+ body = container = innerDiv = checkDiv = table = td = null;
+ jQuery.offset.initialize = jQuery.noop;
+ },
+
+ bodyOffset: function( body ) {
+ var top = body.offsetTop, left = body.offsetLeft;
+
+ jQuery.offset.initialize();
+
+ if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
+ top += parseFloat( jQuery.curCSS(body, "marginTop", true) ) || 0;
+ left += parseFloat( jQuery.curCSS(body, "marginLeft", true) ) || 0;
+ }
+
+ return { top: top, left: left };
+ },
+
+ setOffset: function( elem, options, i ) {
+ // set position first, in-case top/left are set even on static elem
+ if ( /static/.test( jQuery.curCSS( elem, "position" ) ) ) {
+ elem.style.position = "relative";
+ }
+ var curElem = jQuery( elem ),
+ curOffset = curElem.offset(),
+ curTop = parseInt( jQuery.curCSS( elem, "top", true ), 10 ) || 0,
+ curLeft = parseInt( jQuery.curCSS( elem, "left", true ), 10 ) || 0;
+
+ if ( jQuery.isFunction( options ) ) {
+ options = options.call( elem, i, curOffset );
+ }
+
+ var props = {
+ top: (options.top - curOffset.top) + curTop,
+ left: (options.left - curOffset.left) + curLeft
+ };
+
+ if ( "using" in options ) {
+ options.using.call( elem, props );
+ } else {
+ curElem.css( props );
+ }
+ }
+};
+
+
+jQuery.fn.extend({
+ position: function() {
+ if ( !this[0] ) {
+ return null;
+ }
+
+ var elem = this[0],
+
+ // Get *real* offsetParent
+ offsetParent = this.offsetParent(),
+
+ // Get correct offsets
+ offset = this.offset(),
+ parentOffset = /^body|html$/i.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+ // Subtract element margins
+ // note: when an element has margin: auto the offsetLeft and marginLeft
+ // are the same in Safari causing offset.left to incorrectly be 0
+ offset.top -= parseFloat( jQuery.curCSS(elem, "marginTop", true) ) || 0;
+ offset.left -= parseFloat( jQuery.curCSS(elem, "marginLeft", true) ) || 0;
+
+ // Add offsetParent borders
+ parentOffset.top += parseFloat( jQuery.curCSS(offsetParent[0], "borderTopWidth", true) ) || 0;
+ parentOffset.left += parseFloat( jQuery.curCSS(offsetParent[0], "borderLeftWidth", true) ) || 0;
+
+ // Subtract the two offsets
+ return {
+ top: offset.top - parentOffset.top,
+ left: offset.left - parentOffset.left
+ };
+ },
+
+ offsetParent: function() {
+ return this.map(function() {
+ var offsetParent = this.offsetParent || document.body;
+ while ( offsetParent && (!/^body|html$/i.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
+ offsetParent = offsetParent.offsetParent;
+ }
+ return offsetParent;
+ });
+ }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( ["Left", "Top"], function( i, name ) {
+ var method = "scroll" + name;
+
+ jQuery.fn[ method ] = function(val) {
+ var elem = this[0], win;
+
+ if ( !elem ) {
+ return null;
+ }
+
+ if ( val !== undefined ) {
+ // Set the scroll offset
+ return this.each(function() {
+ win = getWindow( this );
+
+ if ( win ) {
+ win.scrollTo(
+ !i ? val : jQuery(win).scrollLeft(),
+ i ? val : jQuery(win).scrollTop()
+ );
+
+ } else {
+ this[ method ] = val;
+ }
+ });
+ } else {
+ win = getWindow( elem );
+
+ // Return the scroll offset
+ return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
+ jQuery.support.boxModel && win.document.documentElement[ method ] ||
+ win.document.body[ method ] :
+ elem[ method ];
+ }
+ };
+});
+
+function getWindow( elem ) {
+ return ("scrollTo" in elem && elem.document) ?
+ elem :
+ elem.nodeType === 9 ?
+ elem.defaultView || elem.parentWindow :
+ false;
+}
+// Create innerHeight, innerWidth, outerHeight and outerWidth methods
+jQuery.each([ "Height", "Width" ], function( i, name ) {
+
+ var type = name.toLowerCase();
+
+ // innerHeight and innerWidth
+ jQuery.fn["inner" + name] = function() {
+ return this[0] ?
+ jQuery.css( this[0], type, false, "padding" ) :
+ null;
+ };
+
+ // outerHeight and outerWidth
+ jQuery.fn["outer" + name] = function( margin ) {
+ return this[0] ?
+ jQuery.css( this[0], type, false, margin ? "margin" : "border" ) :
+ null;
+ };
+
+ jQuery.fn[ type ] = function( size ) {
+ // Get window width or height
+ var elem = this[0];
+ if ( !elem ) {
+ return size == null ? null : this;
+ }
+
+ if ( jQuery.isFunction( size ) ) {
+ return this.each(function( i ) {
+ var self = jQuery( this );
+ self[ type ]( size.call( this, i, self[ type ]() ) );
+ });
+ }
+
+ return ("scrollTo" in elem && elem.document) ? // does it walk and quack like a window?
+ // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
+ elem.document.compatMode === "CSS1Compat" && elem.document.documentElement[ "client" + name ] ||
+ elem.document.body[ "client" + name ] :
+
+ // Get document width or height
+ (elem.nodeType === 9) ? // is it a document
+ // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
+ Math.max(
+ elem.documentElement["client" + name],
+ elem.body["scroll" + name], elem.documentElement["scroll" + name],
+ elem.body["offset" + name], elem.documentElement["offset" + name]
+ ) :
+
+ // Get or set width or height on the element
+ size === undefined ?
+ // Get width or height on the element
+ jQuery.css( elem, type ) :
+
+ // Set the width or height on the element (default to pixels if value is unitless)
+ this.css( type, typeof size === "string" ? size : size + "px" );
+ };
+
+});
+// Expose jQuery to the global object
+window.jQuery = window.$ = jQuery;
+
+})(window);
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/jquery.hotkeys.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/jquery.hotkeys.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/jquery.hotkeys.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,110 @@
+/*
+ * jQuery Hotkeys Plugin
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ *
+ * Based upon the plugin by Tzury Bar Yochay:
+ * http://github.com/tzuryby/hotkeys
+ *
+ * Original idea by:
+ * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
+*/
+
+/*
+ * One small change is: now keys are passed by object { keys: '...' }
+ * Might be useful, when you want to pass some other data to your handler
+ */
+
+(function(jQuery){
+
+ jQuery.hotkeys = {
+ version: "0.8",
+
+ specialKeys: {
+ 8: "backspace", 9: "tab", 10: "return", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
+ 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
+ 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
+ 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
+ 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
+ 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
+ 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 186: ";", 191: "/",
+ 220: "\\", 222: "'", 224: "meta"
+ },
+
+ shiftNums: {
+ "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
+ "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
+ ".": ">", "/": "?", "\\": "|"
+ }
+ };
+
+ function keyHandler( handleObj ) {
+ if ( typeof handleObj.data === "string" ) {
+ handleObj.data = { keys: handleObj.data };
+ }
+
+ // Only care when a possible input has been specified
+ if ( !handleObj.data || !handleObj.data.keys || typeof handleObj.data.keys !== "string" ) {
+ return;
+ }
+
+ var origHandler = handleObj.handler,
+ keys = handleObj.data.keys.toLowerCase().split(" "),
+ textAcceptingInputTypes = ["text", "password", "number", "email", "url", "range", "date", "month", "week", "time", "datetime", "datetime-local", "search", "color", "tel"];
+
+ handleObj.handler = function( event ) {
+ // Don't fire in text-accepting inputs that we didn't directly bind to
+ if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) ||
+ jQuery.inArray(event.target.type, textAcceptingInputTypes) > -1 ) ) {
+ return;
+ }
+
+ var special = jQuery.hotkeys.specialKeys[ event.keyCode ],
+ character = String.fromCharCode( event.which ).toLowerCase(),
+ modif = "", possible = {};
+
+ // check combinations (alt|ctrl|shift+anything)
+ if ( event.altKey && special !== "alt" ) {
+ modif += "alt+";
+ }
+
+ if ( event.ctrlKey && special !== "ctrl" ) {
+ modif += "ctrl+";
+ }
+
+ // TODO: Need to make sure this works consistently across platforms
+ if ( event.metaKey && !event.ctrlKey && special !== "meta" ) {
+ modif += "meta+";
+ }
+
+ if ( event.shiftKey && special !== "shift" ) {
+ modif += "shift+";
+ }
+
+ if ( special ) {
+ possible[ modif + special ] = true;
+ }
+
+ if ( character ) {
+ possible[ modif + character ] = true;
+ possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true;
+
+ // "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
+ if ( modif === "shift+" ) {
+ possible[ jQuery.hotkeys.shiftNums[ character ] ] = true;
+ }
+ }
+
+ for ( var i = 0, l = keys.length; i < l; i++ ) {
+ if ( possible[ keys[i] ] ) {
+ return origHandler.apply( this, arguments );
+ }
+ }
+ };
+ }
+
+ jQuery.each([ "keydown", "keyup", "keypress" ], function() {
+ jQuery.event.special[ this ] = { add: keyHandler };
+ });
+
+})( this.jQuery );
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/package.json
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/package.json (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/package.json 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,19 @@
+{
+ "name": "jquery.hotkeys",
+ "version": "0.1.0",
+ "description": "**jQuery Hotkeys** is a plug-in that lets you easily add and remove handlers for keyboard events anywhere in your code supporting almost any key combination.",
+ "main": "jquery.hotkeys.js",
+ "devDependencies": {
+ "grunt": "~0.4.1",
+ "grunt-contrib-jshint": "~0.8.0",
+ "grunt-contrib-jasmine": "~0.6.0"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/jeresig/jquery.hotkeys"
+ },
+ "license": "MIT or GPL Version 2",
+ "scripts": {
+ "test": "grunt -v"
+ }
+}
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/SpecRunner.html
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/SpecRunner.html (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/SpecRunner.html 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,43 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Jasmine Spec Runner</title>
+
+ <link rel="stylesheet" type="text/css" href="../.grunt/grunt-contrib-jasmine/jasmine.css">
+
+
+
+ <script src="../.grunt/grunt-contrib-jasmine/phantom-polyfill.js"></script>
+
+ <script src="../.grunt/grunt-contrib-jasmine/jasmine.js"></script>
+
+ <script src="../.grunt/grunt-contrib-jasmine/jasmine-html.js"></script>
+
+ <script src="../.grunt/grunt-contrib-jasmine/boot.js"></script>
+
+ <script src="../jquery-1.4.2.js"></script>
+
+ <script src="lib/boot.js"></script>
+
+ <script src="lib/console.js"></script>
+
+ <script src="lib/jasmine-html.js"></script>
+
+ <script src="lib/jasmine.js"></script>
+
+ <script src="lib/sinon.js"></script>
+
+ <script src="lib/underscore.js"></script>
+
+ <script src="../jquery.hotkeys.js"></script>
+
+ <script src="spec/bindingSpec.js"></script>
+
+ <script src="../.grunt/grunt-contrib-jasmine/reporter.js"></script>
+
+
+</head>
+<body>
+</body>
+</html>
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/boot.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/boot.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/boot.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,181 @@
+/**
+ Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project.
+
+ If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms.
+
+ The location of `boot.js` can be specified and/or overridden in `jasmine.yml`.
+
+ [jasmine-gem]: http://github.com/pivotal/jasmine-gem
+ */
+
+(function() {
+
+ /**
+ * ## Require & Instantiate
+ *
+ * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
+ */
+ window.jasmine = jasmineRequire.core(jasmineRequire);
+
+ /**
+ * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
+ */
+ jasmineRequire.html(jasmine);
+
+ /**
+ * Create the Jasmine environment. This is used to run all specs in a project.
+ */
+ var env = jasmine.getEnv();
+
+ /**
+ * ## The Global Interface
+ *
+ * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
+ */
+ var jasmineInterface = {
+ describe: function(description, specDefinitions) {
+ return env.describe(description, specDefinitions);
+ },
+
+ xdescribe: function(description, specDefinitions) {
+ return env.xdescribe(description, specDefinitions);
+ },
+
+ it: function(desc, func) {
+ return env.it(desc, func);
+ },
+
+ xit: function(desc, func) {
+ return env.xit(desc, func);
+ },
+
+ beforeEach: function(beforeEachFunction) {
+ return env.beforeEach(beforeEachFunction);
+ },
+
+ afterEach: function(afterEachFunction) {
+ return env.afterEach(afterEachFunction);
+ },
+
+ expect: function(actual) {
+ return env.expect(actual);
+ },
+
+ pending: function() {
+ return env.pending();
+ },
+
+ spyOn: function(obj, methodName) {
+ return env.spyOn(obj, methodName);
+ },
+
+ jsApiReporter: new jasmine.JsApiReporter({
+ timer: new jasmine.Timer()
+ })
+ };
+
+ /**
+ * Add all of the Jasmine global/public interface to the proper global, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
+ */
+ if (typeof window == "undefined" && typeof exports == "object") {
+ extend(exports, jasmineInterface);
+ } else {
+ extend(window, jasmineInterface);
+ }
+
+ /**
+ * Expose the interface for adding custom equality testers.
+ */
+ jasmine.addCustomEqualityTester = function(tester) {
+ env.addCustomEqualityTester(tester);
+ };
+
+ /**
+ * Expose the interface for adding custom expectation matchers
+ */
+ jasmine.addMatchers = function(matchers) {
+ return env.addMatchers(matchers);
+ };
+
+ /**
+ * Expose the mock interface for the JavaScript timeout functions
+ */
+ jasmine.clock = function() {
+ return env.clock;
+ };
+
+ /**
+ * ## Runner Parameters
+ *
+ * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
+ */
+
+ var queryString = new jasmine.QueryString({
+ getWindowLocation: function() { return window.location; }
+ });
+
+ var catchingExceptions = queryString.getParam("catch");
+ env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions);
+
+ /**
+ * ## Reporters
+ * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
+ */
+ var htmlReporter = new jasmine.HtmlReporter({
+ env: env,
+ onRaiseExceptionsClick: function() { queryString.setParam("catch", !env.catchingExceptions()); },
+ getContainer: function() { return document.body; },
+ createElement: function() { return document.createElement.apply(document, arguments); },
+ createTextNode: function() { return document.createTextNode.apply(document, arguments); },
+ timer: new jasmine.Timer()
+ });
+
+ /**
+ * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
+ */
+ env.addReporter(jasmineInterface.jsApiReporter);
+ env.addReporter(htmlReporter);
+
+ /**
+ * Filter which specs will be run by matching the start of the full name against the `spec` query param.
+ */
+ var specFilter = new jasmine.HtmlSpecFilter({
+ filterString: function() { return queryString.getParam("spec"); }
+ });
+
+ env.specFilter = function(spec) {
+ return specFilter.matches(spec.getFullName());
+ };
+
+ /**
+ * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack.
+ */
+ window.setTimeout = window.setTimeout;
+ window.setInterval = window.setInterval;
+ window.clearTimeout = window.clearTimeout;
+ window.clearInterval = window.clearInterval;
+
+ /**
+ * ## Execution
+ *
+ * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
+ */
+ var currentWindowOnload = window.onload;
+
+ window.onload = function() {
+ if (currentWindowOnload) {
+ currentWindowOnload();
+ }
+ htmlReporter.initialize();
+ env.execute();
+ };
+
+ /**
+ * Helper function for readability above.
+ */
+ function extend(destination, source) {
+ for (var property in source) destination[property] = source[property];
+ return destination;
+ }
+
+}());
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/console.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/console.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/console.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,160 @@
+/*
+Copyright (c) 2008-2013 Pivotal Labs
+
+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.
+*/
+function getJasmineRequireObj() {
+ if (typeof module !== "undefined" && module.exports) {
+ return exports;
+ } else {
+ window.jasmineRequire = window.jasmineRequire || {};
+ return window.jasmineRequire;
+ }
+}
+
+getJasmineRequireObj().console = function(jRequire, j$) {
+ j$.ConsoleReporter = jRequire.ConsoleReporter();
+};
+
+getJasmineRequireObj().ConsoleReporter = function() {
+
+ var noopTimer = {
+ start: function(){},
+ elapsed: function(){ return 0; }
+ };
+
+ function ConsoleReporter(options) {
+ var print = options.print,
+ showColors = options.showColors || false,
+ onComplete = options.onComplete || function() {},
+ timer = options.timer || noopTimer,
+ specCount,
+ failureCount,
+ failedSpecs = [],
+ pendingCount,
+ ansi = {
+ green: '\x1B[32m',
+ red: '\x1B[31m',
+ yellow: '\x1B[33m',
+ none: '\x1B[0m'
+ };
+
+ this.jasmineStarted = function() {
+ specCount = 0;
+ failureCount = 0;
+ pendingCount = 0;
+ print("Started");
+ printNewline();
+ timer.start();
+ };
+
+ this.jasmineDone = function() {
+ printNewline();
+ for (var i = 0; i < failedSpecs.length; i++) {
+ specFailureDetails(failedSpecs[i]);
+ }
+
+ printNewline();
+ var specCounts = specCount + " " + plural("spec", specCount) + ", " +
+ failureCount + " " + plural("failure", failureCount);
+
+ if (pendingCount) {
+ specCounts += ", " + pendingCount + " pending " + plural("spec", pendingCount);
+ }
+
+ print(specCounts);
+
+ printNewline();
+ var seconds = timer.elapsed() / 1000;
+ print("Finished in " + seconds + " " + plural("second", seconds));
+
+ printNewline();
+
+ onComplete(failureCount === 0);
+ };
+
+ this.specDone = function(result) {
+ specCount++;
+
+ if (result.status == "pending") {
+ pendingCount++;
+ print(colored("yellow", "*"));
+ return;
+ }
+
+ if (result.status == "passed") {
+ print(colored("green", '.'));
+ return;
+ }
+
+ if (result.status == "failed") {
+ failureCount++;
+ failedSpecs.push(result);
+ print(colored("red", 'F'));
+ }
+ };
+
+ return this;
+
+ function printNewline() {
+ print("\n");
+ }
+
+ function colored(color, str) {
+ return showColors ? (ansi[color] + str + ansi.none) : str;
+ }
+
+ function plural(str, count) {
+ return count == 1 ? str : str + "s";
+ }
+
+ function repeat(thing, times) {
+ var arr = [];
+ for (var i = 0; i < times; i++) {
+ arr.push(thing);
+ }
+ return arr;
+ }
+
+ function indent(str, spaces) {
+ var lines = (str || '').split("\n");
+ var newArr = [];
+ for (var i = 0; i < lines.length; i++) {
+ newArr.push(repeat(" ", spaces).join("") + lines[i]);
+ }
+ return newArr.join("\n");
+ }
+
+ function specFailureDetails(result) {
+ printNewline();
+ print(result.fullName);
+
+ for (var i = 0; i < result.failedExpectations.length; i++) {
+ var failedExpectation = result.failedExpectations[i];
+ printNewline();
+ print(indent(failedExpectation.stack, 2));
+ }
+
+ printNewline();
+ }
+ }
+
+ return ConsoleReporter;
+};
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/jasmine-html.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/jasmine-html.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/jasmine-html.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,359 @@
+/*
+Copyright (c) 2008-2013 Pivotal Labs
+
+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.
+*/
+jasmineRequire.html = function(j$) {
+ j$.ResultsNode = jasmineRequire.ResultsNode();
+ j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
+ j$.QueryString = jasmineRequire.QueryString();
+ j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter();
+};
+
+jasmineRequire.HtmlReporter = function(j$) {
+
+ var noopTimer = {
+ start: function() {},
+ elapsed: function() { return 0; }
+ };
+
+ function HtmlReporter(options) {
+ var env = options.env || {},
+ getContainer = options.getContainer,
+ createElement = options.createElement,
+ createTextNode = options.createTextNode,
+ onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {},
+ timer = options.timer || noopTimer,
+ results = [],
+ specsExecuted = 0,
+ failureCount = 0,
+ pendingSpecCount = 0,
+ htmlReporterMain,
+ symbols;
+
+ this.initialize = function() {
+ htmlReporterMain = createDom("div", {className: "html-reporter"},
+ createDom("div", {className: "banner"},
+ createDom("span", {className: "title"}, "Jasmine"),
+ createDom("span", {className: "version"}, j$.version)
+ ),
+ createDom("ul", {className: "symbol-summary"}),
+ createDom("div", {className: "alert"}),
+ createDom("div", {className: "results"},
+ createDom("div", {className: "failures"})
+ )
+ );
+ getContainer().appendChild(htmlReporterMain);
+
+ symbols = find(".symbol-summary");
+ };
+
+ var totalSpecsDefined;
+ this.jasmineStarted = function(options) {
+ totalSpecsDefined = options.totalSpecsDefined || 0;
+ timer.start();
+ };
+
+ var summary = createDom("div", {className: "summary"});
+
+ var topResults = new j$.ResultsNode({}, "", null),
+ currentParent = topResults;
+
+ this.suiteStarted = function(result) {
+ currentParent.addChild(result, "suite");
+ currentParent = currentParent.last();
+ };
+
+ this.suiteDone = function(result) {
+ if (currentParent == topResults) {
+ return;
+ }
+
+ currentParent = currentParent.parent;
+ };
+
+ this.specStarted = function(result) {
+ currentParent.addChild(result, "spec");
+ };
+
+ var failures = [];
+ this.specDone = function(result) {
+ if (result.status != "disabled") {
+ specsExecuted++;
+ }
+
+ symbols.appendChild(createDom("li", {
+ className: result.status,
+ id: "spec_" + result.id,
+ title: result.fullName
+ }
+ ));
+
+ if (result.status == "failed") {
+ failureCount++;
+
+ var failure =
+ createDom("div", {className: "spec-detail failed"},
+ createDom("div", {className: "description"},
+ createDom("a", {title: result.fullName, href: specHref(result)}, result.fullName)
+ ),
+ createDom("div", {className: "messages"})
+ );
+ var messages = failure.childNodes[1];
+
+ for (var i = 0; i < result.failedExpectations.length; i++) {
+ var expectation = result.failedExpectations[i];
+ messages.appendChild(createDom("div", {className: "result-message"}, expectation.message));
+ messages.appendChild(createDom("div", {className: "stack-trace"}, expectation.stack));
+ }
+
+ failures.push(failure);
+ }
+
+ if (result.status == "pending") {
+ pendingSpecCount++;
+ }
+ };
+
+ this.jasmineDone = function() {
+ var banner = find(".banner");
+ banner.appendChild(createDom("span", {className: "duration"}, "finished in " + timer.elapsed() / 1000 + "s"));
+
+ var alert = find(".alert");
+
+ alert.appendChild(createDom("span", { className: "exceptions" },
+ createDom("label", { className: "label", 'for': "raise-exceptions" }, "raise exceptions"),
+ createDom("input", {
+ className: "raise",
+ id: "raise-exceptions",
+ type: "checkbox"
+ })
+ ));
+ var checkbox = find("input");
+
+ checkbox.checked = !env.catchingExceptions();
+ checkbox.onclick = onRaiseExceptionsClick;
+
+ if (specsExecuted < totalSpecsDefined) {
+ var skippedMessage = "Ran " + specsExecuted + " of " + totalSpecsDefined + " specs - run all";
+ alert.appendChild(
+ createDom("span", {className: "bar skipped"},
+ createDom("a", {href: "?", title: "Run all specs"}, skippedMessage)
+ )
+ );
+ }
+ var statusBarMessage = "" + pluralize("spec", specsExecuted) + ", " + pluralize("failure", failureCount);
+ if (pendingSpecCount) { statusBarMessage += ", " + pluralize("pending spec", pendingSpecCount); }
+
+ var statusBarClassName = "bar " + ((failureCount > 0) ? "failed" : "passed");
+ alert.appendChild(createDom("span", {className: statusBarClassName}, statusBarMessage));
+
+ var results = find(".results");
+ results.appendChild(summary);
+
+ summaryList(topResults, summary);
+
+ function summaryList(resultsTree, domParent) {
+ var specListNode;
+ for (var i = 0; i < resultsTree.children.length; i++) {
+ var resultNode = resultsTree.children[i];
+ if (resultNode.type == "suite") {
+ var suiteListNode = createDom("ul", {className: "suite", id: "suite-" + resultNode.result.id},
+ createDom("li", {className: "suite-detail"},
+ createDom("a", {href: specHref(resultNode.result)}, resultNode.result.description)
+ )
+ );
+
+ summaryList(resultNode, suiteListNode);
+ domParent.appendChild(suiteListNode);
+ }
+ if (resultNode.type == "spec") {
+ if (domParent.getAttribute("class") != "specs") {
+ specListNode = createDom("ul", {className: "specs"});
+ domParent.appendChild(specListNode);
+ }
+ specListNode.appendChild(
+ createDom("li", {
+ className: resultNode.result.status,
+ id: "spec-" + resultNode.result.id
+ },
+ createDom("a", {href: specHref(resultNode.result)}, resultNode.result.description)
+ )
+ );
+ }
+ }
+ }
+
+ if (failures.length) {
+ alert.appendChild(
+ createDom('span', {className: "menu bar spec-list"},
+ createDom("span", {}, "Spec List | "),
+ createDom('a', {className: "failures-menu", href: "#"}, "Failures")));
+ alert.appendChild(
+ createDom('span', {className: "menu bar failure-list"},
+ createDom('a', {className: "spec-list-menu", href: "#"}, "Spec List"),
+ createDom("span", {}, " | Failures ")));
+
+ find(".failures-menu").onclick = function() {
+ setMenuModeTo('failure-list');
+ };
+ find(".spec-list-menu").onclick = function() {
+ setMenuModeTo('spec-list');
+ };
+
+ setMenuModeTo('failure-list');
+
+ var failureNode = find(".failures");
+ for (var i = 0; i < failures.length; i++) {
+ failureNode.appendChild(failures[i]);
+ }
+ }
+ };
+
+ return this;
+
+ function find(selector) {
+ return getContainer().querySelector(selector);
+ }
+
+ function createDom(type, attrs, childrenVarArgs) {
+ var el = createElement(type);
+
+ for (var i = 2; i < arguments.length; i++) {
+ var child = arguments[i];
+
+ if (typeof child === 'string') {
+ el.appendChild(createTextNode(child));
+ } else {
+ if (child) {
+ el.appendChild(child);
+ }
+ }
+ }
+
+ for (var attr in attrs) {
+ if (attr == "className") {
+ el[attr] = attrs[attr];
+ } else {
+ el.setAttribute(attr, attrs[attr]);
+ }
+ }
+
+ return el;
+ }
+
+ function pluralize(singular, count) {
+ var word = (count == 1 ? singular : singular + "s");
+
+ return "" + count + " " + word;
+ }
+
+ function specHref(result) {
+ return "?spec=" + encodeURIComponent(result.fullName);
+ }
+
+ function setMenuModeTo(mode) {
+ htmlReporterMain.setAttribute("class", "html-reporter " + mode);
+ }
+ }
+
+ return HtmlReporter;
+};
+
+jasmineRequire.HtmlSpecFilter = function() {
+ function HtmlSpecFilter(options) {
+ var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
+ var filterPattern = new RegExp(filterString);
+
+ this.matches = function(specName) {
+ return filterPattern.test(specName);
+ };
+ }
+
+ return HtmlSpecFilter;
+};
+
+jasmineRequire.ResultsNode = function() {
+ function ResultsNode(result, type, parent) {
+ this.result = result;
+ this.type = type;
+ this.parent = parent;
+
+ this.children = [];
+
+ this.addChild = function(result, type) {
+ this.children.push(new ResultsNode(result, type, this));
+ };
+
+ this.last = function() {
+ return this.children[this.children.length - 1];
+ };
+ }
+
+ return ResultsNode;
+};
+
+jasmineRequire.QueryString = function() {
+ function QueryString(options) {
+
+ this.setParam = function(key, value) {
+ var paramMap = queryStringToParamMap();
+ paramMap[key] = value;
+ options.getWindowLocation().search = toQueryString(paramMap);
+ };
+
+ this.getParam = function(key) {
+ return queryStringToParamMap()[key];
+ };
+
+ return this;
+
+ function toQueryString(paramMap) {
+ var qStrPairs = [];
+ for (var prop in paramMap) {
+ qStrPairs.push(encodeURIComponent(prop) + "=" + encodeURIComponent(paramMap[prop]));
+ }
+ return "?" + qStrPairs.join('&');
+ }
+
+ function queryStringToParamMap() {
+ var paramStr = options.getWindowLocation().search.substring(1),
+ params = [],
+ paramMap = {};
+
+ if (paramStr.length > 0) {
+ params = paramStr.split('&');
+ for (var i = 0; i < params.length; i++) {
+ var p = params[i].split('=');
+ var value = decodeURIComponent(p[1]);
+ if (value === "true" || value === "false") {
+ value = JSON.parse(value);
+ }
+ paramMap[decodeURIComponent(p[0])] = value;
+ }
+ }
+
+ return paramMap;
+ }
+
+ }
+
+ return QueryString;
+};
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/jasmine.css
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/jasmine.css (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/jasmine.css 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,55 @@
+body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
+
+.html-reporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
+.html-reporter a { text-decoration: none; }
+.html-reporter a:hover { text-decoration: underline; }
+.html-reporter p, .html-reporter h1, .html-reporter h2, .html-reporter h3, .html-reporter h4, .html-reporter h5, .html-reporter h6 { margin: 0; line-height: 14px; }
+.html-reporter .banner, .html-reporter .symbol-summary, .html-reporter .summary, .html-reporter .result-message, .html-reporter .spec .description, .html-reporter .spec-detail .description, .html-reporter .alert .bar, .html-reporter .stack-trace { padding-left: 9px; padding-right: 9px; }
+.html-reporter .banner .version { margin-left: 14px; }
+.html-reporter #jasmine_content { position: fixed; right: 100%; }
+.html-reporter .version { color: #aaaaaa; }
+.html-reporter .banner { margin-top: 14px; }
+.html-reporter .duration { color: #aaaaaa; float: right; }
+.html-reporter .symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; }
+.html-reporter .symbol-summary li { display: inline-block; height: 8px; width: 14px; font-size: 16px; }
+.html-reporter .symbol-summary li.passed { font-size: 14px; }
+.html-reporter .symbol-summary li.passed:before { color: #5e7d00; content: "\02022"; }
+.html-reporter .symbol-summary li.failed { line-height: 9px; }
+.html-reporter .symbol-summary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
+.html-reporter .symbol-summary li.disabled { font-size: 14px; }
+.html-reporter .symbol-summary li.disabled:before { color: #bababa; content: "\02022"; }
+.html-reporter .symbol-summary li.pending { line-height: 17px; }
+.html-reporter .symbol-summary li.pending:before { color: #ba9d37; content: "*"; }
+.html-reporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
+.html-reporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
+.html-reporter .bar.failed { background-color: #b03911; }
+.html-reporter .bar.passed { background-color: #a6b779; }
+.html-reporter .bar.skipped { background-color: #bababa; }
+.html-reporter .bar.menu { background-color: #fff; color: #aaaaaa; }
+.html-reporter .bar.menu a { color: #333333; }
+.html-reporter .bar a { color: white; }
+.html-reporter.spec-list .bar.menu.failure-list, .html-reporter.spec-list .results .failures { display: none; }
+.html-reporter.failure-list .bar.menu.spec-list, .html-reporter.failure-list .summary { display: none; }
+.html-reporter .running-alert { background-color: #666666; }
+.html-reporter .results { margin-top: 14px; }
+.html-reporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
+.html-reporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
+.html-reporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
+.html-reporter.showDetails .summary { display: none; }
+.html-reporter.showDetails #details { display: block; }
+.html-reporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
+.html-reporter .summary { margin-top: 14px; }
+.html-reporter .summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; }
+.html-reporter .summary ul.suite { margin-top: 7px; margin-bottom: 7px; }
+.html-reporter .summary li.passed a { color: #5e7d00; }
+.html-reporter .summary li.failed a { color: #b03911; }
+.html-reporter .summary li.pending a { color: #ba9d37; }
+.html-reporter .description + .suite { margin-top: 0; }
+.html-reporter .suite { margin-top: 14px; }
+.html-reporter .suite a { color: #333333; }
+.html-reporter .failures .spec-detail { margin-bottom: 28px; }
+.html-reporter .failures .spec-detail .description { background-color: #b03911; }
+.html-reporter .failures .spec-detail .description a { color: white; }
+.html-reporter .result-message { padding-top: 14px; color: #333333; white-space: pre; }
+.html-reporter .result-message span.result { display: block; }
+.html-reporter .stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/jasmine.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/jasmine.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/jasmine.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,2402 @@
+/*
+Copyright (c) 2008-2013 Pivotal Labs
+
+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.
+*/
+function getJasmineRequireObj() {
+ if (typeof module !== "undefined" && module.exports) {
+ return exports;
+ } else {
+ window.jasmineRequire = window.jasmineRequire || {};
+ return window.jasmineRequire;
+ }
+}
+
+getJasmineRequireObj().core = function(jRequire) {
+ var j$ = {};
+
+ jRequire.base(j$);
+ j$.util = jRequire.util();
+ j$.Any = jRequire.Any();
+ j$.CallTracker = jRequire.CallTracker();
+ j$.Clock = jRequire.Clock();
+ j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler();
+ j$.Env = jRequire.Env(j$);
+ j$.ExceptionFormatter = jRequire.ExceptionFormatter();
+ j$.Expectation = jRequire.Expectation();
+ j$.buildExpectationResult = jRequire.buildExpectationResult();
+ j$.JsApiReporter = jRequire.JsApiReporter();
+ j$.matchersUtil = jRequire.matchersUtil(j$);
+ j$.ObjectContaining = jRequire.ObjectContaining(j$);
+ j$.pp = jRequire.pp(j$);
+ j$.QueueRunner = jRequire.QueueRunner();
+ j$.ReportDispatcher = jRequire.ReportDispatcher();
+ j$.Spec = jRequire.Spec(j$);
+ j$.SpyStrategy = jRequire.SpyStrategy();
+ j$.Suite = jRequire.Suite();
+ j$.Timer = jRequire.Timer();
+ j$.version = jRequire.version();
+
+ j$.matchers = jRequire.requireMatchers(jRequire, j$);
+
+ return j$;
+};
+
+getJasmineRequireObj().requireMatchers = function(jRequire, j$) {
+ var availableMatchers = [
+ "toBe",
+ "toBeCloseTo",
+ "toBeDefined",
+ "toBeFalsy",
+ "toBeGreaterThan",
+ "toBeLessThan",
+ "toBeNaN",
+ "toBeNull",
+ "toBeTruthy",
+ "toBeUndefined",
+ "toContain",
+ "toEqual",
+ "toHaveBeenCalled",
+ "toHaveBeenCalledWith",
+ "toMatch",
+ "toThrow",
+ "toThrowError"
+ ],
+ matchers = {};
+
+ for (var i = 0; i < availableMatchers.length; i++) {
+ var name = availableMatchers[i];
+ matchers[name] = jRequire[name](j$);
+ }
+
+ return matchers;
+};
+
+getJasmineRequireObj().base = function(j$) {
+ j$.unimplementedMethod_ = function() {
+ throw new Error("unimplemented method");
+ };
+
+ j$.MAX_PRETTY_PRINT_DEPTH = 40;
+ j$.DEFAULT_TIMEOUT_INTERVAL = 5000;
+
+ j$.getGlobal = (function() {
+ var jasmineGlobal = eval.call(null, "this");
+ return function() {
+ return jasmineGlobal;
+ };
+ })();
+
+ j$.getEnv = function(options) {
+ var env = j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options);
+ //jasmine. singletons in here (setTimeout blah blah).
+ return env;
+ };
+
+ j$.isArray_ = function(value) {
+ return j$.isA_("Array", value);
+ };
+
+ j$.isString_ = function(value) {
+ return j$.isA_("String", value);
+ };
+
+ j$.isNumber_ = function(value) {
+ return j$.isA_("Number", value);
+ };
+
+ j$.isA_ = function(typeName, value) {
+ return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
+ };
+
+ j$.isDomNode = function(obj) {
+ return obj.nodeType > 0;
+ };
+
+ j$.any = function(clazz) {
+ return new j$.Any(clazz);
+ };
+
+ j$.objectContaining = function(sample) {
+ return new j$.ObjectContaining(sample);
+ };
+
+ j$.createSpy = function(name, originalFn) {
+
+ var spyStrategy = new j$.SpyStrategy({
+ name: name,
+ fn: originalFn,
+ getSpy: function() { return spy; }
+ }),
+ callTracker = new j$.CallTracker(),
+ spy = function() {
+ callTracker.track({
+ object: this,
+ args: Array.prototype.slice.apply(arguments)
+ });
+ return spyStrategy.exec.apply(this, arguments);
+ };
+
+ for (var prop in originalFn) {
+ if (prop === 'and' || prop === 'calls') {
+ throw new Error("Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon");
+ }
+
+ spy[prop] = originalFn[prop];
+ }
+
+ spy.and = spyStrategy;
+ spy.calls = callTracker;
+
+ return spy;
+ };
+
+ j$.isSpy = function(putativeSpy) {
+ if (!putativeSpy) {
+ return false;
+ }
+ return putativeSpy.and instanceof j$.SpyStrategy &&
+ putativeSpy.calls instanceof j$.CallTracker;
+ };
+
+ j$.createSpyObj = function(baseName, methodNames) {
+ if (!j$.isArray_(methodNames) || methodNames.length === 0) {
+ throw "createSpyObj requires a non-empty array of method names to create spies for";
+ }
+ var obj = {};
+ for (var i = 0; i < methodNames.length; i++) {
+ obj[methodNames[i]] = j$.createSpy(baseName + '.' + methodNames[i]);
+ }
+ return obj;
+ };
+};
+
+getJasmineRequireObj().util = function() {
+
+ var util = {};
+
+ util.inherit = function(childClass, parentClass) {
+ var Subclass = function() {
+ };
+ Subclass.prototype = parentClass.prototype;
+ childClass.prototype = new Subclass();
+ };
+
+ util.htmlEscape = function(str) {
+ if (!str) {
+ return str;
+ }
+ return str.replace(/&/g, '&')
+ .replace(/</g, '<')
+ .replace(/>/g, '>');
+ };
+
+ util.argsToArray = function(args) {
+ var arrayOfArgs = [];
+ for (var i = 0; i < args.length; i++) {
+ arrayOfArgs.push(args[i]);
+ }
+ return arrayOfArgs;
+ };
+
+ util.isUndefined = function(obj) {
+ return obj === void 0;
+ };
+
+ return util;
+};
+
+getJasmineRequireObj().Spec = function(j$) {
+ function Spec(attrs) {
+ this.expectationFactory = attrs.expectationFactory;
+ this.resultCallback = attrs.resultCallback || function() {};
+ this.id = attrs.id;
+ this.description = attrs.description || '';
+ this.fn = attrs.fn;
+ this.beforeFns = attrs.beforeFns || function() { return []; };
+ this.afterFns = attrs.afterFns || function() { return []; };
+ this.onStart = attrs.onStart || function() {};
+ this.exceptionFormatter = attrs.exceptionFormatter || function() {};
+ this.getSpecName = attrs.getSpecName || function() { return ''; };
+ this.expectationResultFactory = attrs.expectationResultFactory || function() { };
+ this.queueRunnerFactory = attrs.queueRunnerFactory || function() {};
+ this.catchingExceptions = attrs.catchingExceptions || function() { return true; };
+
+ this.timer = attrs.timer || {setTimeout: setTimeout, clearTimeout: clearTimeout};
+
+ if (!this.fn) {
+ this.pend();
+ }
+
+ this.result = {
+ id: this.id,
+ description: this.description,
+ fullName: this.getFullName(),
+ failedExpectations: []
+ };
+ }
+
+ Spec.prototype.addExpectationResult = function(passed, data) {
+ if (passed) {
+ return;
+ }
+ this.result.failedExpectations.push(this.expectationResultFactory(data));
+ };
+
+ Spec.prototype.expect = function(actual) {
+ return this.expectationFactory(actual, this);
+ };
+
+ Spec.prototype.execute = function(onComplete) {
+ var self = this,
+ timeout;
+
+ this.onStart(this);
+
+ if (this.markedPending || this.disabled) {
+ complete();
+ return;
+ }
+
+ function timeoutable(fn) {
+ return function(done) {
+ timeout = Function.prototype.apply.apply(self.timer.setTimeout, [j$.getGlobal(), [function() {
+ onException(new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.'));
+ done();
+ }, j$.DEFAULT_TIMEOUT_INTERVAL]]);
+
+ var callDone = function() {
+ clearTimeoutable();
+ done();
+ };
+
+ fn.call(this, callDone); //TODO: do we care about more than 1 arg?
+ };
+ }
+
+ function clearTimeoutable() {
+ Function.prototype.apply.apply(self.timer.clearTimeout, [j$.getGlobal(), [timeout]]);
+ timeout = void 0;
+ }
+
+ var allFns = this.beforeFns().concat(this.fn).concat(this.afterFns()),
+ allTimeoutableFns = [];
+ for (var i = 0; i < allFns.length; i++) {
+ var fn = allFns[i];
+ allTimeoutableFns.push(fn.length > 0 ? timeoutable(fn) : fn);
+ }
+
+ this.queueRunnerFactory({
+ fns: allTimeoutableFns,
+ onException: onException,
+ onComplete: complete
+ });
+
+ function onException(e) {
+ clearTimeoutable();
+ if (Spec.isPendingSpecException(e)) {
+ self.pend();
+ return;
+ }
+
+ self.addExpectationResult(false, {
+ matcherName: "",
+ passed: false,
+ expected: "",
+ actual: "",
+ error: e
+ });
+ }
+
+ function complete() {
+ self.result.status = self.status();
+ self.resultCallback(self.result);
+
+ if (onComplete) {
+ onComplete();
+ }
+ }
+ };
+
+ Spec.prototype.disable = function() {
+ this.disabled = true;
+ };
+
+ Spec.prototype.pend = function() {
+ this.markedPending = true;
+ };
+
+ Spec.prototype.status = function() {
+ if (this.disabled) {
+ return 'disabled';
+ }
+
+ if (this.markedPending) {
+ return 'pending';
+ }
+
+ if (this.result.failedExpectations.length > 0) {
+ return 'failed';
+ } else {
+ return 'passed';
+ }
+ };
+
+ Spec.prototype.getFullName = function() {
+ return this.getSpecName(this);
+ };
+
+ Spec.pendingSpecExceptionMessage = "=> marked Pending";
+
+ Spec.isPendingSpecException = function(e) {
+ return e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1;
+ };
+
+ return Spec;
+};
+
+if (typeof window == void 0 && typeof exports == "object") {
+ exports.Spec = jasmineRequire.Spec;
+}
+
+getJasmineRequireObj().Env = function(j$) {
+ function Env(options) {
+ options = options || {};
+
+ var self = this;
+ var global = options.global || j$.getGlobal();
+
+ var totalSpecsDefined = 0;
+
+ var catchExceptions = true;
+
+ var realSetTimeout = j$.getGlobal().setTimeout;
+ var realClearTimeout = j$.getGlobal().clearTimeout;
+ this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler());
+
+ var runnableLookupTable = {};
+
+ var spies = [];
+
+ var currentSpec = null;
+ var currentSuite = null;
+
+ var reporter = new j$.ReportDispatcher([
+ "jasmineStarted",
+ "jasmineDone",
+ "suiteStarted",
+ "suiteDone",
+ "specStarted",
+ "specDone"
+ ]);
+
+ this.specFilter = function() {
+ return true;
+ };
+
+ var equalityTesters = [];
+
+ var customEqualityTesters = [];
+ this.addCustomEqualityTester = function(tester) {
+ customEqualityTesters.push(tester);
+ };
+
+ j$.Expectation.addCoreMatchers(j$.matchers);
+
+ var nextSpecId = 0;
+ var getNextSpecId = function() {
+ return 'spec' + nextSpecId++;
+ };
+
+ var nextSuiteId = 0;
+ var getNextSuiteId = function() {
+ return 'suite' + nextSuiteId++;
+ };
+
+ var expectationFactory = function(actual, spec) {
+ return j$.Expectation.Factory({
+ util: j$.matchersUtil,
+ customEqualityTesters: customEqualityTesters,
+ actual: actual,
+ addExpectationResult: addExpectationResult
+ });
+
+ function addExpectationResult(passed, result) {
+ return spec.addExpectationResult(passed, result);
+ }
+ };
+
+ var specStarted = function(spec) {
+ currentSpec = spec;
+ reporter.specStarted(spec.result);
+ };
+
+ var beforeFns = function(suite) {
+ return function() {
+ var befores = [];
+ while(suite) {
+ befores = befores.concat(suite.beforeFns);
+ suite = suite.parentSuite;
+ }
+ return befores.reverse();
+ };
+ };
+
+ var afterFns = function(suite) {
+ return function() {
+ var afters = [];
+ while(suite) {
+ afters = afters.concat(suite.afterFns);
+ suite = suite.parentSuite;
+ }
+ return afters;
+ };
+ };
+
+ var getSpecName = function(spec, suite) {
+ return suite.getFullName() + ' ' + spec.description;
+ };
+
+ // TODO: we may just be able to pass in the fn instead of wrapping here
+ var buildExpectationResult = j$.buildExpectationResult,
+ exceptionFormatter = new j$.ExceptionFormatter(),
+ expectationResultFactory = function(attrs) {
+ attrs.messageFormatter = exceptionFormatter.message;
+ attrs.stackFormatter = exceptionFormatter.stack;
+
+ return buildExpectationResult(attrs);
+ };
+
+ // TODO: fix this naming, and here's where the value comes in
+ this.catchExceptions = function(value) {
+ catchExceptions = !!value;
+ return catchExceptions;
+ };
+
+ this.catchingExceptions = function() {
+ return catchExceptions;
+ };
+
+ var maximumSpecCallbackDepth = 20;
+ var currentSpecCallbackDepth = 0;
+
+ function clearStack(fn) {
+ currentSpecCallbackDepth++;
+ if (currentSpecCallbackDepth >= maximumSpecCallbackDepth) {
+ currentSpecCallbackDepth = 0;
+ realSetTimeout(fn, 0);
+ } else {
+ fn();
+ }
+ }
+
+ var catchException = function(e) {
+ return j$.Spec.isPendingSpecException(e) || catchExceptions;
+ };
+
+ var queueRunnerFactory = function(options) {
+ options.catchException = catchException;
+ options.clearStack = options.clearStack || clearStack;
+
+ new j$.QueueRunner(options).execute();
+ };
+
+ var topSuite = new j$.Suite({
+ env: this,
+ id: getNextSuiteId(),
+ description: 'Jasmine__TopLevel__Suite',
+ queueRunner: queueRunnerFactory,
+ resultCallback: function() {} // TODO - hook this up
+ });
+ runnableLookupTable[topSuite.id] = topSuite;
+ currentSuite = topSuite;
+
+ this.topSuite = function() {
+ return topSuite;
+ };
+
+ this.execute = function(runnablesToRun) {
+ runnablesToRun = runnablesToRun || [topSuite.id];
+
+ var allFns = [];
+ for(var i = 0; i < runnablesToRun.length; i++) {
+ var runnable = runnableLookupTable[runnablesToRun[i]];
+ allFns.push((function(runnable) { return function(done) { runnable.execute(done); }; })(runnable));
+ }
+
+ reporter.jasmineStarted({
+ totalSpecsDefined: totalSpecsDefined
+ });
+
+ queueRunnerFactory({fns: allFns, onComplete: reporter.jasmineDone});
+ };
+
+ this.addReporter = function(reporterToAdd) {
+ reporter.addReporter(reporterToAdd);
+ };
+
+ this.addMatchers = function(matchersToAdd) {
+ j$.Expectation.addMatchers(matchersToAdd);
+ };
+
+ this.spyOn = function(obj, methodName) {
+ if (j$.util.isUndefined(obj)) {
+ throw new Error("spyOn could not find an object to spy upon for " + methodName + "()");
+ }
+
+ if (j$.util.isUndefined(obj[methodName])) {
+ throw new Error(methodName + '() method does not exist');
+ }
+
+ if (obj[methodName] && j$.isSpy(obj[methodName])) {
+ //TODO?: should this return the current spy? Downside: may cause user confusion about spy state
+ throw new Error(methodName + ' has already been spied upon');
+ }
+
+ var spy = j$.createSpy(methodName, obj[methodName]);
+
+ spies.push({
+ spy: spy,
+ baseObj: obj,
+ methodName: methodName,
+ originalValue: obj[methodName]
+ });
+
+ obj[methodName] = spy;
+
+ return spy;
+ };
+
+ var suiteFactory = function(description) {
+ var suite = new j$.Suite({
+ env: self,
+ id: getNextSuiteId(),
+ description: description,
+ parentSuite: currentSuite,
+ queueRunner: queueRunnerFactory,
+ onStart: suiteStarted,
+ resultCallback: function(attrs) {
+ reporter.suiteDone(attrs);
+ }
+ });
+
+ runnableLookupTable[suite.id] = suite;
+ return suite;
+ };
+
+ this.describe = function(description, specDefinitions) {
+ var suite = suiteFactory(description);
+
+ var parentSuite = currentSuite;
+ parentSuite.addChild(suite);
+ currentSuite = suite;
+
+ var declarationError = null;
+ try {
+ specDefinitions.call(suite);
+ } catch (e) {
+ declarationError = e;
+ }
+
+ if (declarationError) {
+ this.it("encountered a declaration exception", function() {
+ throw declarationError;
+ });
+ }
+
+ currentSuite = parentSuite;
+
+ return suite;
+ };
+
+ this.xdescribe = function(description, specDefinitions) {
+ var suite = this.describe(description, specDefinitions);
+ suite.disable();
+ return suite;
+ };
+
+ var specFactory = function(description, fn, suite) {
+ totalSpecsDefined++;
+
+ var spec = new j$.Spec({
+ id: getNextSpecId(),
+ beforeFns: beforeFns(suite),
+ afterFns: afterFns(suite),
+ expectationFactory: expectationFactory,
+ exceptionFormatter: exceptionFormatter,
+ resultCallback: specResultCallback,
+ getSpecName: function(spec) {
+ return getSpecName(spec, suite);
+ },
+ onStart: specStarted,
+ description: description,
+ expectationResultFactory: expectationResultFactory,
+ queueRunnerFactory: queueRunnerFactory,
+ fn: fn,
+ timer: {setTimeout: realSetTimeout, clearTimeout: realClearTimeout}
+ });
+
+ runnableLookupTable[spec.id] = spec;
+
+ if (!self.specFilter(spec)) {
+ spec.disable();
+ }
+
+ return spec;
+
+ function removeAllSpies() {
+ for (var i = 0; i < spies.length; i++) {
+ var spyEntry = spies[i];
+ spyEntry.baseObj[spyEntry.methodName] = spyEntry.originalValue;
+ }
+ spies = [];
+ }
+
+ function specResultCallback(result) {
+ removeAllSpies();
+ j$.Expectation.resetMatchers();
+ customEqualityTesters = [];
+ currentSpec = null;
+ reporter.specDone(result);
+ }
+ };
+
+ var suiteStarted = function(suite) {
+ reporter.suiteStarted(suite.result);
+ };
+
+ this.it = function(description, fn) {
+ var spec = specFactory(description, fn, currentSuite);
+ currentSuite.addChild(spec);
+ return spec;
+ };
+
+ this.xit = function(description, fn) {
+ var spec = this.it(description, fn);
+ spec.pend();
+ return spec;
+ };
+
+ this.expect = function(actual) {
+ return currentSpec.expect(actual);
+ };
+
+ this.beforeEach = function(beforeEachFunction) {
+ currentSuite.beforeEach(beforeEachFunction);
+ };
+
+ this.afterEach = function(afterEachFunction) {
+ currentSuite.afterEach(afterEachFunction);
+ };
+
+ this.pending = function() {
+ throw j$.Spec.pendingSpecExceptionMessage;
+ };
+ }
+
+ return Env;
+};
+
+getJasmineRequireObj().JsApiReporter = function() {
+
+ var noopTimer = {
+ start: function(){},
+ elapsed: function(){ return 0; }
+ };
+
+ function JsApiReporter(options) {
+ var timer = options.timer || noopTimer,
+ status = "loaded";
+
+ this.started = false;
+ this.finished = false;
+
+ this.jasmineStarted = function() {
+ this.started = true;
+ status = 'started';
+ timer.start();
+ };
+
+ var executionTime;
+
+ this.jasmineDone = function() {
+ this.finished = true;
+ executionTime = timer.elapsed();
+ status = 'done';
+ };
+
+ this.status = function() {
+ return status;
+ };
+
+ var suites = {};
+
+ this.suiteStarted = function(result) {
+ storeSuite(result);
+ };
+
+ this.suiteDone = function(result) {
+ storeSuite(result);
+ };
+
+ function storeSuite(result) {
+ suites[result.id] = result;
+ }
+
+ this.suites = function() {
+ return suites;
+ };
+
+ var specs = [];
+ this.specStarted = function(result) { };
+
+ this.specDone = function(result) {
+ specs.push(result);
+ };
+
+ this.specResults = function(index, length) {
+ return specs.slice(index, index + length);
+ };
+
+ this.specs = function() {
+ return specs;
+ };
+
+ this.executionTime = function() {
+ return executionTime;
+ };
+
+ }
+
+ return JsApiReporter;
+};
+
+getJasmineRequireObj().Any = function() {
+
+ function Any(expectedObject) {
+ this.expectedObject = expectedObject;
+ }
+
+ Any.prototype.jasmineMatches = function(other) {
+ if (this.expectedObject == String) {
+ return typeof other == 'string' || other instanceof String;
+ }
+
+ if (this.expectedObject == Number) {
+ return typeof other == 'number' || other instanceof Number;
+ }
+
+ if (this.expectedObject == Function) {
+ return typeof other == 'function' || other instanceof Function;
+ }
+
+ if (this.expectedObject == Object) {
+ return typeof other == 'object';
+ }
+
+ if (this.expectedObject == Boolean) {
+ return typeof other == 'boolean';
+ }
+
+ return other instanceof this.expectedObject;
+ };
+
+ Any.prototype.jasmineToString = function() {
+ return '<jasmine.any(' + this.expectedClass + ')>';
+ };
+
+ return Any;
+};
+
+getJasmineRequireObj().CallTracker = function() {
+
+ function CallTracker() {
+ var calls = [];
+
+ this.track = function(context) {
+ calls.push(context);
+ };
+
+ this.any = function() {
+ return !!calls.length;
+ };
+
+ this.count = function() {
+ return calls.length;
+ };
+
+ this.argsFor = function(index) {
+ var call = calls[index];
+ return call ? call.args : [];
+ };
+
+ this.all = function() {
+ return calls;
+ };
+
+ this.allArgs = function() {
+ var callArgs = [];
+ for(var i = 0; i < calls.length; i++){
+ callArgs.push(calls[i].args);
+ }
+
+ return callArgs;
+ };
+
+ this.first = function() {
+ return calls[0];
+ };
+
+ this.mostRecent = function() {
+ return calls[calls.length - 1];
+ };
+
+ this.reset = function() {
+ calls = [];
+ };
+ }
+
+ return CallTracker;
+};
+
+getJasmineRequireObj().Clock = function() {
+ function Clock(global, delayedFunctionScheduler) {
+ var self = this,
+ realTimingFunctions = {
+ setTimeout: global.setTimeout,
+ clearTimeout: global.clearTimeout,
+ setInterval: global.setInterval,
+ clearInterval: global.clearInterval
+ },
+ fakeTimingFunctions = {
+ setTimeout: setTimeout,
+ clearTimeout: clearTimeout,
+ setInterval: setInterval,
+ clearInterval: clearInterval
+ },
+ installed = false,
+ timer;
+
+ self.install = function() {
+ replace(global, fakeTimingFunctions);
+ timer = fakeTimingFunctions;
+ installed = true;
+ };
+
+ self.uninstall = function() {
+ delayedFunctionScheduler.reset();
+ replace(global, realTimingFunctions);
+ timer = realTimingFunctions;
+ installed = false;
+ };
+
+ self.setTimeout = function(fn, delay, params) {
+ if (legacyIE()) {
+ if (arguments.length > 2) {
+ throw new Error("IE < 9 cannot support extra params to setTimeout without a polyfill");
+ }
+ return timer.setTimeout(fn, delay);
+ }
+ return Function.prototype.apply.apply(timer.setTimeout, [global, arguments]);
+ };
+
+ self.setInterval = function(fn, delay, params) {
+ if (legacyIE()) {
+ if (arguments.length > 2) {
+ throw new Error("IE < 9 cannot support extra params to setInterval without a polyfill");
+ }
+ return timer.setInterval(fn, delay);
+ }
+ return Function.prototype.apply.apply(timer.setInterval, [global, arguments]);
+ };
+
+ self.clearTimeout = function(id) {
+ return Function.prototype.call.apply(timer.clearTimeout, [global, id]);
+ };
+
+ self.clearInterval = function(id) {
+ return Function.prototype.call.apply(timer.clearInterval, [global, id]);
+ };
+
+ self.tick = function(millis) {
+ if (installed) {
+ delayedFunctionScheduler.tick(millis);
+ } else {
+ throw new Error("Mock clock is not installed, use jasmine.clock().install()");
+ }
+ };
+
+ return self;
+
+ function legacyIE() {
+ //if these methods are polyfilled, apply will be present
+ return !(realTimingFunctions.setTimeout || realTimingFunctions.setInterval).apply;
+ }
+
+ function replace(dest, source) {
+ for (var prop in source) {
+ dest[prop] = source[prop];
+ }
+ }
+
+ function setTimeout(fn, delay) {
+ return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2));
+ }
+
+ function clearTimeout(id) {
+ return delayedFunctionScheduler.removeFunctionWithId(id);
+ }
+
+ function setInterval(fn, interval) {
+ return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true);
+ }
+
+ function clearInterval(id) {
+ return delayedFunctionScheduler.removeFunctionWithId(id);
+ }
+
+ function argSlice(argsObj, n) {
+ return Array.prototype.slice.call(argsObj, 2);
+ }
+ }
+
+ return Clock;
+};
+
+getJasmineRequireObj().DelayedFunctionScheduler = function() {
+ function DelayedFunctionScheduler() {
+ var self = this;
+ var scheduledLookup = [];
+ var scheduledFunctions = {};
+ var currentTime = 0;
+ var delayedFnCount = 0;
+
+ self.tick = function(millis) {
+ millis = millis || 0;
+ var endTime = currentTime + millis;
+
+ runScheduledFunctions(endTime);
+ currentTime = endTime;
+ };
+
+ self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) {
+ var f;
+ if (typeof(funcToCall) === 'string') {
+ /* jshint evil: true */
+ f = function() { return eval(funcToCall); };
+ /* jshint evil: false */
+ } else {
+ f = funcToCall;
+ }
+
+ millis = millis || 0;
+ timeoutKey = timeoutKey || ++delayedFnCount;
+ runAtMillis = runAtMillis || (currentTime + millis);
+
+ var funcToSchedule = {
+ runAtMillis: runAtMillis,
+ funcToCall: f,
+ recurring: recurring,
+ params: params,
+ timeoutKey: timeoutKey,
+ millis: millis
+ };
+
+ if (runAtMillis in scheduledFunctions) {
+ scheduledFunctions[runAtMillis].push(funcToSchedule);
+ } else {
+ scheduledFunctions[runAtMillis] = [funcToSchedule];
+ scheduledLookup.push(runAtMillis);
+ scheduledLookup.sort(function (a, b) {
+ return a - b;
+ });
+ }
+
+ return timeoutKey;
+ };
+
+ self.removeFunctionWithId = function(timeoutKey) {
+ for (var runAtMillis in scheduledFunctions) {
+ var funcs = scheduledFunctions[runAtMillis];
+ var i = indexOfFirstToPass(funcs, function (func) {
+ return func.timeoutKey === timeoutKey;
+ });
+
+ if (i > -1) {
+ if (funcs.length === 1) {
+ delete scheduledFunctions[runAtMillis];
+ deleteFromLookup(runAtMillis);
+ } else {
+ funcs.splice(i, 1);
+ }
+
+ // intervals get rescheduled when executed, so there's never more
+ // than a single scheduled function with a given timeoutKey
+ break;
+ }
+ }
+ };
+
+ self.reset = function() {
+ currentTime = 0;
+ scheduledLookup = [];
+ scheduledFunctions = {};
+ delayedFnCount = 0;
+ };
+
+ return self;
+
+ function indexOfFirstToPass(array, testFn) {
+ var index = -1;
+
+ for (var i = 0; i < array.length; ++i) {
+ if (testFn(array[i])) {
+ index = i;
+ break;
+ }
+ }
+
+ return index;
+ }
+
+ function deleteFromLookup(key) {
+ var value = Number(key);
+ var i = indexOfFirstToPass(scheduledLookup, function (millis) {
+ return millis === value;
+ });
+
+ if (i > -1) {
+ scheduledLookup.splice(i, 1);
+ }
+ }
+
+ function reschedule(scheduledFn) {
+ self.scheduleFunction(scheduledFn.funcToCall,
+ scheduledFn.millis,
+ scheduledFn.params,
+ true,
+ scheduledFn.timeoutKey,
+ scheduledFn.runAtMillis + scheduledFn.millis);
+ }
+
+ function runScheduledFunctions(endTime) {
+ if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) {
+ return;
+ }
+
+ do {
+ currentTime = scheduledLookup.shift();
+
+ var funcsToRun = scheduledFunctions[currentTime];
+ delete scheduledFunctions[currentTime];
+
+ for (var i = 0; i < funcsToRun.length; ++i) {
+ var funcToRun = funcsToRun[i];
+ funcToRun.funcToCall.apply(null, funcToRun.params || []);
+
+ if (funcToRun.recurring) {
+ reschedule(funcToRun);
+ }
+ }
+ } while (scheduledLookup.length > 0 &&
+ // checking first if we're out of time prevents setTimeout(0)
+ // scheduled in a funcToRun from forcing an extra iteration
+ currentTime !== endTime &&
+ scheduledLookup[0] <= endTime);
+ }
+ }
+
+ return DelayedFunctionScheduler;
+};
+
+getJasmineRequireObj().ExceptionFormatter = function() {
+ function ExceptionFormatter() {
+ this.message = function(error) {
+ var message = error.name +
+ ': ' +
+ error.message;
+
+ if (error.fileName || error.sourceURL) {
+ message += " in " + (error.fileName || error.sourceURL);
+ }
+
+ if (error.line || error.lineNumber) {
+ message += " (line " + (error.line || error.lineNumber) + ")";
+ }
+
+ return message;
+ };
+
+ this.stack = function(error) {
+ return error ? error.stack : null;
+ };
+ }
+
+ return ExceptionFormatter;
+};
+
+getJasmineRequireObj().Expectation = function() {
+
+ var matchers = {};
+
+ function Expectation(options) {
+ this.util = options.util || { buildFailureMessage: function() {} };
+ this.customEqualityTesters = options.customEqualityTesters || [];
+ this.actual = options.actual;
+ this.addExpectationResult = options.addExpectationResult || function(){};
+ this.isNot = options.isNot;
+
+ for (var matcherName in matchers) {
+ this[matcherName] = matchers[matcherName];
+ }
+ }
+
+ Expectation.prototype.wrapCompare = function(name, matcherFactory) {
+ return function() {
+ var args = Array.prototype.slice.call(arguments, 0),
+ expected = args.slice(0),
+ message = "";
+
+ args.unshift(this.actual);
+
+ var matcher = matcherFactory(this.util, this.customEqualityTesters),
+ matcherCompare = matcher.compare;
+
+ function defaultNegativeCompare() {
+ var result = matcher.compare.apply(null, args);
+ result.pass = !result.pass;
+ return result;
+ }
+
+ if (this.isNot) {
+ matcherCompare = matcher.negativeCompare || defaultNegativeCompare;
+ }
+
+ var result = matcherCompare.apply(null, args);
+
+ if (!result.pass) {
+ if (!result.message) {
+ args.unshift(this.isNot);
+ args.unshift(name);
+ message = this.util.buildFailureMessage.apply(null, args);
+ } else {
+ message = result.message;
+ }
+ }
+
+ if (expected.length == 1) {
+ expected = expected[0];
+ }
+
+ // TODO: how many of these params are needed?
+ this.addExpectationResult(
+ result.pass,
+ {
+ matcherName: name,
+ passed: result.pass,
+ message: message,
+ actual: this.actual,
+ expected: expected // TODO: this may need to be arrayified/sliced
+ }
+ );
+ };
+ };
+
+ Expectation.addCoreMatchers = function(matchers) {
+ var prototype = Expectation.prototype;
+ for (var matcherName in matchers) {
+ var matcher = matchers[matcherName];
+ prototype[matcherName] = prototype.wrapCompare(matcherName, matcher);
+ }
+ };
+
+ Expectation.addMatchers = function(matchersToAdd) {
+ for (var name in matchersToAdd) {
+ var matcher = matchersToAdd[name];
+ matchers[name] = Expectation.prototype.wrapCompare(name, matcher);
+ }
+ };
+
+ Expectation.resetMatchers = function() {
+ for (var name in matchers) {
+ delete matchers[name];
+ }
+ };
+
+ Expectation.Factory = function(options) {
+ options = options || {};
+
+ var expect = new Expectation(options);
+
+ // TODO: this would be nice as its own Object - NegativeExpectation
+ // TODO: copy instead of mutate options
+ options.isNot = true;
+ expect.not = new Expectation(options);
+
+ return expect;
+ };
+
+ return Expectation;
+};
+
+//TODO: expectation result may make more sense as a presentation of an expectation.
+getJasmineRequireObj().buildExpectationResult = function() {
+ function buildExpectationResult(options) {
+ var messageFormatter = options.messageFormatter || function() {},
+ stackFormatter = options.stackFormatter || function() {};
+
+ return {
+ matcherName: options.matcherName,
+ expected: options.expected,
+ actual: options.actual,
+ message: message(),
+ stack: stack(),
+ passed: options.passed
+ };
+
+ function message() {
+ if (options.passed) {
+ return "Passed.";
+ } else if (options.message) {
+ return options.message;
+ } else if (options.error) {
+ return messageFormatter(options.error);
+ }
+ return "";
+ }
+
+ function stack() {
+ if (options.passed) {
+ return "";
+ }
+
+ var error = options.error;
+ if (!error) {
+ try {
+ throw new Error(message());
+ } catch (e) {
+ error = e;
+ }
+ }
+ return stackFormatter(error);
+ }
+ }
+
+ return buildExpectationResult;
+};
+
+getJasmineRequireObj().ObjectContaining = function(j$) {
+
+ function ObjectContaining(sample) {
+ this.sample = sample;
+ }
+
+ ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) {
+ if (typeof(this.sample) !== "object") { throw new Error("You must provide an object to objectContaining, not '"+this.sample+"'."); }
+
+ mismatchKeys = mismatchKeys || [];
+ mismatchValues = mismatchValues || [];
+
+ var hasKey = function(obj, keyName) {
+ return obj !== null && !j$.util.isUndefined(obj[keyName]);
+ };
+
+ for (var property in this.sample) {
+ if (!hasKey(other, property) && hasKey(this.sample, property)) {
+ mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
+ }
+ else if (!j$.matchersUtil.equals(this.sample[property], other[property])) {
+ mismatchValues.push("'" + property + "' was '" + (other[property] ? j$.util.htmlEscape(other[property].toString()) : other[property]) + "' in actual, but was '" + (this.sample[property] ? j$.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in expected.");
+ }
+ }
+
+ return (mismatchKeys.length === 0 && mismatchValues.length === 0);
+ };
+
+ ObjectContaining.prototype.jasmineToString = function() {
+ return "<jasmine.objectContaining(" + j$.pp(this.sample) + ")>";
+ };
+
+ return ObjectContaining;
+};
+
+getJasmineRequireObj().pp = function(j$) {
+
+ function PrettyPrinter() {
+ this.ppNestLevel_ = 0;
+ }
+
+ PrettyPrinter.prototype.format = function(value) {
+ this.ppNestLevel_++;
+ try {
+ if (j$.util.isUndefined(value)) {
+ this.emitScalar('undefined');
+ } else if (value === null) {
+ this.emitScalar('null');
+ } else if (value === j$.getGlobal()) {
+ this.emitScalar('<global>');
+ } else if (value.jasmineToString) {
+ this.emitScalar(value.jasmineToString());
+ } else if (typeof value === 'string') {
+ this.emitString(value);
+ } else if (j$.isSpy(value)) {
+ this.emitScalar("spy on " + value.and.identity());
+ } else if (value instanceof RegExp) {
+ this.emitScalar(value.toString());
+ } else if (typeof value === 'function') {
+ this.emitScalar('Function');
+ } else if (typeof value.nodeType === 'number') {
+ this.emitScalar('HTMLNode');
+ } else if (value instanceof Date) {
+ this.emitScalar('Date(' + value + ')');
+ } else if (value.__Jasmine_been_here_before__) {
+ this.emitScalar('<circular reference: ' + (j$.isArray_(value) ? 'Array' : 'Object') + '>');
+ } else if (j$.isArray_(value) || j$.isA_('Object', value)) {
+ value.__Jasmine_been_here_before__ = true;
+ if (j$.isArray_(value)) {
+ this.emitArray(value);
+ } else {
+ this.emitObject(value);
+ }
+ delete value.__Jasmine_been_here_before__;
+ } else {
+ this.emitScalar(value.toString());
+ }
+ } finally {
+ this.ppNestLevel_--;
+ }
+ };
+
+ PrettyPrinter.prototype.iterateObject = function(obj, fn) {
+ for (var property in obj) {
+ if (!obj.hasOwnProperty(property)) { continue; }
+ if (property == '__Jasmine_been_here_before__') { continue; }
+ fn(property, obj.__lookupGetter__ ? (!j$.util.isUndefined(obj.__lookupGetter__(property)) &&
+ obj.__lookupGetter__(property) !== null) : false);
+ }
+ };
+
+ PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_;
+ PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_;
+ PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_;
+ PrettyPrinter.prototype.emitString = j$.unimplementedMethod_;
+
+ function StringPrettyPrinter() {
+ PrettyPrinter.call(this);
+
+ this.string = '';
+ }
+
+ j$.util.inherit(StringPrettyPrinter, PrettyPrinter);
+
+ StringPrettyPrinter.prototype.emitScalar = function(value) {
+ this.append(value);
+ };
+
+ StringPrettyPrinter.prototype.emitString = function(value) {
+ this.append("'" + value + "'");
+ };
+
+ StringPrettyPrinter.prototype.emitArray = function(array) {
+ if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
+ this.append("Array");
+ return;
+ }
+
+ this.append('[ ');
+ for (var i = 0; i < array.length; i++) {
+ if (i > 0) {
+ this.append(', ');
+ }
+ this.format(array[i]);
+ }
+ this.append(' ]');
+ };
+
+ StringPrettyPrinter.prototype.emitObject = function(obj) {
+ if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
+ this.append("Object");
+ return;
+ }
+
+ var self = this;
+ this.append('{ ');
+ var first = true;
+
+ this.iterateObject(obj, function(property, isGetter) {
+ if (first) {
+ first = false;
+ } else {
+ self.append(', ');
+ }
+
+ self.append(property);
+ self.append(' : ');
+ if (isGetter) {
+ self.append('<getter>');
+ } else {
+ self.format(obj[property]);
+ }
+ });
+
+ this.append(' }');
+ };
+
+ StringPrettyPrinter.prototype.append = function(value) {
+ this.string += value;
+ };
+
+ return function(value) {
+ var stringPrettyPrinter = new StringPrettyPrinter();
+ stringPrettyPrinter.format(value);
+ return stringPrettyPrinter.string;
+ };
+};
+
+getJasmineRequireObj().QueueRunner = function() {
+
+ function QueueRunner(attrs) {
+ this.fns = attrs.fns || [];
+ this.onComplete = attrs.onComplete || function() {};
+ this.clearStack = attrs.clearStack || function(fn) {fn();};
+ this.onException = attrs.onException || function() {};
+ this.catchException = attrs.catchException || function() { return true; };
+ this.userContext = {};
+ }
+
+ QueueRunner.prototype.execute = function() {
+ this.run(this.fns, 0);
+ };
+
+ QueueRunner.prototype.run = function(fns, recursiveIndex) {
+ var length = fns.length,
+ self = this,
+ iterativeIndex;
+
+ for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) {
+ var fn = fns[iterativeIndex];
+ if (fn.length > 0) {
+ return attemptAsync(fn);
+ } else {
+ attemptSync(fn);
+ }
+ }
+
+ var runnerDone = iterativeIndex >= length;
+
+ if (runnerDone) {
+ this.clearStack(this.onComplete);
+ }
+
+ function attemptSync(fn) {
+ try {
+ fn.call(self.userContext);
+ } catch (e) {
+ handleException(e);
+ }
+ }
+
+ function attemptAsync(fn) {
+ var next = function () { self.run(fns, iterativeIndex + 1); };
+
+ try {
+ fn.call(self.userContext, next);
+ } catch (e) {
+ handleException(e);
+ next();
+ }
+ }
+
+ function handleException(e) {
+ self.onException(e);
+ if (!self.catchException(e)) {
+ //TODO: set a var when we catch an exception and
+ //use a finally block to close the loop in a nice way..
+ throw e;
+ }
+ }
+ };
+
+ return QueueRunner;
+};
+
+getJasmineRequireObj().ReportDispatcher = function() {
+ function ReportDispatcher(methods) {
+
+ var dispatchedMethods = methods || [];
+
+ for (var i = 0; i < dispatchedMethods.length; i++) {
+ var method = dispatchedMethods[i];
+ this[method] = (function(m) {
+ return function() {
+ dispatch(m, arguments);
+ };
+ }(method));
+ }
+
+ var reporters = [];
+
+ this.addReporter = function(reporter) {
+ reporters.push(reporter);
+ };
+
+ return this;
+
+ function dispatch(method, args) {
+ for (var i = 0; i < reporters.length; i++) {
+ var reporter = reporters[i];
+ if (reporter[method]) {
+ reporter[method].apply(reporter, args);
+ }
+ }
+ }
+ }
+
+ return ReportDispatcher;
+};
+
+
+getJasmineRequireObj().SpyStrategy = function() {
+
+ function SpyStrategy(options) {
+ options = options || {};
+
+ var identity = options.name || "unknown",
+ originalFn = options.fn || function() {},
+ getSpy = options.getSpy || function() {},
+ plan = function() {};
+
+ this.identity = function() {
+ return identity;
+ };
+
+ this.exec = function() {
+ return plan.apply(this, arguments);
+ };
+
+ this.callThrough = function() {
+ plan = originalFn;
+ return getSpy();
+ };
+
+ this.returnValue = function(value) {
+ plan = function() {
+ return value;
+ };
+ return getSpy();
+ };
+
+ this.throwError = function(something) {
+ var error = (something instanceof Error) ? something : new Error(something);
+ plan = function() {
+ throw error;
+ };
+ return getSpy();
+ };
+
+ this.callFake = function(fn) {
+ plan = fn;
+ return getSpy();
+ };
+
+ this.stub = function(fn) {
+ plan = function() {};
+ return getSpy();
+ };
+ }
+
+ return SpyStrategy;
+};
+
+getJasmineRequireObj().Suite = function() {
+ function Suite(attrs) {
+ this.env = attrs.env;
+ this.id = attrs.id;
+ this.parentSuite = attrs.parentSuite;
+ this.description = attrs.description;
+ this.onStart = attrs.onStart || function() {};
+ this.resultCallback = attrs.resultCallback || function() {};
+ this.clearStack = attrs.clearStack || function(fn) {fn();};
+
+ this.beforeFns = [];
+ this.afterFns = [];
+ this.queueRunner = attrs.queueRunner || function() {};
+ this.disabled = false;
+
+ this.children = [];
+
+ this.result = {
+ id: this.id,
+ status: this.disabled ? 'disabled' : '',
+ description: this.description,
+ fullName: this.getFullName()
+ };
+ }
+
+ Suite.prototype.getFullName = function() {
+ var fullName = this.description;
+ for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
+ if (parentSuite.parentSuite) {
+ fullName = parentSuite.description + ' ' + fullName;
+ }
+ }
+ return fullName;
+ };
+
+ Suite.prototype.disable = function() {
+ this.disabled = true;
+ };
+
+ Suite.prototype.beforeEach = function(fn) {
+ this.beforeFns.unshift(fn);
+ };
+
+ Suite.prototype.afterEach = function(fn) {
+ this.afterFns.unshift(fn);
+ };
+
+ Suite.prototype.addChild = function(child) {
+ this.children.push(child);
+ };
+
+ Suite.prototype.execute = function(onComplete) {
+ var self = this;
+ if (this.disabled) {
+ complete();
+ return;
+ }
+
+ var allFns = [];
+
+ for (var i = 0; i < this.children.length; i++) {
+ allFns.push(wrapChildAsAsync(this.children[i]));
+ }
+
+ this.onStart(this);
+
+ this.queueRunner({
+ fns: allFns,
+ onComplete: complete
+ });
+
+ function complete() {
+ self.resultCallback(self.result);
+
+ if (onComplete) {
+ onComplete();
+ }
+ }
+
+ function wrapChildAsAsync(child) {
+ return function(done) { child.execute(done); };
+ }
+ };
+
+ return Suite;
+};
+
+if (typeof window == void 0 && typeof exports == "object") {
+ exports.Suite = jasmineRequire.Suite;
+}
+
+getJasmineRequireObj().Timer = function() {
+ function Timer(options) {
+ options = options || {};
+
+ var now = options.now || function() { return new Date().getTime(); },
+ startTime;
+
+ this.start = function() {
+ startTime = now();
+ };
+
+ this.elapsed = function() {
+ return now() - startTime;
+ };
+ }
+
+ return Timer;
+};
+
+getJasmineRequireObj().matchersUtil = function(j$) {
+ // TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter?
+
+ return {
+ equals: function(a, b, customTesters) {
+ customTesters = customTesters || [];
+
+ return eq(a, b, [], [], customTesters);
+ },
+
+ contains: function(haystack, needle, customTesters) {
+ customTesters = customTesters || [];
+
+ if (Object.prototype.toString.apply(haystack) === "[object Array]") {
+ for (var i = 0; i < haystack.length; i++) {
+ if (eq(haystack[i], needle, [], [], customTesters)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return haystack.indexOf(needle) >= 0;
+ },
+
+ buildFailureMessage: function() {
+ var args = Array.prototype.slice.call(arguments, 0),
+ matcherName = args[0],
+ isNot = args[1],
+ actual = args[2],
+ expected = args.slice(3),
+ englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
+
+ var message = "Expected " +
+ j$.pp(actual) +
+ (isNot ? " not " : " ") +
+ englishyPredicate;
+
+ if (expected.length > 0) {
+ for (var i = 0; i < expected.length; i++) {
+ if (i > 0) {
+ message += ",";
+ }
+ message += " " + j$.pp(expected[i]);
+ }
+ }
+
+ return message + ".";
+ }
+ };
+
+ // Equality function lovingly adapted from isEqual in
+ // [Underscore](http://underscorejs.org)
+ function eq(a, b, aStack, bStack, customTesters) {
+ var result = true;
+
+ for (var i = 0; i < customTesters.length; i++) {
+ var customTesterResult = customTesters[i](a, b);
+ if (!j$.util.isUndefined(customTesterResult)) {
+ return customTesterResult;
+ }
+ }
+
+ if (a instanceof j$.Any) {
+ result = a.jasmineMatches(b);
+ if (result) {
+ return true;
+ }
+ }
+
+ if (b instanceof j$.Any) {
+ result = b.jasmineMatches(a);
+ if (result) {
+ return true;
+ }
+ }
+
+ if (b instanceof j$.ObjectContaining) {
+ result = b.jasmineMatches(a);
+ if (result) {
+ return true;
+ }
+ }
+
+ if (a instanceof Error && b instanceof Error) {
+ return a.message == b.message;
+ }
+
+ // Identical objects are equal. `0 === -0`, but they aren't identical.
+ // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
+ if (a === b) { return a !== 0 || 1 / a == 1 / b; }
+ // A strict comparison is necessary because `null == undefined`.
+ if (a === null || b === null) { return a === b; }
+ var className = Object.prototype.toString.call(a);
+ if (className != Object.prototype.toString.call(b)) { return false; }
+ switch (className) {
+ // Strings, numbers, dates, and booleans are compared by value.
+ case '[object String]':
+ // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
+ // equivalent to `new String("5")`.
+ return a == String(b);
+ case '[object Number]':
+ // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
+ // other numeric values.
+ return a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b);
+ case '[object Date]':
+ case '[object Boolean]':
+ // Coerce dates and booleans to numeric primitive values. Dates are compared by their
+ // millisecond representations. Note that invalid dates with millisecond representations
+ // of `NaN` are not equivalent.
+ return +a == +b;
+ // RegExps are compared by their source patterns and flags.
+ case '[object RegExp]':
+ return a.source == b.source &&
+ a.global == b.global &&
+ a.multiline == b.multiline &&
+ a.ignoreCase == b.ignoreCase;
+ }
+ if (typeof a != 'object' || typeof b != 'object') { return false; }
+ // Assume equality for cyclic structures. The algorithm for detecting cyclic
+ // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
+ var length = aStack.length;
+ while (length--) {
+ // Linear search. Performance is inversely proportional to the number of
+ // unique nested structures.
+ if (aStack[length] == a) { return bStack[length] == b; }
+ }
+ // Add the first object to the stack of traversed objects.
+ aStack.push(a);
+ bStack.push(b);
+ var size = 0;
+ // Recursively compare objects and arrays.
+ if (className == '[object Array]') {
+ // Compare array lengths to determine if a deep comparison is necessary.
+ size = a.length;
+ result = size == b.length;
+ if (result) {
+ // Deep compare the contents, ignoring non-numeric properties.
+ while (size--) {
+ if (!(result = eq(a[size], b[size], aStack, bStack, customTesters))) { break; }
+ }
+ }
+ } else {
+ // Objects with different constructors are not equivalent, but `Object`s
+ // from different frames are.
+ var aCtor = a.constructor, bCtor = b.constructor;
+ if (aCtor !== bCtor && !(isFunction(aCtor) && (aCtor instanceof aCtor) &&
+ isFunction(bCtor) && (bCtor instanceof bCtor))) {
+ return false;
+ }
+ // Deep compare objects.
+ for (var key in a) {
+ if (has(a, key)) {
+ // Count the expected number of properties.
+ size++;
+ // Deep compare each member.
+ if (!(result = has(b, key) && eq(a[key], b[key], aStack, bStack, customTesters))) { break; }
+ }
+ }
+ // Ensure that both objects contain the same number of properties.
+ if (result) {
+ for (key in b) {
+ if (has(b, key) && !(size--)) { break; }
+ }
+ result = !size;
+ }
+ }
+ // Remove the first object from the stack of traversed objects.
+ aStack.pop();
+ bStack.pop();
+
+ return result;
+
+ function has(obj, key) {
+ return obj.hasOwnProperty(key);
+ }
+
+ function isFunction(obj) {
+ return typeof obj === 'function';
+ }
+ }
+};
+
+getJasmineRequireObj().toBe = function() {
+ function toBe() {
+ return {
+ compare: function(actual, expected) {
+ return {
+ pass: actual === expected
+ };
+ }
+ };
+ }
+
+ return toBe;
+};
+
+getJasmineRequireObj().toBeCloseTo = function() {
+
+ function toBeCloseTo() {
+ return {
+ compare: function(actual, expected, precision) {
+ if (precision !== 0) {
+ precision = precision || 2;
+ }
+
+ return {
+ pass: Math.abs(expected - actual) < (Math.pow(10, -precision) / 2)
+ };
+ }
+ };
+ }
+
+ return toBeCloseTo;
+};
+
+getJasmineRequireObj().toBeDefined = function() {
+ function toBeDefined() {
+ return {
+ compare: function(actual) {
+ return {
+ pass: (void 0 !== actual)
+ };
+ }
+ };
+ }
+
+ return toBeDefined;
+};
+
+getJasmineRequireObj().toBeFalsy = function() {
+ function toBeFalsy() {
+ return {
+ compare: function(actual) {
+ return {
+ pass: !!!actual
+ };
+ }
+ };
+ }
+
+ return toBeFalsy;
+};
+
+getJasmineRequireObj().toBeGreaterThan = function() {
+
+ function toBeGreaterThan() {
+ return {
+ compare: function(actual, expected) {
+ return {
+ pass: actual > expected
+ };
+ }
+ };
+ }
+
+ return toBeGreaterThan;
+};
+
+
+getJasmineRequireObj().toBeLessThan = function() {
+ function toBeLessThan() {
+ return {
+
+ compare: function(actual, expected) {
+ return {
+ pass: actual < expected
+ };
+ }
+ };
+ }
+
+ return toBeLessThan;
+};
+getJasmineRequireObj().toBeNaN = function(j$) {
+
+ function toBeNaN() {
+ return {
+ compare: function(actual) {
+ var result = {
+ pass: (actual !== actual)
+ };
+
+ if (result.pass) {
+ result.message = "Expected actual not to be NaN.";
+ } else {
+ result.message = "Expected " + j$.pp(actual) + " to be NaN.";
+ }
+
+ return result;
+ }
+ };
+ }
+
+ return toBeNaN;
+};
+
+getJasmineRequireObj().toBeNull = function() {
+
+ function toBeNull() {
+ return {
+ compare: function(actual) {
+ return {
+ pass: actual === null
+ };
+ }
+ };
+ }
+
+ return toBeNull;
+};
+
+getJasmineRequireObj().toBeTruthy = function() {
+
+ function toBeTruthy() {
+ return {
+ compare: function(actual) {
+ return {
+ pass: !!actual
+ };
+ }
+ };
+ }
+
+ return toBeTruthy;
+};
+
+getJasmineRequireObj().toBeUndefined = function() {
+
+ function toBeUndefined() {
+ return {
+ compare: function(actual) {
+ return {
+ pass: void 0 === actual
+ };
+ }
+ };
+ }
+
+ return toBeUndefined;
+};
+
+getJasmineRequireObj().toContain = function() {
+ function toContain(util, customEqualityTesters) {
+ customEqualityTesters = customEqualityTesters || [];
+
+ return {
+ compare: function(actual, expected) {
+
+ return {
+ pass: util.contains(actual, expected, customEqualityTesters)
+ };
+ }
+ };
+ }
+
+ return toContain;
+};
+
+getJasmineRequireObj().toEqual = function() {
+
+ function toEqual(util, customEqualityTesters) {
+ customEqualityTesters = customEqualityTesters || [];
+
+ return {
+ compare: function(actual, expected) {
+ var result = {
+ pass: false
+ };
+
+ result.pass = util.equals(actual, expected, customEqualityTesters);
+
+ return result;
+ }
+ };
+ }
+
+ return toEqual;
+};
+
+getJasmineRequireObj().toHaveBeenCalled = function(j$) {
+
+ function toHaveBeenCalled() {
+ return {
+ compare: function(actual) {
+ var result = {};
+
+ if (!j$.isSpy(actual)) {
+ throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.');
+ }
+
+ if (arguments.length > 1) {
+ throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
+ }
+
+ result.pass = actual.calls.any();
+
+ result.message = result.pass ?
+ "Expected spy " + actual.and.identity() + " not to have been called." :
+ "Expected spy " + actual.and.identity() + " to have been called.";
+
+ return result;
+ }
+ };
+ }
+
+ return toHaveBeenCalled;
+};
+
+getJasmineRequireObj().toHaveBeenCalledWith = function(j$) {
+
+ function toHaveBeenCalledWith(util) {
+ return {
+ compare: function() {
+ var args = Array.prototype.slice.call(arguments, 0),
+ actual = args[0],
+ expectedArgs = args.slice(1),
+ result = { pass: false };
+
+ if (!j$.isSpy(actual)) {
+ throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.');
+ }
+
+ if (!actual.calls.any()) {
+ result.message = "Expected spy " + actual.and.identity() + " to have been called with " + j$.pp(expectedArgs) + " but it was never called.";
+ return result;
+ }
+
+ if (util.contains(actual.calls.allArgs(), expectedArgs)) {
+ result.pass = true;
+ result.message = "Expected spy " + actual.and.identity() + " not to have been called with " + j$.pp(expectedArgs) + " but it was.";
+ } else {
+ result.message = "Expected spy " + actual.and.identity() + " to have been called with " + j$.pp(expectedArgs) + " but actual calls were " + j$.pp(actual.calls.allArgs()).replace(/^\[ | \]$/g, '') + ".";
+ }
+
+ return result;
+ }
+ };
+ }
+
+ return toHaveBeenCalledWith;
+};
+
+getJasmineRequireObj().toMatch = function() {
+
+ function toMatch() {
+ return {
+ compare: function(actual, expected) {
+ var regexp = new RegExp(expected);
+
+ return {
+ pass: regexp.test(actual)
+ };
+ }
+ };
+ }
+
+ return toMatch;
+};
+
+getJasmineRequireObj().toThrow = function(j$) {
+
+ function toThrow(util) {
+ return {
+ compare: function(actual, expected) {
+ var result = { pass: false },
+ threw = false,
+ thrown;
+
+ if (typeof actual != "function") {
+ throw new Error("Actual is not a Function");
+ }
+
+ try {
+ actual();
+ } catch (e) {
+ threw = true;
+ thrown = e;
+ }
+
+ if (!threw) {
+ result.message = "Expected function to throw an exception.";
+ return result;
+ }
+
+ if (arguments.length == 1) {
+ result.pass = true;
+ result.message = "Expected function not to throw, but it threw " + j$.pp(thrown) + ".";
+
+ return result;
+ }
+
+ if (util.equals(thrown, expected)) {
+ result.pass = true;
+ result.message = "Expected function not to throw " + j$.pp(expected) + ".";
+ } else {
+ result.message = "Expected function to throw " + j$.pp(expected) + ", but it threw " + j$.pp(thrown) + ".";
+ }
+
+ return result;
+ }
+ };
+ }
+
+ return toThrow;
+};
+
+getJasmineRequireObj().toThrowError = function(j$) {
+ function toThrowError (util) {
+ return {
+ compare: function(actual) {
+ var threw = false,
+ thrown,
+ errorType,
+ message,
+ regexp,
+ name,
+ constructorName;
+
+ if (typeof actual != "function") {
+ throw new Error("Actual is not a Function");
+ }
+
+ extractExpectedParams.apply(null, arguments);
+
+ try {
+ actual();
+ } catch (e) {
+ threw = true;
+ thrown = e;
+ }
+
+ if (!threw) {
+ return fail("Expected function to throw an Error.");
+ }
+
+ if (!(thrown instanceof Error)) {
+ return fail("Expected function to throw an Error, but it threw " + thrown + ".");
+ }
+
+ if (arguments.length == 1) {
+ return pass("Expected function not to throw an Error, but it threw " + fnNameFor(thrown) + ".");
+ }
+
+ if (errorType) {
+ name = fnNameFor(errorType);
+ constructorName = fnNameFor(thrown.constructor);
+ }
+
+ if (errorType && message) {
+ if (thrown.constructor == errorType && util.equals(thrown.message, message)) {
+ return pass("Expected function not to throw " + name + " with message \"" + message + "\".");
+ } else {
+ return fail("Expected function to throw " + name + " with message \"" + message +
+ "\", but it threw " + constructorName + " with message \"" + thrown.message + "\".");
+ }
+ }
+
+ if (errorType && regexp) {
+ if (thrown.constructor == errorType && regexp.test(thrown.message)) {
+ return pass("Expected function not to throw " + name + " with message matching " + regexp + ".");
+ } else {
+ return fail("Expected function to throw " + name + " with message matching " + regexp +
+ ", but it threw " + constructorName + " with message \"" + thrown.message + "\".");
+ }
+ }
+
+ if (errorType) {
+ if (thrown.constructor == errorType) {
+ return pass("Expected function not to throw " + name + ".");
+ } else {
+ return fail("Expected function to throw " + name + ", but it threw " + constructorName + ".");
+ }
+ }
+
+ if (message) {
+ if (thrown.message == message) {
+ return pass("Expected function not to throw an exception with message " + j$.pp(message) + ".");
+ } else {
+ return fail("Expected function to throw an exception with message " + j$.pp(message) +
+ ", but it threw an exception with message " + j$.pp(thrown.message) + ".");
+ }
+ }
+
+ if (regexp) {
+ if (regexp.test(thrown.message)) {
+ return pass("Expected function not to throw an exception with a message matching " + j$.pp(regexp) + ".");
+ } else {
+ return fail("Expected function to throw an exception with a message matching " + j$.pp(regexp) +
+ ", but it threw an exception with message " + j$.pp(thrown.message) + ".");
+ }
+ }
+
+ function fnNameFor(func) {
+ return func.name || func.toString().match(/^\s*function\s*(\w*)\s*\(/)[1];
+ }
+
+ function pass(notMessage) {
+ return {
+ pass: true,
+ message: notMessage
+ };
+ }
+
+ function fail(message) {
+ return {
+ pass: false,
+ message: message
+ };
+ }
+
+ function extractExpectedParams() {
+ if (arguments.length == 1) {
+ return;
+ }
+
+ if (arguments.length == 2) {
+ var expected = arguments[1];
+
+ if (expected instanceof RegExp) {
+ regexp = expected;
+ } else if (typeof expected == "string") {
+ message = expected;
+ } else if (checkForAnErrorType(expected)) {
+ errorType = expected;
+ }
+
+ if (!(errorType || message || regexp)) {
+ throw new Error("Expected is not an Error, string, or RegExp.");
+ }
+ } else {
+ if (checkForAnErrorType(arguments[1])) {
+ errorType = arguments[1];
+ } else {
+ throw new Error("Expected error type is not an Error.");
+ }
+
+ if (arguments[2] instanceof RegExp) {
+ regexp = arguments[2];
+ } else if (typeof arguments[2] == "string") {
+ message = arguments[2];
+ } else {
+ throw new Error("Expected error message is not a string or RegExp.");
+ }
+ }
+ }
+
+ function checkForAnErrorType(type) {
+ if (typeof type !== "function") {
+ return false;
+ }
+
+ var Surrogate = function() {};
+ Surrogate.prototype = type.prototype;
+ return (new Surrogate()) instanceof Error;
+ }
+ }
+ };
+ }
+
+ return toThrowError;
+};
+
+getJasmineRequireObj().version = function() {
+ return "2.0.0";
+};
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/jasmine_favicon.png
===================================================================
(Binary files differ)
Property changes on: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/jasmine_favicon.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/sinon.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/sinon.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/sinon.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,4299 @@
+/**
+ * Sinon.JS 1.7.1, 2013/05/07
+ *
+ * @author Christian Johansen (christian(a)cjohansen.no)
+ * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS
+ *
+ * (The BSD License)
+ *
+ * Copyright (c) 2010-2013, Christian Johansen, christian(a)cjohansen.no
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Christian Johansen nor the names of his contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+this.sinon = (function () {
+ var buster = (function (setTimeout, B) {
+ var isNode = typeof require == "function" && typeof module == "object";
+ var div = typeof document != "undefined" && document.createElement("div");
+ var F = function () {};
+
+ var buster = {
+ bind: function bind(obj, methOrProp) {
+ var method = typeof methOrProp == "string" ? obj[methOrProp] : methOrProp;
+ var args = Array.prototype.slice.call(arguments, 2);
+ return function () {
+ var allArgs = args.concat(Array.prototype.slice.call(arguments));
+ return method.apply(obj, allArgs);
+ };
+ },
+
+ partial: function partial(fn) {
+ var args = [].slice.call(arguments, 1);
+ return function () {
+ return fn.apply(this, args.concat([].slice.call(arguments)));
+ };
+ },
+
+ create: function create(object) {
+ F.prototype = object;
+ return new F();
+ },
+
+ extend: function extend(target) {
+ if (!target) { return; }
+ for (var i = 1, l = arguments.length, prop; i < l; ++i) {
+ for (prop in arguments[i]) {
+ target[prop] = arguments[i][prop];
+ }
+ }
+ return target;
+ },
+
+ nextTick: function nextTick(callback) {
+ if (typeof process != "undefined" && process.nextTick) {
+ return process.nextTick(callback);
+ }
+ setTimeout(callback, 0);
+ },
+
+ functionName: function functionName(func) {
+ if (!func) return "";
+ if (func.displayName) return func.displayName;
+ if (func.name) return func.name;
+ var matches = func.toString().match(/function\s+([^\(]+)/m);
+ return matches && matches[1] || "";
+ },
+
+ isNode: function isNode(obj) {
+ if (!div) return false;
+ try {
+ obj.appendChild(div);
+ obj.removeChild(div);
+ } catch (e) {
+ return false;
+ }
+ return true;
+ },
+
+ isElement: function isElement(obj) {
+ return obj && obj.nodeType === 1 && buster.isNode(obj);
+ },
+
+ isArray: function isArray(arr) {
+ return Object.prototype.toString.call(arr) == "[object Array]";
+ },
+
+ flatten: function flatten(arr) {
+ var result = [], arr = arr || [];
+ for (var i = 0, l = arr.length; i < l; ++i) {
+ result = result.concat(buster.isArray(arr[i]) ? flatten(arr[i]) : arr[i]);
+ }
+ return result;
+ },
+
+ each: function each(arr, callback) {
+ for (var i = 0, l = arr.length; i < l; ++i) {
+ callback(arr[i]);
+ }
+ },
+
+ map: function map(arr, callback) {
+ var results = [];
+ for (var i = 0, l = arr.length; i < l; ++i) {
+ results.push(callback(arr[i]));
+ }
+ return results;
+ },
+
+ parallel: function parallel(fns, callback) {
+ function cb(err, res) {
+ if (typeof callback == "function") {
+ callback(err, res);
+ callback = null;
+ }
+ }
+ if (fns.length == 0) { return cb(null, []); }
+ var remaining = fns.length, results = [];
+ function makeDone(num) {
+ return function done(err, result) {
+ if (err) { return cb(err); }
+ results[num] = result;
+ if (--remaining == 0) { cb(null, results); }
+ };
+ }
+ for (var i = 0, l = fns.length; i < l; ++i) {
+ fns[i](makeDone(i));
+ }
+ },
+
+ series: function series(fns, callback) {
+ function cb(err, res) {
+ if (typeof callback == "function") {
+ callback(err, res);
+ }
+ }
+ var remaining = fns.slice();
+ var results = [];
+ function callNext() {
+ if (remaining.length == 0) return cb(null, results);
+ var promise = remaining.shift()(next);
+ if (promise && typeof promise.then == "function") {
+ promise.then(buster.partial(next, null), next);
+ }
+ }
+ function next(err, result) {
+ if (err) return cb(err);
+ results.push(result);
+ callNext();
+ }
+ callNext();
+ },
+
+ countdown: function countdown(num, done) {
+ return function () {
+ if (--num == 0) done();
+ };
+ }
+ };
+
+ if (typeof process === "object" &&
+ typeof require === "function" && typeof module === "object") {
+ var crypto = require("crypto");
+ var path = require("path");
+
+ buster.tmpFile = function (fileName) {
+ var hashed = crypto.createHash("sha1");
+ hashed.update(fileName);
+ var tmpfileName = hashed.digest("hex");
+
+ if (process.platform == "win32") {
+ return path.join(process.env["TEMP"], tmpfileName);
+ } else {
+ return path.join("/tmp", tmpfileName);
+ }
+ };
+ }
+
+ if (Array.prototype.some) {
+ buster.some = function (arr, fn, thisp) {
+ return arr.some(fn, thisp);
+ };
+ } else {
+ // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/…
+ buster.some = function (arr, fun, thisp) {
+ if (arr == null) { throw new TypeError(); }
+ arr = Object(arr);
+ var len = arr.length >>> 0;
+ if (typeof fun !== "function") { throw new TypeError(); }
+
+ for (var i = 0; i < len; i++) {
+ if (arr.hasOwnProperty(i) && fun.call(thisp, arr[i], i, arr)) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+ }
+
+ if (Array.prototype.filter) {
+ buster.filter = function (arr, fn, thisp) {
+ return arr.filter(fn, thisp);
+ };
+ } else {
+ // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/…
+ buster.filter = function (fn, thisp) {
+ if (this == null) { throw new TypeError(); }
+
+ var t = Object(this);
+ var len = t.length >>> 0;
+ if (typeof fn != "function") { throw new TypeError(); }
+
+ var res = [];
+ for (var i = 0; i < len; i++) {
+ if (i in t) {
+ var val = t[i]; // in case fun mutates this
+ if (fn.call(thisp, val, i, t)) { res.push(val); }
+ }
+ }
+
+ return res;
+ };
+ }
+
+ if (isNode) {
+ module.exports = buster;
+ buster.eventEmitter = require("./buster-event-emitter");
+ Object.defineProperty(buster, "defineVersionGetter", {
+ get: function () {
+ return require("./define-version-getter");
+ }
+ });
+ }
+
+ return buster.extend(B || {}, buster);
+ }(setTimeout, buster));
+ if (typeof buster === "undefined") {
+ var buster = {};
+ }
+
+ if (typeof module === "object" && typeof require === "function") {
+ buster = require("buster-core");
+ }
+
+ buster.format = buster.format || {};
+ buster.format.excludeConstructors = ["Object", /^.$/];
+ buster.format.quoteStrings = true;
+
+ buster.format.ascii = (function () {
+
+ var hasOwn = Object.prototype.hasOwnProperty;
+
+ var specialObjects = [];
+ if (typeof global != "undefined") {
+ specialObjects.push({ obj: global, value: "[object global]" });
+ }
+ if (typeof document != "undefined") {
+ specialObjects.push({ obj: document, value: "[object HTMLDocument]" });
+ }
+ if (typeof window != "undefined") {
+ specialObjects.push({ obj: window, value: "[object Window]" });
+ }
+
+ function keys(object) {
+ var k = Object.keys && Object.keys(object) || [];
+
+ if (k.length == 0) {
+ for (var prop in object) {
+ if (hasOwn.call(object, prop)) {
+ k.push(prop);
+ }
+ }
+ }
+
+ return k.sort();
+ }
+
+ function isCircular(object, objects) {
+ if (typeof object != "object") {
+ return false;
+ }
+
+ for (var i = 0, l = objects.length; i < l; ++i) {
+ if (objects[i] === object) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ function ascii(object, processed, indent) {
+ if (typeof object == "string") {
+ var quote = typeof this.quoteStrings != "boolean" || this.quoteStrings;
+ return processed || quote ? '"' + object + '"' : object;
+ }
+
+ if (typeof object == "function" && !(object instanceof RegExp)) {
+ return ascii.func(object);
+ }
+
+ processed = processed || [];
+
+ if (isCircular(object, processed)) {
+ return "[Circular]";
+ }
+
+ if (Object.prototype.toString.call(object) == "[object Array]") {
+ return ascii.array.call(this, object, processed);
+ }
+
+ if (!object) {
+ return "" + object;
+ }
+
+ if (buster.isElement(object)) {
+ return ascii.element(object);
+ }
+
+ if (typeof object.toString == "function" &&
+ object.toString !== Object.prototype.toString) {
+ return object.toString();
+ }
+
+ for (var i = 0, l = specialObjects.length; i < l; i++) {
+ if (object === specialObjects[i].obj) {
+ return specialObjects[i].value;
+ }
+ }
+
+ return ascii.object.call(this, object, processed, indent);
+ }
+
+ ascii.func = function (func) {
+ return "function " + buster.functionName(func) + "() {}";
+ };
+
+ ascii.array = function (array, processed) {
+ processed = processed || [];
+ processed.push(array);
+ var pieces = [];
+
+ for (var i = 0, l = array.length; i < l; ++i) {
+ pieces.push(ascii.call(this, array[i], processed));
+ }
+
+ return "[" + pieces.join(", ") + "]";
+ };
+
+ ascii.object = function (object, processed, indent) {
+ processed = processed || [];
+ processed.push(object);
+ indent = indent || 0;
+ var pieces = [], properties = keys(object), prop, str, obj;
+ var is = "";
+ var length = 3;
+
+ for (var i = 0, l = indent; i < l; ++i) {
+ is += " ";
+ }
+
+ for (i = 0, l = properties.length; i < l; ++i) {
+ prop = properties[i];
+ obj = object[prop];
+
+ if (isCircular(obj, processed)) {
+ str = "[Circular]";
+ } else {
+ str = ascii.call(this, obj, processed, indent + 2);
+ }
+
+ str = (/\s/.test(prop) ? '"' + prop + '"' : prop) + ": " + str;
+ length += str.length;
+ pieces.push(str);
+ }
+
+ var cons = ascii.constructorName.call(this, object);
+ var prefix = cons ? "[" + cons + "] " : ""
+
+ return (length + indent) > 80 ?
+ prefix + "{\n " + is + pieces.join(",\n " + is) + "\n" + is + "}" :
+ prefix + "{ " + pieces.join(", ") + " }";
+ };
+
+ ascii.element = function (element) {
+ var tagName = element.tagName.toLowerCase();
+ var attrs = element.attributes, attribute, pairs = [], attrName;
+
+ for (var i = 0, l = attrs.length; i < l; ++i) {
+ attribute = attrs.item(i);
+ attrName = attribute.nodeName.toLowerCase().replace("html:", "");
+
+ if (attrName == "contenteditable" && attribute.nodeValue == "inherit") {
+ continue;
+ }
+
+ if (!!attribute.nodeValue) {
+ pairs.push(attrName + "=\"" + attribute.nodeValue + "\"");
+ }
+ }
+
+ var formatted = "<" + tagName + (pairs.length > 0 ? " " : "");
+ var content = element.innerHTML;
+
+ if (content.length > 20) {
+ content = content.substr(0, 20) + "[...]";
+ }
+
+ var res = formatted + pairs.join(" ") + ">" + content + "</" + tagName + ">";
+
+ return res.replace(/ contentEditable="inherit"/, "");
+ };
+
+ ascii.constructorName = function (object) {
+ var name = buster.functionName(object && object.constructor);
+ var excludes = this.excludeConstructors || buster.format.excludeConstructors || [];
+
+ for (var i = 0, l = excludes.length; i < l; ++i) {
+ if (typeof excludes[i] == "string" && excludes[i] == name) {
+ return "";
+ } else if (excludes[i].test && excludes[i].test(name)) {
+ return "";
+ }
+ }
+
+ return name;
+ };
+
+ return ascii;
+ }());
+
+ if (typeof module != "undefined") {
+ module.exports = buster.format;
+ }
+ /*jslint eqeqeq: false, onevar: false, forin: true, nomen: false, regexp: false, plusplus: false*/
+ /*global module, require, __dirname, document*/
+ /**
+ * Sinon core utilities. For internal use only.
+ *
+ * @author Christian Johansen (christian(a)cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+ var sinon = (function (buster) {
+ var div = typeof document != "undefined" && document.createElement("div");
+ var hasOwn = Object.prototype.hasOwnProperty;
+
+ function isDOMNode(obj) {
+ var success = false;
+
+ try {
+ obj.appendChild(div);
+ success = div.parentNode == obj;
+ } catch (e) {
+ return false;
+ } finally {
+ try {
+ obj.removeChild(div);
+ } catch (e) {
+ // Remove failed, not much we can do about that
+ }
+ }
+
+ return success;
+ }
+
+ function isElement(obj) {
+ return div && obj && obj.nodeType === 1 && isDOMNode(obj);
+ }
+
+ function isFunction(obj) {
+ return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply);
+ }
+
+ function mirrorProperties(target, source) {
+ for (var prop in source) {
+ if (!hasOwn.call(target, prop)) {
+ target[prop] = source[prop];
+ }
+ }
+ }
+
+ function isRestorable (obj) {
+ return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon;
+ }
+
+ var sinon = {
+ wrapMethod: function wrapMethod(object, property, method) {
+ if (!object) {
+ throw new TypeError("Should wrap property of object");
+ }
+
+ if (typeof method != "function") {
+ throw new TypeError("Method wrapper should be function");
+ }
+
+ var wrappedMethod = object[property];
+
+ if (!isFunction(wrappedMethod)) {
+ throw new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " +
+ property + " as function");
+ }
+
+ if (wrappedMethod.restore && wrappedMethod.restore.sinon) {
+ throw new TypeError("Attempted to wrap " + property + " which is already wrapped");
+ }
+
+ if (wrappedMethod.calledBefore) {
+ var verb = !!wrappedMethod.returns ? "stubbed" : "spied on";
+ throw new TypeError("Attempted to wrap " + property + " which is already " + verb);
+ }
+
+ // IE 8 does not support hasOwnProperty on the window object.
+ var owned = hasOwn.call(object, property);
+ object[property] = method;
+ method.displayName = property;
+
+ method.restore = function () {
+ // For prototype properties try to reset by delete first.
+ // If this fails (ex: localStorage on mobile safari) then force a reset
+ // via direct assignment.
+ if (!owned) {
+ delete object[property];
+ }
+ if (object[property] === method) {
+ object[property] = wrappedMethod;
+ }
+ };
+
+ method.restore.sinon = true;
+ mirrorProperties(method, wrappedMethod);
+
+ return method;
+ },
+
+ extend: function extend(target) {
+ for (var i = 1, l = arguments.length; i < l; i += 1) {
+ for (var prop in arguments[i]) {
+ if (arguments[i].hasOwnProperty(prop)) {
+ target[prop] = arguments[i][prop];
+ }
+
+ // DONT ENUM bug, only care about toString
+ if (arguments[i].hasOwnProperty("toString") &&
+ arguments[i].toString != target.toString) {
+ target.toString = arguments[i].toString;
+ }
+ }
+ }
+
+ return target;
+ },
+
+ create: function create(proto) {
+ var F = function () {};
+ F.prototype = proto;
+ return new F();
+ },
+
+ deepEqual: function deepEqual(a, b) {
+ if (sinon.match && sinon.match.isMatcher(a)) {
+ return a.test(b);
+ }
+ if (typeof a != "object" || typeof b != "object") {
+ return a === b;
+ }
+
+ if (isElement(a) || isElement(b)) {
+ return a === b;
+ }
+
+ if (a === b) {
+ return true;
+ }
+
+ if ((a === null && b !== null) || (a !== null && b === null)) {
+ return false;
+ }
+
+ var aString = Object.prototype.toString.call(a);
+ if (aString != Object.prototype.toString.call(b)) {
+ return false;
+ }
+
+ if (aString == "[object Array]") {
+ if (a.length !== b.length) {
+ return false;
+ }
+
+ for (var i = 0, l = a.length; i < l; i += 1) {
+ if (!deepEqual(a[i], b[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ var prop, aLength = 0, bLength = 0;
+
+ for (prop in a) {
+ aLength += 1;
+
+ if (!deepEqual(a[prop], b[prop])) {
+ return false;
+ }
+ }
+
+ for (prop in b) {
+ bLength += 1;
+ }
+
+ return aLength == bLength;
+ },
+
+ functionName: function functionName(func) {
+ var name = func.displayName || func.name;
+
+ // Use function decomposition as a last resort to get function
+ // name. Does not rely on function decomposition to work - if it
+ // doesn't debugging will be slightly less informative
+ // (i.e. toString will say 'spy' rather than 'myFunc').
+ if (!name) {
+ var matches = func.toString().match(/function ([^\s\(]+)/);
+ name = matches && matches[1];
+ }
+
+ return name;
+ },
+
+ functionToString: function toString() {
+ if (this.getCall && this.callCount) {
+ var thisValue, prop, i = this.callCount;
+
+ while (i--) {
+ thisValue = this.getCall(i).thisValue;
+
+ for (prop in thisValue) {
+ if (thisValue[prop] === this) {
+ return prop;
+ }
+ }
+ }
+ }
+
+ return this.displayName || "sinon fake";
+ },
+
+ getConfig: function (custom) {
+ var config = {};
+ custom = custom || {};
+ var defaults = sinon.defaultConfig;
+
+ for (var prop in defaults) {
+ if (defaults.hasOwnProperty(prop)) {
+ config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop];
+ }
+ }
+
+ return config;
+ },
+
+ format: function (val) {
+ return "" + val;
+ },
+
+ defaultConfig: {
+ injectIntoThis: true,
+ injectInto: null,
+ properties: ["spy", "stub", "mock", "clock", "server", "requests"],
+ useFakeTimers: true,
+ useFakeServer: true
+ },
+
+ timesInWords: function timesInWords(count) {
+ return count == 1 && "once" ||
+ count == 2 && "twice" ||
+ count == 3 && "thrice" ||
+ (count || 0) + " times";
+ },
+
+ calledInOrder: function (spies) {
+ for (var i = 1, l = spies.length; i < l; i++) {
+ if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ orderByFirstCall: function (spies) {
+ return spies.sort(function (a, b) {
+ // uuid, won't ever be equal
+ var aCall = a.getCall(0);
+ var bCall = b.getCall(0);
+ var aId = aCall && aCall.callId || -1;
+ var bId = bCall && bCall.callId || -1;
+
+ return aId < bId ? -1 : 1;
+ });
+ },
+
+ log: function () {},
+
+ logError: function (label, err) {
+ var msg = label + " threw exception: "
+ sinon.log(msg + "[" + err.name + "] " + err.message);
+ if (err.stack) { sinon.log(err.stack); }
+
+ setTimeout(function () {
+ err.message = msg + err.message;
+ throw err;
+ }, 0);
+ },
+
+ typeOf: function (value) {
+ if (value === null) {
+ return "null";
+ }
+ else if (value === undefined) {
+ return "undefined";
+ }
+ var string = Object.prototype.toString.call(value);
+ return string.substring(8, string.length - 1).toLowerCase();
+ },
+
+ createStubInstance: function (constructor) {
+ if (typeof constructor !== "function") {
+ throw new TypeError("The constructor should be a function.");
+ }
+ return sinon.stub(sinon.create(constructor.prototype));
+ },
+
+ restore: function (object) {
+ if (object !== null && typeof object === "object") {
+ for (var prop in object) {
+ if (isRestorable(object[prop])) {
+ object[prop].restore();
+ }
+ }
+ }
+ else if (isRestorable(object)) {
+ object.restore();
+ }
+ }
+ };
+
+ var isNode = typeof module == "object" && typeof require == "function";
+
+ if (isNode) {
+ try {
+ buster = { format: require("buster-format") };
+ } catch (e) {}
+ module.exports = sinon;
+ module.exports.spy = require("./sinon/spy");
+ module.exports.spyCall = require("./sinon/call");
+ module.exports.stub = require("./sinon/stub");
+ module.exports.mock = require("./sinon/mock");
+ module.exports.collection = require("./sinon/collection");
+ module.exports.assert = require("./sinon/assert");
+ module.exports.sandbox = require("./sinon/sandbox");
+ module.exports.test = require("./sinon/test");
+ module.exports.testCase = require("./sinon/test_case");
+ module.exports.assert = require("./sinon/assert");
+ module.exports.match = require("./sinon/match");
+ }
+
+ if (buster) {
+ var formatter = sinon.create(buster.format);
+ formatter.quoteStrings = false;
+ sinon.format = function () {
+ return formatter.ascii.apply(formatter, arguments);
+ };
+ } else if (isNode) {
+ try {
+ var util = require("util");
+ sinon.format = function (value) {
+ return typeof value == "object" && value.toString === Object.prototype.toString ? util.inspect(value) : value;
+ };
+ } catch (e) {
+ /* Node, but no util module - would be very old, but better safe than
+ sorry */
+ }
+ }
+
+ return sinon;
+ }(typeof buster == "object" && buster));
+
+ /* @depend ../sinon.js */
+ /*jslint eqeqeq: false, onevar: false, plusplus: false*/
+ /*global module, require, sinon*/
+ /**
+ * Match functions
+ *
+ * @author Maximilian Antoni (mail(a)maxantoni.de)
+ * @license BSD
+ *
+ * Copyright (c) 2012 Maximilian Antoni
+ */
+
+ (function (sinon) {
+ var commonJSModule = typeof module == "object" && typeof require == "function";
+
+ if (!sinon && commonJSModule) {
+ sinon = require("../sinon");
+ }
+
+ if (!sinon) {
+ return;
+ }
+
+ function assertType(value, type, name) {
+ var actual = sinon.typeOf(value);
+ if (actual !== type) {
+ throw new TypeError("Expected type of " + name + " to be " +
+ type + ", but was " + actual);
+ }
+ }
+
+ var matcher = {
+ toString: function () {
+ return this.message;
+ }
+ };
+
+ function isMatcher(object) {
+ return matcher.isPrototypeOf(object);
+ }
+
+ function matchObject(expectation, actual) {
+ if (actual === null || actual === undefined) {
+ return false;
+ }
+ for (var key in expectation) {
+ if (expectation.hasOwnProperty(key)) {
+ var exp = expectation[key];
+ var act = actual[key];
+ if (match.isMatcher(exp)) {
+ if (!exp.test(act)) {
+ return false;
+ }
+ } else if (sinon.typeOf(exp) === "object") {
+ if (!matchObject(exp, act)) {
+ return false;
+ }
+ } else if (!sinon.deepEqual(exp, act)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ matcher.or = function (m2) {
+ if (!isMatcher(m2)) {
+ throw new TypeError("Matcher expected");
+ }
+ var m1 = this;
+ var or = sinon.create(matcher);
+ or.test = function (actual) {
+ return m1.test(actual) || m2.test(actual);
+ };
+ or.message = m1.message + ".or(" + m2.message + ")";
+ return or;
+ };
+
+ matcher.and = function (m2) {
+ if (!isMatcher(m2)) {
+ throw new TypeError("Matcher expected");
+ }
+ var m1 = this;
+ var and = sinon.create(matcher);
+ and.test = function (actual) {
+ return m1.test(actual) && m2.test(actual);
+ };
+ and.message = m1.message + ".and(" + m2.message + ")";
+ return and;
+ };
+
+ var match = function (expectation, message) {
+ var m = sinon.create(matcher);
+ var type = sinon.typeOf(expectation);
+ switch (type) {
+ case "object":
+ if (typeof expectation.test === "function") {
+ m.test = function (actual) {
+ return expectation.test(actual) === true;
+ };
+ m.message = "match(" + sinon.functionName(expectation.test) + ")";
+ return m;
+ }
+ var str = [];
+ for (var key in expectation) {
+ if (expectation.hasOwnProperty(key)) {
+ str.push(key + ": " + expectation[key]);
+ }
+ }
+ m.test = function (actual) {
+ return matchObject(expectation, actual);
+ };
+ m.message = "match(" + str.join(", ") + ")";
+ break;
+ case "number":
+ m.test = function (actual) {
+ return expectation == actual;
+ };
+ break;
+ case "string":
+ m.test = function (actual) {
+ if (typeof actual !== "string") {
+ return false;
+ }
+ return actual.indexOf(expectation) !== -1;
+ };
+ m.message = "match(\"" + expectation + "\")";
+ break;
+ case "regexp":
+ m.test = function (actual) {
+ if (typeof actual !== "string") {
+ return false;
+ }
+ return expectation.test(actual);
+ };
+ break;
+ case "function":
+ m.test = expectation;
+ if (message) {
+ m.message = message;
+ } else {
+ m.message = "match(" + sinon.functionName(expectation) + ")";
+ }
+ break;
+ default:
+ m.test = function (actual) {
+ return sinon.deepEqual(expectation, actual);
+ };
+ }
+ if (!m.message) {
+ m.message = "match(" + expectation + ")";
+ }
+ return m;
+ };
+
+ match.isMatcher = isMatcher;
+
+ match.any = match(function () {
+ return true;
+ }, "any");
+
+ match.defined = match(function (actual) {
+ return actual !== null && actual !== undefined;
+ }, "defined");
+
+ match.truthy = match(function (actual) {
+ return !!actual;
+ }, "truthy");
+
+ match.falsy = match(function (actual) {
+ return !actual;
+ }, "falsy");
+
+ match.same = function (expectation) {
+ return match(function (actual) {
+ return expectation === actual;
+ }, "same(" + expectation + ")");
+ };
+
+ match.typeOf = function (type) {
+ assertType(type, "string", "type");
+ return match(function (actual) {
+ return sinon.typeOf(actual) === type;
+ }, "typeOf(\"" + type + "\")");
+ };
+
+ match.instanceOf = function (type) {
+ assertType(type, "function", "type");
+ return match(function (actual) {
+ return actual instanceof type;
+ }, "instanceOf(" + sinon.functionName(type) + ")");
+ };
+
+ function createPropertyMatcher(propertyTest, messagePrefix) {
+ return function (property, value) {
+ assertType(property, "string", "property");
+ var onlyProperty = arguments.length === 1;
+ var message = messagePrefix + "(\"" + property + "\"";
+ if (!onlyProperty) {
+ message += ", " + value;
+ }
+ message += ")";
+ return match(function (actual) {
+ if (actual === undefined || actual === null ||
+ !propertyTest(actual, property)) {
+ return false;
+ }
+ return onlyProperty || sinon.deepEqual(value, actual[property]);
+ }, message);
+ };
+ }
+
+ match.has = createPropertyMatcher(function (actual, property) {
+ if (typeof actual === "object") {
+ return property in actual;
+ }
+ return actual[property] !== undefined;
+ }, "has");
+
+ match.hasOwn = createPropertyMatcher(function (actual, property) {
+ return actual.hasOwnProperty(property);
+ }, "hasOwn");
+
+ match.bool = match.typeOf("boolean");
+ match.number = match.typeOf("number");
+ match.string = match.typeOf("string");
+ match.object = match.typeOf("object");
+ match.func = match.typeOf("function");
+ match.array = match.typeOf("array");
+ match.regexp = match.typeOf("regexp");
+ match.date = match.typeOf("date");
+
+ if (commonJSModule) {
+ module.exports = match;
+ } else {
+ sinon.match = match;
+ }
+ }(typeof sinon == "object" && sinon || null));
+
+ /**
+ * @depend ../sinon.js
+ * @depend match.js
+ */
+ /*jslint eqeqeq: false, onevar: false, plusplus: false*/
+ /*global module, require, sinon*/
+ /**
+ * Spy calls
+ *
+ * @author Christian Johansen (christian(a)cjohansen.no)
+ * @author Maximilian Antoni (mail(a)maxantoni.de)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ * Copyright (c) 2013 Maximilian Antoni
+ */
+
+ (function (sinon) {
+ var commonJSModule = typeof module == "object" && typeof require == "function";
+ if (!sinon && commonJSModule) {
+ sinon = require("../sinon");
+ }
+
+ if (!sinon) {
+ return;
+ }
+
+ function throwYieldError(proxy, text, args) {
+ var msg = sinon.functionName(proxy) + text;
+ if (args.length) {
+ msg += " Received [" + slice.call(args).join(", ") + "]";
+ }
+ throw new Error(msg);
+ }
+
+ var slice = Array.prototype.slice;
+
+ var callProto = {
+ calledOn: function calledOn(thisValue) {
+ if (sinon.match && sinon.match.isMatcher(thisValue)) {
+ return thisValue.test(this.thisValue);
+ }
+ return this.thisValue === thisValue;
+ },
+
+ calledWith: function calledWith() {
+ for (var i = 0, l = arguments.length; i < l; i += 1) {
+ if (!sinon.deepEqual(arguments[i], this.args[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ calledWithMatch: function calledWithMatch() {
+ for (var i = 0, l = arguments.length; i < l; i += 1) {
+ var actual = this.args[i];
+ var expectation = arguments[i];
+ if (!sinon.match || !sinon.match(expectation).test(actual)) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ calledWithExactly: function calledWithExactly() {
+ return arguments.length == this.args.length &&
+ this.calledWith.apply(this, arguments);
+ },
+
+ notCalledWith: function notCalledWith() {
+ return !this.calledWith.apply(this, arguments);
+ },
+
+ notCalledWithMatch: function notCalledWithMatch() {
+ return !this.calledWithMatch.apply(this, arguments);
+ },
+
+ returned: function returned(value) {
+ return sinon.deepEqual(value, this.returnValue);
+ },
+
+ threw: function threw(error) {
+ if (typeof error === "undefined" || !this.exception) {
+ return !!this.exception;
+ }
+
+ return this.exception === error || this.exception.name === error;
+ },
+
+ calledWithNew: function calledWithNew(thisValue) {
+ return this.thisValue instanceof this.proxy;
+ },
+
+ calledBefore: function (other) {
+ return this.callId < other.callId;
+ },
+
+ calledAfter: function (other) {
+ return this.callId > other.callId;
+ },
+
+ callArg: function (pos) {
+ this.args[pos]();
+ },
+
+ callArgOn: function (pos, thisValue) {
+ this.args[pos].apply(thisValue);
+ },
+
+ callArgWith: function (pos) {
+ this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1)));
+ },
+
+ callArgOnWith: function (pos, thisValue) {
+ var args = slice.call(arguments, 2);
+ this.args[pos].apply(thisValue, args);
+ },
+
+ "yield": function () {
+ this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0)));
+ },
+
+ yieldOn: function (thisValue) {
+ var args = this.args;
+ for (var i = 0, l = args.length; i < l; ++i) {
+ if (typeof args[i] === "function") {
+ args[i].apply(thisValue, slice.call(arguments, 1));
+ return;
+ }
+ }
+ throwYieldError(this.proxy, " cannot yield since no callback was passed.", args);
+ },
+
+ yieldTo: function (prop) {
+ this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1)));
+ },
+
+ yieldToOn: function (prop, thisValue) {
+ var args = this.args;
+ for (var i = 0, l = args.length; i < l; ++i) {
+ if (args[i] && typeof args[i][prop] === "function") {
+ args[i][prop].apply(thisValue, slice.call(arguments, 2));
+ return;
+ }
+ }
+ throwYieldError(this.proxy, " cannot yield to '" + prop +
+ "' since no callback was passed.", args);
+ },
+
+ toString: function () {
+ var callStr = this.proxy.toString() + "(";
+ var args = [];
+
+ for (var i = 0, l = this.args.length; i < l; ++i) {
+ args.push(sinon.format(this.args[i]));
+ }
+
+ callStr = callStr + args.join(", ") + ")";
+
+ if (typeof this.returnValue != "undefined") {
+ callStr += " => " + sinon.format(this.returnValue);
+ }
+
+ if (this.exception) {
+ callStr += " !" + this.exception.name;
+
+ if (this.exception.message) {
+ callStr += "(" + this.exception.message + ")";
+ }
+ }
+
+ return callStr;
+ }
+ };
+
+ callProto.invokeCallback = callProto.yield;
+
+ function createSpyCall(spy, thisValue, args, returnValue, exception, id) {
+ if (typeof id !== "number") {
+ throw new TypeError("Call id is not a number");
+ }
+ var proxyCall = sinon.create(callProto);
+ proxyCall.proxy = spy;
+ proxyCall.thisValue = thisValue;
+ proxyCall.args = args;
+ proxyCall.returnValue = returnValue;
+ proxyCall.exception = exception;
+ proxyCall.callId = id;
+
+ return proxyCall;
+ };
+ createSpyCall.toString = callProto.toString; // used by mocks
+
+ if (commonJSModule) {
+ module.exports = createSpyCall;
+ } else {
+ sinon.spyCall = createSpyCall;
+ }
+ }(typeof sinon == "object" && sinon || null));
+
+
+ /**
+ * @depend ../sinon.js
+ * @depend call.js
+ */
+ /*jslint eqeqeq: false, onevar: false, plusplus: false*/
+ /*global module, require, sinon*/
+ /**
+ * Spy functions
+ *
+ * @author Christian Johansen (christian(a)cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+ (function (sinon) {
+ var commonJSModule = typeof module == "object" && typeof require == "function";
+ var push = Array.prototype.push;
+ var slice = Array.prototype.slice;
+ var callId = 0;
+
+ if (!sinon && commonJSModule) {
+ sinon = require("../sinon");
+ }
+
+ if (!sinon) {
+ return;
+ }
+
+ function spy(object, property) {
+ if (!property && typeof object == "function") {
+ return spy.create(object);
+ }
+
+ if (!object && !property) {
+ return spy.create(function () { });
+ }
+
+ var method = object[property];
+ return sinon.wrapMethod(object, property, spy.create(method));
+ }
+
+ function matchingFake(fakes, args, strict) {
+ if (!fakes) {
+ return;
+ }
+
+ var alen = args.length;
+
+ for (var i = 0, l = fakes.length; i < l; i++) {
+ if (fakes[i].matches(args, strict)) {
+ return fakes[i];
+ }
+ }
+ }
+
+ function incrementCallCount() {
+ this.called = true;
+ this.callCount += 1;
+ this.notCalled = false;
+ this.calledOnce = this.callCount == 1;
+ this.calledTwice = this.callCount == 2;
+ this.calledThrice = this.callCount == 3;
+ }
+
+ function createCallProperties() {
+ this.firstCall = this.getCall(0);
+ this.secondCall = this.getCall(1);
+ this.thirdCall = this.getCall(2);
+ this.lastCall = this.getCall(this.callCount - 1);
+ }
+
+ var vars = "a,b,c,d,e,f,g,h,i,j,k,l";
+ function createProxy(func) {
+ // Retain the function length:
+ var p;
+ if (func.length) {
+ eval("p = (function proxy(" + vars.substring(0, func.length * 2 - 1) +
+ ") { return p.invoke(func, this, slice.call(arguments)); });");
+ }
+ else {
+ p = function proxy() {
+ return p.invoke(func, this, slice.call(arguments));
+ };
+ }
+ return p;
+ }
+
+ var uuid = 0;
+
+ // Public API
+ var spyApi = {
+ reset: function () {
+ this.called = false;
+ this.notCalled = true;
+ this.calledOnce = false;
+ this.calledTwice = false;
+ this.calledThrice = false;
+ this.callCount = 0;
+ this.firstCall = null;
+ this.secondCall = null;
+ this.thirdCall = null;
+ this.lastCall = null;
+ this.args = [];
+ this.returnValues = [];
+ this.thisValues = [];
+ this.exceptions = [];
+ this.callIds = [];
+ if (this.fakes) {
+ for (var i = 0; i < this.fakes.length; i++) {
+ this.fakes[i].reset();
+ }
+ }
+ },
+
+ create: function create(func) {
+ var name;
+
+ if (typeof func != "function") {
+ func = function () { };
+ } else {
+ name = sinon.functionName(func);
+ }
+
+ var proxy = createProxy(func);
+
+ sinon.extend(proxy, spy);
+ delete proxy.create;
+ sinon.extend(proxy, func);
+
+ proxy.reset();
+ proxy.prototype = func.prototype;
+ proxy.displayName = name || "spy";
+ proxy.toString = sinon.functionToString;
+ proxy._create = sinon.spy.create;
+ proxy.id = "spy#" + uuid++;
+
+ return proxy;
+ },
+
+ invoke: function invoke(func, thisValue, args) {
+ var matching = matchingFake(this.fakes, args);
+ var exception, returnValue;
+
+ incrementCallCount.call(this);
+ push.call(this.thisValues, thisValue);
+ push.call(this.args, args);
+ push.call(this.callIds, callId++);
+
+ try {
+ if (matching) {
+ returnValue = matching.invoke(func, thisValue, args);
+ } else {
+ returnValue = (this.func || func).apply(thisValue, args);
+ }
+ } catch (e) {
+ push.call(this.returnValues, undefined);
+ exception = e;
+ throw e;
+ } finally {
+ push.call(this.exceptions, exception);
+ }
+
+ push.call(this.returnValues, returnValue);
+
+ createCallProperties.call(this);
+
+ return returnValue;
+ },
+
+ getCall: function getCall(i) {
+ if (i < 0 || i >= this.callCount) {
+ return null;
+ }
+
+ return sinon.spyCall(this, this.thisValues[i], this.args[i],
+ this.returnValues[i], this.exceptions[i],
+ this.callIds[i]);
+ },
+
+ calledBefore: function calledBefore(spyFn) {
+ if (!this.called) {
+ return false;
+ }
+
+ if (!spyFn.called) {
+ return true;
+ }
+
+ return this.callIds[0] < spyFn.callIds[spyFn.callIds.length - 1];
+ },
+
+ calledAfter: function calledAfter(spyFn) {
+ if (!this.called || !spyFn.called) {
+ return false;
+ }
+
+ return this.callIds[this.callCount - 1] > spyFn.callIds[spyFn.callCount - 1];
+ },
+
+ withArgs: function () {
+ var args = slice.call(arguments);
+
+ if (this.fakes) {
+ var match = matchingFake(this.fakes, args, true);
+
+ if (match) {
+ return match;
+ }
+ } else {
+ this.fakes = [];
+ }
+
+ var original = this;
+ var fake = this._create();
+ fake.matchingAguments = args;
+ push.call(this.fakes, fake);
+
+ fake.withArgs = function () {
+ return original.withArgs.apply(original, arguments);
+ };
+
+ for (var i = 0; i < this.args.length; i++) {
+ if (fake.matches(this.args[i])) {
+ incrementCallCount.call(fake);
+ push.call(fake.thisValues, this.thisValues[i]);
+ push.call(fake.args, this.args[i]);
+ push.call(fake.returnValues, this.returnValues[i]);
+ push.call(fake.exceptions, this.exceptions[i]);
+ push.call(fake.callIds, this.callIds[i]);
+ }
+ }
+ createCallProperties.call(fake);
+
+ return fake;
+ },
+
+ matches: function (args, strict) {
+ var margs = this.matchingAguments;
+
+ if (margs.length <= args.length &&
+ sinon.deepEqual(margs, args.slice(0, margs.length))) {
+ return !strict || margs.length == args.length;
+ }
+ },
+
+ printf: function (format) {
+ var spy = this;
+ var args = slice.call(arguments, 1);
+ var formatter;
+
+ return (format || "").replace(/%(.)/g, function (match, specifyer) {
+ formatter = spyApi.formatters[specifyer];
+
+ if (typeof formatter == "function") {
+ return formatter.call(null, spy, args);
+ } else if (!isNaN(parseInt(specifyer), 10)) {
+ return sinon.format(args[specifyer - 1]);
+ }
+
+ return "%" + specifyer;
+ });
+ }
+ };
+
+ function delegateToCalls(method, matchAny, actual, notCalled) {
+ spyApi[method] = function () {
+ if (!this.called) {
+ if (notCalled) {
+ return notCalled.apply(this, arguments);
+ }
+ return false;
+ }
+
+ var currentCall;
+ var matches = 0;
+
+ for (var i = 0, l = this.callCount; i < l; i += 1) {
+ currentCall = this.getCall(i);
+
+ if (currentCall[actual || method].apply(currentCall, arguments)) {
+ matches += 1;
+
+ if (matchAny) {
+ return true;
+ }
+ }
+ }
+
+ return matches === this.callCount;
+ };
+ }
+
+ delegateToCalls("calledOn", true);
+ delegateToCalls("alwaysCalledOn", false, "calledOn");
+ delegateToCalls("calledWith", true);
+ delegateToCalls("calledWithMatch", true);
+ delegateToCalls("alwaysCalledWith", false, "calledWith");
+ delegateToCalls("alwaysCalledWithMatch", false, "calledWithMatch");
+ delegateToCalls("calledWithExactly", true);
+ delegateToCalls("alwaysCalledWithExactly", false, "calledWithExactly");
+ delegateToCalls("neverCalledWith", false, "notCalledWith",
+ function () { return true; });
+ delegateToCalls("neverCalledWithMatch", false, "notCalledWithMatch",
+ function () { return true; });
+ delegateToCalls("threw", true);
+ delegateToCalls("alwaysThrew", false, "threw");
+ delegateToCalls("returned", true);
+ delegateToCalls("alwaysReturned", false, "returned");
+ delegateToCalls("calledWithNew", true);
+ delegateToCalls("alwaysCalledWithNew", false, "calledWithNew");
+ delegateToCalls("callArg", false, "callArgWith", function () {
+ throw new Error(this.toString() + " cannot call arg since it was not yet invoked.");
+ });
+ spyApi.callArgWith = spyApi.callArg;
+ delegateToCalls("callArgOn", false, "callArgOnWith", function () {
+ throw new Error(this.toString() + " cannot call arg since it was not yet invoked.");
+ });
+ spyApi.callArgOnWith = spyApi.callArgOn;
+ delegateToCalls("yield", false, "yield", function () {
+ throw new Error(this.toString() + " cannot yield since it was not yet invoked.");
+ });
+ // "invokeCallback" is an alias for "yield" since "yield" is invalid in strict mode.
+ spyApi.invokeCallback = spyApi.yield;
+ delegateToCalls("yieldOn", false, "yieldOn", function () {
+ throw new Error(this.toString() + " cannot yield since it was not yet invoked.");
+ });
+ delegateToCalls("yieldTo", false, "yieldTo", function (property) {
+ throw new Error(this.toString() + " cannot yield to '" + property +
+ "' since it was not yet invoked.");
+ });
+ delegateToCalls("yieldToOn", false, "yieldToOn", function (property) {
+ throw new Error(this.toString() + " cannot yield to '" + property +
+ "' since it was not yet invoked.");
+ });
+
+ spyApi.formatters = {
+ "c": function (spy) {
+ return sinon.timesInWords(spy.callCount);
+ },
+
+ "n": function (spy) {
+ return spy.toString();
+ },
+
+ "C": function (spy) {
+ var calls = [];
+
+ for (var i = 0, l = spy.callCount; i < l; ++i) {
+ var stringifiedCall = " " + spy.getCall(i).toString();
+ if (/\n/.test(calls[i - 1])) {
+ stringifiedCall = "\n" + stringifiedCall;
+ }
+ push.call(calls, stringifiedCall);
+ }
+
+ return calls.length > 0 ? "\n" + calls.join("\n") : "";
+ },
+
+ "t": function (spy) {
+ var objects = [];
+
+ for (var i = 0, l = spy.callCount; i < l; ++i) {
+ push.call(objects, sinon.format(spy.thisValues[i]));
+ }
+
+ return objects.join(", ");
+ },
+
+ "*": function (spy, args) {
+ var formatted = [];
+
+ for (var i = 0, l = args.length; i < l; ++i) {
+ push.call(formatted, sinon.format(args[i]));
+ }
+
+ return formatted.join(", ");
+ }
+ };
+
+ sinon.extend(spy, spyApi);
+
+ spy.spyCall = sinon.spyCall;
+
+ if (commonJSModule) {
+ module.exports = spy;
+ } else {
+ sinon.spy = spy;
+ }
+ }(typeof sinon == "object" && sinon || null));
+
+ /**
+ * @depend ../sinon.js
+ * @depend spy.js
+ */
+ /*jslint eqeqeq: false, onevar: false*/
+ /*global module, require, sinon*/
+ /**
+ * Stub functions
+ *
+ * @author Christian Johansen (christian(a)cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+ (function (sinon) {
+ var commonJSModule = typeof module == "object" && typeof require == "function";
+
+ if (!sinon && commonJSModule) {
+ sinon = require("../sinon");
+ }
+
+ if (!sinon) {
+ return;
+ }
+
+ function stub(object, property, func) {
+ if (!!func && typeof func != "function") {
+ throw new TypeError("Custom stub should be function");
+ }
+
+ var wrapper;
+
+ if (func) {
+ wrapper = sinon.spy && sinon.spy.create ? sinon.spy.create(func) : func;
+ } else {
+ wrapper = stub.create();
+ }
+
+ if (!object && !property) {
+ return sinon.stub.create();
+ }
+
+ if (!property && !!object && typeof object == "object") {
+ for (var prop in object) {
+ if (typeof object[prop] === "function") {
+ stub(object, prop);
+ }
+ }
+
+ return object;
+ }
+
+ return sinon.wrapMethod(object, property, wrapper);
+ }
+
+ function getChangingValue(stub, property) {
+ var index = stub.callCount - 1;
+ var values = stub[property];
+ var prop = index in values ? values[index] : values[values.length - 1];
+ stub[property + "Last"] = prop;
+
+ return prop;
+ }
+
+ function getCallback(stub, args) {
+ var callArgAt = getChangingValue(stub, "callArgAts");
+
+ if (callArgAt < 0) {
+ var callArgProp = getChangingValue(stub, "callArgProps");
+
+ for (var i = 0, l = args.length; i < l; ++i) {
+ if (!callArgProp && typeof args[i] == "function") {
+ return args[i];
+ }
+
+ if (callArgProp && args[i] &&
+ typeof args[i][callArgProp] == "function") {
+ return args[i][callArgProp];
+ }
+ }
+
+ return null;
+ }
+
+ return args[callArgAt];
+ }
+
+ var join = Array.prototype.join;
+
+ function getCallbackError(stub, func, args) {
+ if (stub.callArgAtsLast < 0) {
+ var msg;
+
+ if (stub.callArgPropsLast) {
+ msg = sinon.functionName(stub) +
+ " expected to yield to '" + stub.callArgPropsLast +
+ "', but no object with such a property was passed."
+ } else {
+ msg = sinon.functionName(stub) +
+ " expected to yield, but no callback was passed."
+ }
+
+ if (args.length > 0) {
+ msg += " Received [" + join.call(args, ", ") + "]";
+ }
+
+ return msg;
+ }
+
+ return "argument at index " + stub.callArgAtsLast + " is not a function: " + func;
+ }
+
+ var nextTick = (function () {
+ if (typeof process === "object" && typeof process.nextTick === "function") {
+ return process.nextTick;
+ } else if (typeof setImmediate === "function") {
+ return setImmediate;
+ } else {
+ return function (callback) {
+ setTimeout(callback, 0);
+ };
+ }
+ })();
+
+ function callCallback(stub, args) {
+ if (stub.callArgAts.length > 0) {
+ var func = getCallback(stub, args);
+
+ if (typeof func != "function") {
+ throw new TypeError(getCallbackError(stub, func, args));
+ }
+
+ var callbackArguments = getChangingValue(stub, "callbackArguments");
+ var callbackContext = getChangingValue(stub, "callbackContexts");
+
+ if (stub.callbackAsync) {
+ nextTick(function() {
+ func.apply(callbackContext, callbackArguments);
+ });
+ } else {
+ func.apply(callbackContext, callbackArguments);
+ }
+ }
+ }
+
+ var uuid = 0;
+
+ sinon.extend(stub, (function () {
+ var slice = Array.prototype.slice, proto;
+
+ function throwsException(error, message) {
+ if (typeof error == "string") {
+ this.exception = new Error(message || "");
+ this.exception.name = error;
+ } else if (!error) {
+ this.exception = new Error("Error");
+ } else {
+ this.exception = error;
+ }
+
+ return this;
+ }
+
+ proto = {
+ create: function create() {
+ var functionStub = function () {
+
+ callCallback(functionStub, arguments);
+
+ if (functionStub.exception) {
+ throw functionStub.exception;
+ } else if (typeof functionStub.returnArgAt == 'number') {
+ return arguments[functionStub.returnArgAt];
+ } else if (functionStub.returnThis) {
+ return this;
+ }
+ return functionStub.returnValue;
+ };
+
+ functionStub.id = "stub#" + uuid++;
+ var orig = functionStub;
+ functionStub = sinon.spy.create(functionStub);
+ functionStub.func = orig;
+
+ functionStub.callArgAts = [];
+ functionStub.callbackArguments = [];
+ functionStub.callbackContexts = [];
+ functionStub.callArgProps = [];
+
+ sinon.extend(functionStub, stub);
+ functionStub._create = sinon.stub.create;
+ functionStub.displayName = "stub";
+ functionStub.toString = sinon.functionToString;
+
+ return functionStub;
+ },
+
+ resetBehavior: function () {
+ var i;
+
+ this.callArgAts = [];
+ this.callbackArguments = [];
+ this.callbackContexts = [];
+ this.callArgProps = [];
+
+ delete this.returnValue;
+ delete this.returnArgAt;
+ this.returnThis = false;
+
+ if (this.fakes) {
+ for (i = 0; i < this.fakes.length; i++) {
+ this.fakes[i].resetBehavior();
+ }
+ }
+ },
+
+ returns: function returns(value) {
+ this.returnValue = value;
+
+ return this;
+ },
+
+ returnsArg: function returnsArg(pos) {
+ if (typeof pos != "number") {
+ throw new TypeError("argument index is not number");
+ }
+
+ this.returnArgAt = pos;
+
+ return this;
+ },
+
+ returnsThis: function returnsThis() {
+ this.returnThis = true;
+
+ return this;
+ },
+
+ "throws": throwsException,
+ throwsException: throwsException,
+
+ callsArg: function callsArg(pos) {
+ if (typeof pos != "number") {
+ throw new TypeError("argument index is not number");
+ }
+
+ this.callArgAts.push(pos);
+ this.callbackArguments.push([]);
+ this.callbackContexts.push(undefined);
+ this.callArgProps.push(undefined);
+
+ return this;
+ },
+
+ callsArgOn: function callsArgOn(pos, context) {
+ if (typeof pos != "number") {
+ throw new TypeError("argument index is not number");
+ }
+ if (typeof context != "object") {
+ throw new TypeError("argument context is not an object");
+ }
+
+ this.callArgAts.push(pos);
+ this.callbackArguments.push([]);
+ this.callbackContexts.push(context);
+ this.callArgProps.push(undefined);
+
+ return this;
+ },
+
+ callsArgWith: function callsArgWith(pos) {
+ if (typeof pos != "number") {
+ throw new TypeError("argument index is not number");
+ }
+
+ this.callArgAts.push(pos);
+ this.callbackArguments.push(slice.call(arguments, 1));
+ this.callbackContexts.push(undefined);
+ this.callArgProps.push(undefined);
+
+ return this;
+ },
+
+ callsArgOnWith: function callsArgWith(pos, context) {
+ if (typeof pos != "number") {
+ throw new TypeError("argument index is not number");
+ }
+ if (typeof context != "object") {
+ throw new TypeError("argument context is not an object");
+ }
+
+ this.callArgAts.push(pos);
+ this.callbackArguments.push(slice.call(arguments, 2));
+ this.callbackContexts.push(context);
+ this.callArgProps.push(undefined);
+
+ return this;
+ },
+
+ yields: function () {
+ this.callArgAts.push(-1);
+ this.callbackArguments.push(slice.call(arguments, 0));
+ this.callbackContexts.push(undefined);
+ this.callArgProps.push(undefined);
+
+ return this;
+ },
+
+ yieldsOn: function (context) {
+ if (typeof context != "object") {
+ throw new TypeError("argument context is not an object");
+ }
+
+ this.callArgAts.push(-1);
+ this.callbackArguments.push(slice.call(arguments, 1));
+ this.callbackContexts.push(context);
+ this.callArgProps.push(undefined);
+
+ return this;
+ },
+
+ yieldsTo: function (prop) {
+ this.callArgAts.push(-1);
+ this.callbackArguments.push(slice.call(arguments, 1));
+ this.callbackContexts.push(undefined);
+ this.callArgProps.push(prop);
+
+ return this;
+ },
+
+ yieldsToOn: function (prop, context) {
+ if (typeof context != "object") {
+ throw new TypeError("argument context is not an object");
+ }
+
+ this.callArgAts.push(-1);
+ this.callbackArguments.push(slice.call(arguments, 2));
+ this.callbackContexts.push(context);
+ this.callArgProps.push(prop);
+
+ return this;
+ }
+ };
+
+ // create asynchronous versions of callsArg* and yields* methods
+ for (var method in proto) {
+ // need to avoid creating anotherasync versions of the newly added async methods
+ if (proto.hasOwnProperty(method) &&
+ method.match(/^(callsArg|yields|thenYields$)/) &&
+ !method.match(/Async/)) {
+ proto[method + 'Async'] = (function (syncFnName) {
+ return function () {
+ this.callbackAsync = true;
+ return this[syncFnName].apply(this, arguments);
+ };
+ })(method);
+ }
+ }
+
+ return proto;
+
+ }()));
+
+ if (commonJSModule) {
+ module.exports = stub;
+ } else {
+ sinon.stub = stub;
+ }
+ }(typeof sinon == "object" && sinon || null));
+
+ /**
+ * @depend ../sinon.js
+ * @depend stub.js
+ */
+ /*jslint eqeqeq: false, onevar: false, nomen: false*/
+ /*global module, require, sinon*/
+ /**
+ * Mock functions.
+ *
+ * @author Christian Johansen (christian(a)cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+ (function (sinon) {
+ var commonJSModule = typeof module == "object" && typeof require == "function";
+ var push = [].push;
+
+ if (!sinon && commonJSModule) {
+ sinon = require("../sinon");
+ }
+
+ if (!sinon) {
+ return;
+ }
+
+ function mock(object) {
+ if (!object) {
+ return sinon.expectation.create("Anonymous mock");
+ }
+
+ return mock.create(object);
+ }
+
+ sinon.mock = mock;
+
+ sinon.extend(mock, (function () {
+ function each(collection, callback) {
+ if (!collection) {
+ return;
+ }
+
+ for (var i = 0, l = collection.length; i < l; i += 1) {
+ callback(collection[i]);
+ }
+ }
+
+ return {
+ create: function create(object) {
+ if (!object) {
+ throw new TypeError("object is null");
+ }
+
+ var mockObject = sinon.extend({}, mock);
+ mockObject.object = object;
+ delete mockObject.create;
+
+ return mockObject;
+ },
+
+ expects: function expects(method) {
+ if (!method) {
+ throw new TypeError("method is falsy");
+ }
+
+ if (!this.expectations) {
+ this.expectations = {};
+ this.proxies = [];
+ }
+
+ if (!this.expectations[method]) {
+ this.expectations[method] = [];
+ var mockObject = this;
+
+ sinon.wrapMethod(this.object, method, function () {
+ return mockObject.invokeMethod(method, this, arguments);
+ });
+
+ push.call(this.proxies, method);
+ }
+
+ var expectation = sinon.expectation.create(method);
+ push.call(this.expectations[method], expectation);
+
+ return expectation;
+ },
+
+ restore: function restore() {
+ var object = this.object;
+
+ each(this.proxies, function (proxy) {
+ if (typeof object[proxy].restore == "function") {
+ object[proxy].restore();
+ }
+ });
+ },
+
+ verify: function verify() {
+ var expectations = this.expectations || {};
+ var messages = [], met = [];
+
+ each(this.proxies, function (proxy) {
+ each(expectations[proxy], function (expectation) {
+ if (!expectation.met()) {
+ push.call(messages, expectation.toString());
+ } else {
+ push.call(met, expectation.toString());
+ }
+ });
+ });
+
+ this.restore();
+
+ if (messages.length > 0) {
+ sinon.expectation.fail(messages.concat(met).join("\n"));
+ } else {
+ sinon.expectation.pass(messages.concat(met).join("\n"));
+ }
+
+ return true;
+ },
+
+ invokeMethod: function invokeMethod(method, thisValue, args) {
+ var expectations = this.expectations && this.expectations[method];
+ var length = expectations && expectations.length || 0, i;
+
+ for (i = 0; i < length; i += 1) {
+ if (!expectations[i].met() &&
+ expectations[i].allowsCall(thisValue, args)) {
+ return expectations[i].apply(thisValue, args);
+ }
+ }
+
+ var messages = [], available, exhausted = 0;
+
+ for (i = 0; i < length; i += 1) {
+ if (expectations[i].allowsCall(thisValue, args)) {
+ available = available || expectations[i];
+ } else {
+ exhausted += 1;
+ }
+ push.call(messages, " " + expectations[i].toString());
+ }
+
+ if (exhausted === 0) {
+ return available.apply(thisValue, args);
+ }
+
+ messages.unshift("Unexpected call: " + sinon.spyCall.toString.call({
+ proxy: method,
+ args: args
+ }));
+
+ sinon.expectation.fail(messages.join("\n"));
+ }
+ };
+ }()));
+
+ var times = sinon.timesInWords;
+
+ sinon.expectation = (function () {
+ var slice = Array.prototype.slice;
+ var _invoke = sinon.spy.invoke;
+
+ function callCountInWords(callCount) {
+ if (callCount == 0) {
+ return "never called";
+ } else {
+ return "called " + times(callCount);
+ }
+ }
+
+ function expectedCallCountInWords(expectation) {
+ var min = expectation.minCalls;
+ var max = expectation.maxCalls;
+
+ if (typeof min == "number" && typeof max == "number") {
+ var str = times(min);
+
+ if (min != max) {
+ str = "at least " + str + " and at most " + times(max);
+ }
+
+ return str;
+ }
+
+ if (typeof min == "number") {
+ return "at least " + times(min);
+ }
+
+ return "at most " + times(max);
+ }
+
+ function receivedMinCalls(expectation) {
+ var hasMinLimit = typeof expectation.minCalls == "number";
+ return !hasMinLimit || expectation.callCount >= expectation.minCalls;
+ }
+
+ function receivedMaxCalls(expectation) {
+ if (typeof expectation.maxCalls != "number") {
+ return false;
+ }
+
+ return expectation.callCount == expectation.maxCalls;
+ }
+
+ return {
+ minCalls: 1,
+ maxCalls: 1,
+
+ create: function create(methodName) {
+ var expectation = sinon.extend(sinon.stub.create(), sinon.expectation);
+ delete expectation.create;
+ expectation.method = methodName;
+
+ return expectation;
+ },
+
+ invoke: function invoke(func, thisValue, args) {
+ this.verifyCallAllowed(thisValue, args);
+
+ return _invoke.apply(this, arguments);
+ },
+
+ atLeast: function atLeast(num) {
+ if (typeof num != "number") {
+ throw new TypeError("'" + num + "' is not number");
+ }
+
+ if (!this.limitsSet) {
+ this.maxCalls = null;
+ this.limitsSet = true;
+ }
+
+ this.minCalls = num;
+
+ return this;
+ },
+
+ atMost: function atMost(num) {
+ if (typeof num != "number") {
+ throw new TypeError("'" + num + "' is not number");
+ }
+
+ if (!this.limitsSet) {
+ this.minCalls = null;
+ this.limitsSet = true;
+ }
+
+ this.maxCalls = num;
+
+ return this;
+ },
+
+ never: function never() {
+ return this.exactly(0);
+ },
+
+ once: function once() {
+ return this.exactly(1);
+ },
+
+ twice: function twice() {
+ return this.exactly(2);
+ },
+
+ thrice: function thrice() {
+ return this.exactly(3);
+ },
+
+ exactly: function exactly(num) {
+ if (typeof num != "number") {
+ throw new TypeError("'" + num + "' is not a number");
+ }
+
+ this.atLeast(num);
+ return this.atMost(num);
+ },
+
+ met: function met() {
+ return !this.failed && receivedMinCalls(this);
+ },
+
+ verifyCallAllowed: function verifyCallAllowed(thisValue, args) {
+ if (receivedMaxCalls(this)) {
+ this.failed = true;
+ sinon.expectation.fail(this.method + " already called " + times(this.maxCalls));
+ }
+
+ if ("expectedThis" in this && this.expectedThis !== thisValue) {
+ sinon.expectation.fail(this.method + " called with " + thisValue + " as thisValue, expected " +
+ this.expectedThis);
+ }
+
+ if (!("expectedArguments" in this)) {
+ return;
+ }
+
+ if (!args) {
+ sinon.expectation.fail(this.method + " received no arguments, expected " +
+ sinon.format(this.expectedArguments));
+ }
+
+ if (args.length < this.expectedArguments.length) {
+ sinon.expectation.fail(this.method + " received too few arguments (" + sinon.format(args) +
+ "), expected " + sinon.format(this.expectedArguments));
+ }
+
+ if (this.expectsExactArgCount &&
+ args.length != this.expectedArguments.length) {
+ sinon.expectation.fail(this.method + " received too many arguments (" + sinon.format(args) +
+ "), expected " + sinon.format(this.expectedArguments));
+ }
+
+ for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) {
+ if (!sinon.deepEqual(this.expectedArguments[i], args[i])) {
+ sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) +
+ ", expected " + sinon.format(this.expectedArguments));
+ }
+ }
+ },
+
+ allowsCall: function allowsCall(thisValue, args) {
+ if (this.met() && receivedMaxCalls(this)) {
+ return false;
+ }
+
+ if ("expectedThis" in this && this.expectedThis !== thisValue) {
+ return false;
+ }
+
+ if (!("expectedArguments" in this)) {
+ return true;
+ }
+
+ args = args || [];
+
+ if (args.length < this.expectedArguments.length) {
+ return false;
+ }
+
+ if (this.expectsExactArgCount &&
+ args.length != this.expectedArguments.length) {
+ return false;
+ }
+
+ for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) {
+ if (!sinon.deepEqual(this.expectedArguments[i], args[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ withArgs: function withArgs() {
+ this.expectedArguments = slice.call(arguments);
+ return this;
+ },
+
+ withExactArgs: function withExactArgs() {
+ this.withArgs.apply(this, arguments);
+ this.expectsExactArgCount = true;
+ return this;
+ },
+
+ on: function on(thisValue) {
+ this.expectedThis = thisValue;
+ return this;
+ },
+
+ toString: function () {
+ var args = (this.expectedArguments || []).slice();
+
+ if (!this.expectsExactArgCount) {
+ push.call(args, "[...]");
+ }
+
+ var callStr = sinon.spyCall.toString.call({
+ proxy: this.method || "anonymous mock expectation",
+ args: args
+ });
+
+ var message = callStr.replace(", [...", "[, ...") + " " +
+ expectedCallCountInWords(this);
+
+ if (this.met()) {
+ return "Expectation met: " + message;
+ }
+
+ return "Expected " + message + " (" +
+ callCountInWords(this.callCount) + ")";
+ },
+
+ verify: function verify() {
+ if (!this.met()) {
+ sinon.expectation.fail(this.toString());
+ } else {
+ sinon.expectation.pass(this.toString());
+ }
+
+ return true;
+ },
+
+ pass: function(message) {
+ sinon.assert.pass(message);
+ },
+ fail: function (message) {
+ var exception = new Error(message);
+ exception.name = "ExpectationError";
+
+ throw exception;
+ }
+ };
+ }());
+
+ if (commonJSModule) {
+ module.exports = mock;
+ } else {
+ sinon.mock = mock;
+ }
+ }(typeof sinon == "object" && sinon || null));
+
+ /**
+ * @depend ../sinon.js
+ * @depend stub.js
+ * @depend mock.js
+ */
+ /*jslint eqeqeq: false, onevar: false, forin: true*/
+ /*global module, require, sinon*/
+ /**
+ * Collections of stubs, spies and mocks.
+ *
+ * @author Christian Johansen (christian(a)cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+ (function (sinon) {
+ var commonJSModule = typeof module == "object" && typeof require == "function";
+ var push = [].push;
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+ if (!sinon && commonJSModule) {
+ sinon = require("../sinon");
+ }
+
+ if (!sinon) {
+ return;
+ }
+
+ function getFakes(fakeCollection) {
+ if (!fakeCollection.fakes) {
+ fakeCollection.fakes = [];
+ }
+
+ return fakeCollection.fakes;
+ }
+
+ function each(fakeCollection, method) {
+ var fakes = getFakes(fakeCollection);
+
+ for (var i = 0, l = fakes.length; i < l; i += 1) {
+ if (typeof fakes[i][method] == "function") {
+ fakes[i][method]();
+ }
+ }
+ }
+
+ function compact(fakeCollection) {
+ var fakes = getFakes(fakeCollection);
+ var i = 0;
+ while (i < fakes.length) {
+ fakes.splice(i, 1);
+ }
+ }
+
+ var collection = {
+ verify: function resolve() {
+ each(this, "verify");
+ },
+
+ restore: function restore() {
+ each(this, "restore");
+ compact(this);
+ },
+
+ verifyAndRestore: function verifyAndRestore() {
+ var exception;
+
+ try {
+ this.verify();
+ } catch (e) {
+ exception = e;
+ }
+
+ this.restore();
+
+ if (exception) {
+ throw exception;
+ }
+ },
+
+ add: function add(fake) {
+ push.call(getFakes(this), fake);
+ return fake;
+ },
+
+ spy: function spy() {
+ return this.add(sinon.spy.apply(sinon, arguments));
+ },
+
+ stub: function stub(object, property, value) {
+ if (property) {
+ var original = object[property];
+
+ if (typeof original != "function") {
+ if (!hasOwnProperty.call(object, property)) {
+ throw new TypeError("Cannot stub non-existent own property " + property);
+ }
+
+ object[property] = value;
+
+ return this.add({
+ restore: function () {
+ object[property] = original;
+ }
+ });
+ }
+ }
+ if (!property && !!object && typeof object == "object") {
+ var stubbedObj = sinon.stub.apply(sinon, arguments);
+
+ for (var prop in stubbedObj) {
+ if (typeof stubbedObj[prop] === "function") {
+ this.add(stubbedObj[prop]);
+ }
+ }
+
+ return stubbedObj;
+ }
+
+ return this.add(sinon.stub.apply(sinon, arguments));
+ },
+
+ mock: function mock() {
+ return this.add(sinon.mock.apply(sinon, arguments));
+ },
+
+ inject: function inject(obj) {
+ var col = this;
+
+ obj.spy = function () {
+ return col.spy.apply(col, arguments);
+ };
+
+ obj.stub = function () {
+ return col.stub.apply(col, arguments);
+ };
+
+ obj.mock = function () {
+ return col.mock.apply(col, arguments);
+ };
+
+ return obj;
+ }
+ };
+
+ if (commonJSModule) {
+ module.exports = collection;
+ } else {
+ sinon.collection = collection;
+ }
+ }(typeof sinon == "object" && sinon || null));
+
+ /*jslint eqeqeq: false, plusplus: false, evil: true, onevar: false, browser: true, forin: false*/
+ /*global module, require, window*/
+ /**
+ * Fake timer API
+ * setTimeout
+ * setInterval
+ * clearTimeout
+ * clearInterval
+ * tick
+ * reset
+ * Date
+ *
+ * Inspired by jsUnitMockTimeOut from JsUnit
+ *
+ * @author Christian Johansen (christian(a)cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+ if (typeof sinon == "undefined") {
+ var sinon = {};
+ }
+
+ (function (global) {
+ var id = 1;
+
+ function addTimer(args, recurring) {
+ if (args.length === 0) {
+ throw new Error("Function requires at least 1 parameter");
+ }
+
+ var toId = id++;
+ var delay = args[1] || 0;
+
+ if (!this.timeouts) {
+ this.timeouts = {};
+ }
+
+ this.timeouts[toId] = {
+ id: toId,
+ func: args[0],
+ callAt: this.now + delay,
+ invokeArgs: Array.prototype.slice.call(args, 2)
+ };
+
+ if (recurring === true) {
+ this.timeouts[toId].interval = delay;
+ }
+
+ return toId;
+ }
+
+ function parseTime(str) {
+ if (!str) {
+ return 0;
+ }
+
+ var strings = str.split(":");
+ var l = strings.length, i = l;
+ var ms = 0, parsed;
+
+ if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
+ throw new Error("tick only understands numbers and 'h:m:s'");
+ }
+
+ while (i--) {
+ parsed = parseInt(strings[i], 10);
+
+ if (parsed >= 60) {
+ throw new Error("Invalid time " + str);
+ }
+
+ ms += parsed * Math.pow(60, (l - i - 1));
+ }
+
+ return ms * 1000;
+ }
+
+ function createObject(object) {
+ var newObject;
+
+ if (Object.create) {
+ newObject = Object.create(object);
+ } else {
+ var F = function () {};
+ F.prototype = object;
+ newObject = new F();
+ }
+
+ newObject.Date.clock = newObject;
+ return newObject;
+ }
+
+ sinon.clock = {
+ now: 0,
+
+ create: function create(now) {
+ var clock = createObject(this);
+
+ if (typeof now == "number") {
+ clock.now = now;
+ }
+
+ if (!!now && typeof now == "object") {
+ throw new TypeError("now should be milliseconds since UNIX epoch");
+ }
+
+ return clock;
+ },
+
+ setTimeout: function setTimeout(callback, timeout) {
+ return addTimer.call(this, arguments, false);
+ },
+
+ clearTimeout: function clearTimeout(timerId) {
+ if (!this.timeouts) {
+ this.timeouts = [];
+ }
+
+ if (timerId in this.timeouts) {
+ delete this.timeouts[timerId];
+ }
+ },
+
+ setInterval: function setInterval(callback, timeout) {
+ return addTimer.call(this, arguments, true);
+ },
+
+ clearInterval: function clearInterval(timerId) {
+ this.clearTimeout(timerId);
+ },
+
+ tick: function tick(ms) {
+ ms = typeof ms == "number" ? ms : parseTime(ms);
+ var tickFrom = this.now, tickTo = this.now + ms, previous = this.now;
+ var timer = this.firstTimerInRange(tickFrom, tickTo);
+
+ var firstException;
+ while (timer && tickFrom <= tickTo) {
+ if (this.timeouts[timer.id]) {
+ tickFrom = this.now = timer.callAt;
+ try {
+ this.callTimer(timer);
+ } catch (e) {
+ firstException = firstException || e;
+ }
+ }
+
+ timer = this.firstTimerInRange(previous, tickTo);
+ previous = tickFrom;
+ }
+
+ this.now = tickTo;
+
+ if (firstException) {
+ throw firstException;
+ }
+
+ return this.now;
+ },
+
+ firstTimerInRange: function (from, to) {
+ var timer, smallest, originalTimer;
+
+ for (var id in this.timeouts) {
+ if (this.timeouts.hasOwnProperty(id)) {
+ if (this.timeouts[id].callAt < from || this.timeouts[id].callAt > to) {
+ continue;
+ }
+
+ if (!smallest || this.timeouts[id].callAt < smallest) {
+ originalTimer = this.timeouts[id];
+ smallest = this.timeouts[id].callAt;
+
+ timer = {
+ func: this.timeouts[id].func,
+ callAt: this.timeouts[id].callAt,
+ interval: this.timeouts[id].interval,
+ id: this.timeouts[id].id,
+ invokeArgs: this.timeouts[id].invokeArgs
+ };
+ }
+ }
+ }
+
+ return timer || null;
+ },
+
+ callTimer: function (timer) {
+ if (typeof timer.interval == "number") {
+ this.timeouts[timer.id].callAt += timer.interval;
+ } else {
+ delete this.timeouts[timer.id];
+ }
+
+ try {
+ if (typeof timer.func == "function") {
+ timer.func.apply(null, timer.invokeArgs);
+ } else {
+ eval(timer.func);
+ }
+ } catch (e) {
+ var exception = e;
+ }
+
+ if (!this.timeouts[timer.id]) {
+ if (exception) {
+ throw exception;
+ }
+ return;
+ }
+
+ if (exception) {
+ throw exception;
+ }
+ },
+
+ reset: function reset() {
+ this.timeouts = {};
+ },
+
+ Date: (function () {
+ var NativeDate = Date;
+
+ function ClockDate(year, month, date, hour, minute, second, ms) {
+ // Defensive and verbose to avoid potential harm in passing
+ // explicit undefined when user does not pass argument
+ switch (arguments.length) {
+ case 0:
+ return new NativeDate(ClockDate.clock.now);
+ case 1:
+ return new NativeDate(year);
+ case 2:
+ return new NativeDate(year, month);
+ case 3:
+ return new NativeDate(year, month, date);
+ case 4:
+ return new NativeDate(year, month, date, hour);
+ case 5:
+ return new NativeDate(year, month, date, hour, minute);
+ case 6:
+ return new NativeDate(year, month, date, hour, minute, second);
+ default:
+ return new NativeDate(year, month, date, hour, minute, second, ms);
+ }
+ }
+
+ return mirrorDateProperties(ClockDate, NativeDate);
+ }())
+ };
+
+ function mirrorDateProperties(target, source) {
+ if (source.now) {
+ target.now = function now() {
+ return target.clock.now;
+ };
+ } else {
+ delete target.now;
+ }
+
+ if (source.toSource) {
+ target.toSource = function toSource() {
+ return source.toSource();
+ };
+ } else {
+ delete target.toSource;
+ }
+
+ target.toString = function toString() {
+ return source.toString();
+ };
+
+ target.prototype = source.prototype;
+ target.parse = source.parse;
+ target.UTC = source.UTC;
+ target.prototype.toUTCString = source.prototype.toUTCString;
+ return target;
+ }
+
+ var methods = ["Date", "setTimeout", "setInterval",
+ "clearTimeout", "clearInterval"];
+
+ function restore() {
+ var method;
+
+ for (var i = 0, l = this.methods.length; i < l; i++) {
+ method = this.methods[i];
+ if (global[method].hadOwnProperty) {
+ global[method] = this["_" + method];
+ } else {
+ delete global[method];
+ }
+ }
+
+ // Prevent multiple executions which will completely remove these props
+ this.methods = [];
+ }
+
+ function stubGlobal(method, clock) {
+ clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(global, method);
+ clock["_" + method] = global[method];
+
+ if (method == "Date") {
+ var date = mirrorDateProperties(clock[method], global[method]);
+ global[method] = date;
+ } else {
+ global[method] = function () {
+ return clock[method].apply(clock, arguments);
+ };
+
+ for (var prop in clock[method]) {
+ if (clock[method].hasOwnProperty(prop)) {
+ global[method][prop] = clock[method][prop];
+ }
+ }
+ }
+
+ global[method].clock = clock;
+ }
+
+ sinon.useFakeTimers = function useFakeTimers(now) {
+ var clock = sinon.clock.create(now);
+ clock.restore = restore;
+ clock.methods = Array.prototype.slice.call(arguments,
+ typeof now == "number" ? 1 : 0);
+
+ if (clock.methods.length === 0) {
+ clock.methods = methods;
+ }
+
+ for (var i = 0, l = clock.methods.length; i < l; i++) {
+ stubGlobal(clock.methods[i], clock);
+ }
+
+ return clock;
+ };
+ }(typeof global != "undefined" && typeof global !== "function" ? global : this));
+
+ sinon.timers = {
+ setTimeout: setTimeout,
+ clearTimeout: clearTimeout,
+ setInterval: setInterval,
+ clearInterval: clearInterval,
+ Date: Date
+ };
+
+ if (typeof module == "object" && typeof require == "function") {
+ module.exports = sinon;
+ }
+
+ /*jslint eqeqeq: false, onevar: false*/
+ /*global sinon, module, require, ActiveXObject, XMLHttpRequest, DOMParser*/
+ /**
+ * Minimal Event interface implementation
+ *
+ * Original implementation by Sven Fuchs: https://gist.github.com/995028
+ * Modifications and tests by Christian Johansen.
+ *
+ * @author Sven Fuchs (svenfuchs(a)artweb-design.de)
+ * @author Christian Johansen (christian(a)cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2011 Sven Fuchs, Christian Johansen
+ */
+
+ if (typeof sinon == "undefined") {
+ this.sinon = {};
+ }
+
+ (function () {
+ var push = [].push;
+
+ sinon.Event = function Event(type, bubbles, cancelable, target) {
+ this.initEvent(type, bubbles, cancelable, target);
+ };
+
+ sinon.Event.prototype = {
+ initEvent: function(type, bubbles, cancelable, target) {
+ this.type = type;
+ this.bubbles = bubbles;
+ this.cancelable = cancelable;
+ this.target = target;
+ },
+
+ stopPropagation: function () {},
+
+ preventDefault: function () {
+ this.defaultPrevented = true;
+ }
+ };
+
+ sinon.EventTarget = {
+ addEventListener: function addEventListener(event, listener, useCapture) {
+ this.eventListeners = this.eventListeners || {};
+ this.eventListeners[event] = this.eventListeners[event] || [];
+ push.call(this.eventListeners[event], listener);
+ },
+
+ removeEventListener: function removeEventListener(event, listener, useCapture) {
+ var listeners = this.eventListeners && this.eventListeners[event] || [];
+
+ for (var i = 0, l = listeners.length; i < l; ++i) {
+ if (listeners[i] == listener) {
+ return listeners.splice(i, 1);
+ }
+ }
+ },
+
+ dispatchEvent: function dispatchEvent(event) {
+ var type = event.type;
+ var listeners = this.eventListeners && this.eventListeners[type] || [];
+
+ for (var i = 0; i < listeners.length; i++) {
+ if (typeof listeners[i] == "function") {
+ listeners[i].call(this, event);
+ } else {
+ listeners[i].handleEvent(event);
+ }
+ }
+
+ return !!event.defaultPrevented;
+ }
+ };
+ }());
+
+ /**
+ * @depend ../../sinon.js
+ * @depend event.js
+ */
+ /*jslint eqeqeq: false, onevar: false*/
+ /*global sinon, module, require, ActiveXObject, XMLHttpRequest, DOMParser*/
+ /**
+ * Fake XMLHttpRequest object
+ *
+ * @author Christian Johansen (christian(a)cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+ if (typeof sinon == "undefined") {
+ this.sinon = {};
+ }
+ sinon.xhr = { XMLHttpRequest: this.XMLHttpRequest };
+
+// wrapper for global
+ (function(global) {
+ var xhr = sinon.xhr;
+ xhr.GlobalXMLHttpRequest = global.XMLHttpRequest;
+ xhr.GlobalActiveXObject = global.ActiveXObject;
+ xhr.supportsActiveX = typeof xhr.GlobalActiveXObject != "undefined";
+ xhr.supportsXHR = typeof xhr.GlobalXMLHttpRequest != "undefined";
+ xhr.workingXHR = xhr.supportsXHR ? xhr.GlobalXMLHttpRequest : xhr.supportsActiveX
+ ? function() { return new xhr.GlobalActiveXObject("MSXML2.XMLHTTP.3.0") } : false;
+
+ /*jsl:ignore*/
+ var unsafeHeaders = {
+ "Accept-Charset": true,
+ "Accept-Encoding": true,
+ "Connection": true,
+ "Content-Length": true,
+ "Cookie": true,
+ "Cookie2": true,
+ "Content-Transfer-Encoding": true,
+ "Date": true,
+ "Expect": true,
+ "Host": true,
+ "Keep-Alive": true,
+ "Referer": true,
+ "TE": true,
+ "Trailer": true,
+ "Transfer-Encoding": true,
+ "Upgrade": true,
+ "User-Agent": true,
+ "Via": true
+ };
+ /*jsl:end*/
+
+ function FakeXMLHttpRequest() {
+ this.readyState = FakeXMLHttpRequest.UNSENT;
+ this.requestHeaders = {};
+ this.requestBody = null;
+ this.status = 0;
+ this.statusText = "";
+
+ var xhr = this;
+
+ ["loadstart", "load", "abort", "loadend"].forEach(function (eventName) {
+ xhr.addEventListener(eventName, function (event) {
+ var listener = xhr["on" + eventName];
+
+ if (listener && typeof listener == "function") {
+ listener(event);
+ }
+ });
+ });
+
+ if (typeof FakeXMLHttpRequest.onCreate == "function") {
+ FakeXMLHttpRequest.onCreate(this);
+ }
+ }
+
+ function verifyState(xhr) {
+ if (xhr.readyState !== FakeXMLHttpRequest.OPENED) {
+ throw new Error("INVALID_STATE_ERR");
+ }
+
+ if (xhr.sendFlag) {
+ throw new Error("INVALID_STATE_ERR");
+ }
+ }
+
+ // filtering to enable a white-list version of Sinon FakeXhr,
+ // where whitelisted requests are passed through to real XHR
+ function each(collection, callback) {
+ if (!collection) return;
+ for (var i = 0, l = collection.length; i < l; i += 1) {
+ callback(collection[i]);
+ }
+ }
+ function some(collection, callback) {
+ for (var index = 0; index < collection.length; index++) {
+ if(callback(collection[index]) === true) return true;
+ };
+ return false;
+ }
+ // largest arity in XHR is 5 - XHR#open
+ var apply = function(obj,method,args) {
+ switch(args.length) {
+ case 0: return obj[method]();
+ case 1: return obj[method](args[0]);
+ case 2: return obj[method](args[0],args[1]);
+ case 3: return obj[method](args[0],args[1],args[2]);
+ case 4: return obj[method](args[0],args[1],args[2],args[3]);
+ case 5: return obj[method](args[0],args[1],args[2],args[3],args[4]);
+ };
+ };
+
+ FakeXMLHttpRequest.filters = [];
+ FakeXMLHttpRequest.addFilter = function(fn) {
+ this.filters.push(fn)
+ };
+ var IE6Re = /MSIE 6/;
+ FakeXMLHttpRequest.defake = function(fakeXhr,xhrArgs) {
+ var xhr = new sinon.xhr.workingXHR();
+ each(["open","setRequestHeader","send","abort","getResponseHeader",
+ "getAllResponseHeaders","addEventListener","overrideMimeType","removeEventListener"],
+ function(method) {
+ fakeXhr[method] = function() {
+ return apply(xhr,method,arguments);
+ };
+ });
+
+ var copyAttrs = function(args) {
+ each(args, function(attr) {
+ try {
+ fakeXhr[attr] = xhr[attr]
+ } catch(e) {
+ if(!IE6Re.test(navigator.userAgent)) throw e;
+ }
+ });
+ };
+
+ var stateChange = function() {
+ fakeXhr.readyState = xhr.readyState;
+ if(xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) {
+ copyAttrs(["status","statusText"]);
+ }
+ if(xhr.readyState >= FakeXMLHttpRequest.LOADING) {
+ copyAttrs(["responseText"]);
+ }
+ if(xhr.readyState === FakeXMLHttpRequest.DONE) {
+ copyAttrs(["responseXML"]);
+ }
+ if(fakeXhr.onreadystatechange) fakeXhr.onreadystatechange.call(fakeXhr);
+ };
+ if(xhr.addEventListener) {
+ for(var event in fakeXhr.eventListeners) {
+ if(fakeXhr.eventListeners.hasOwnProperty(event)) {
+ each(fakeXhr.eventListeners[event],function(handler) {
+ xhr.addEventListener(event, handler);
+ });
+ }
+ }
+ xhr.addEventListener("readystatechange",stateChange);
+ } else {
+ xhr.onreadystatechange = stateChange;
+ }
+ apply(xhr,"open",xhrArgs);
+ };
+ FakeXMLHttpRequest.useFilters = false;
+
+ function verifyRequestSent(xhr) {
+ if (xhr.readyState == FakeXMLHttpRequest.DONE) {
+ throw new Error("Request done");
+ }
+ }
+
+ function verifyHeadersReceived(xhr) {
+ if (xhr.async && xhr.readyState != FakeXMLHttpRequest.HEADERS_RECEIVED) {
+ throw new Error("No headers received");
+ }
+ }
+
+ function verifyResponseBodyType(body) {
+ if (typeof body != "string") {
+ var error = new Error("Attempted to respond to fake XMLHttpRequest with " +
+ body + ", which is not a string.");
+ error.name = "InvalidBodyException";
+ throw error;
+ }
+ }
+
+ sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, {
+ async: true,
+
+ open: function open(method, url, async, username, password) {
+ this.method = method;
+ this.url = url;
+ this.async = typeof async == "boolean" ? async : true;
+ this.username = username;
+ this.password = password;
+ this.responseText = null;
+ this.responseXML = null;
+ this.requestHeaders = {};
+ this.sendFlag = false;
+ if(sinon.FakeXMLHttpRequest.useFilters === true) {
+ var xhrArgs = arguments;
+ var defake = some(FakeXMLHttpRequest.filters,function(filter) {
+ return filter.apply(this,xhrArgs)
+ });
+ if (defake) {
+ return sinon.FakeXMLHttpRequest.defake(this,arguments);
+ }
+ }
+ this.readyStateChange(FakeXMLHttpRequest.OPENED);
+ },
+
+ readyStateChange: function readyStateChange(state) {
+ this.readyState = state;
+
+ if (typeof this.onreadystatechange == "function") {
+ try {
+ this.onreadystatechange();
+ } catch (e) {
+ sinon.logError("Fake XHR onreadystatechange handler", e);
+ }
+ }
+
+ this.dispatchEvent(new sinon.Event("readystatechange"));
+
+ switch (this.readyState) {
+ case FakeXMLHttpRequest.DONE:
+ this.dispatchEvent(new sinon.Event("load", false, false, this));
+ this.dispatchEvent(new sinon.Event("loadend", false, false, this));
+ break;
+ }
+ },
+
+ setRequestHeader: function setRequestHeader(header, value) {
+ verifyState(this);
+
+ if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) {
+ throw new Error("Refused to set unsafe header \"" + header + "\"");
+ }
+
+ if (this.requestHeaders[header]) {
+ this.requestHeaders[header] += "," + value;
+ } else {
+ this.requestHeaders[header] = value;
+ }
+ },
+
+ // Helps testing
+ setResponseHeaders: function setResponseHeaders(headers) {
+ this.responseHeaders = {};
+
+ for (var header in headers) {
+ if (headers.hasOwnProperty(header)) {
+ this.responseHeaders[header] = headers[header];
+ }
+ }
+
+ if (this.async) {
+ this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED);
+ } else {
+ this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED;
+ }
+ },
+
+ // Currently treats ALL data as a DOMString (i.e. no Document)
+ send: function send(data) {
+ verifyState(this);
+
+ if (!/^(get|head)$/i.test(this.method)) {
+ if (this.requestHeaders["Content-Type"]) {
+ var value = this.requestHeaders["Content-Type"].split(";");
+ this.requestHeaders["Content-Type"] = value[0] + ";charset=utf-8";
+ } else {
+ this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8";
+ }
+
+ this.requestBody = data;
+ }
+
+ this.errorFlag = false;
+ this.sendFlag = this.async;
+ this.readyStateChange(FakeXMLHttpRequest.OPENED);
+
+ if (typeof this.onSend == "function") {
+ this.onSend(this);
+ }
+
+ this.dispatchEvent(new sinon.Event("loadstart", false, false, this));
+ },
+
+ abort: function abort() {
+ this.aborted = true;
+ this.responseText = null;
+ this.errorFlag = true;
+ this.requestHeaders = {};
+
+ if (this.readyState > sinon.FakeXMLHttpRequest.UNSENT && this.sendFlag) {
+ this.readyStateChange(sinon.FakeXMLHttpRequest.DONE);
+ this.sendFlag = false;
+ }
+
+ this.readyState = sinon.FakeXMLHttpRequest.UNSENT;
+
+ this.dispatchEvent(new sinon.Event("abort", false, false, this));
+ if (typeof this.onerror === "function") {
+ this.onerror();
+ }
+ },
+
+ getResponseHeader: function getResponseHeader(header) {
+ if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
+ return null;
+ }
+
+ if (/^Set-Cookie2?$/i.test(header)) {
+ return null;
+ }
+
+ header = header.toLowerCase();
+
+ for (var h in this.responseHeaders) {
+ if (h.toLowerCase() == header) {
+ return this.responseHeaders[h];
+ }
+ }
+
+ return null;
+ },
+
+ getAllResponseHeaders: function getAllResponseHeaders() {
+ if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
+ return "";
+ }
+
+ var headers = "";
+
+ for (var header in this.responseHeaders) {
+ if (this.responseHeaders.hasOwnProperty(header) &&
+ !/^Set-Cookie2?$/i.test(header)) {
+ headers += header + ": " + this.responseHeaders[header] + "\r\n";
+ }
+ }
+
+ return headers;
+ },
+
+ setResponseBody: function setResponseBody(body) {
+ verifyRequestSent(this);
+ verifyHeadersReceived(this);
+ verifyResponseBodyType(body);
+
+ var chunkSize = this.chunkSize || 10;
+ var index = 0;
+ this.responseText = "";
+
+ do {
+ if (this.async) {
+ this.readyStateChange(FakeXMLHttpRequest.LOADING);
+ }
+
+ this.responseText += body.substring(index, index + chunkSize);
+ index += chunkSize;
+ } while (index < body.length);
+
+ var type = this.getResponseHeader("Content-Type");
+
+ if (this.responseText &&
+ (!type || /(text\/xml)|(application\/xml)|(\+xml)/.test(type))) {
+ try {
+ this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText);
+ } catch (e) {
+ // Unable to parse XML - no biggie
+ }
+ }
+
+ if (this.async) {
+ this.readyStateChange(FakeXMLHttpRequest.DONE);
+ } else {
+ this.readyState = FakeXMLHttpRequest.DONE;
+ }
+ },
+
+ respond: function respond(status, headers, body) {
+ this.setResponseHeaders(headers || {});
+ this.status = typeof status == "number" ? status : 200;
+ this.statusText = FakeXMLHttpRequest.statusCodes[this.status];
+ this.setResponseBody(body || "");
+ if (typeof this.onload === "function"){
+ this.onload();
+ }
+
+ }
+ });
+
+ sinon.extend(FakeXMLHttpRequest, {
+ UNSENT: 0,
+ OPENED: 1,
+ HEADERS_RECEIVED: 2,
+ LOADING: 3,
+ DONE: 4
+ });
+
+ // Borrowed from JSpec
+ FakeXMLHttpRequest.parseXML = function parseXML(text) {
+ var xmlDoc;
+
+ if (typeof DOMParser != "undefined") {
+ var parser = new DOMParser();
+ xmlDoc = parser.parseFromString(text, "text/xml");
+ } else {
+ xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
+ xmlDoc.async = "false";
+ xmlDoc.loadXML(text);
+ }
+
+ return xmlDoc;
+ };
+
+ FakeXMLHttpRequest.statusCodes = {
+ 100: "Continue",
+ 101: "Switching Protocols",
+ 200: "OK",
+ 201: "Created",
+ 202: "Accepted",
+ 203: "Non-Authoritative Information",
+ 204: "No Content",
+ 205: "Reset Content",
+ 206: "Partial Content",
+ 300: "Multiple Choice",
+ 301: "Moved Permanently",
+ 302: "Found",
+ 303: "See Other",
+ 304: "Not Modified",
+ 305: "Use Proxy",
+ 307: "Temporary Redirect",
+ 400: "Bad Request",
+ 401: "Unauthorized",
+ 402: "Payment Required",
+ 403: "Forbidden",
+ 404: "Not Found",
+ 405: "Method Not Allowed",
+ 406: "Not Acceptable",
+ 407: "Proxy Authentication Required",
+ 408: "Request Timeout",
+ 409: "Conflict",
+ 410: "Gone",
+ 411: "Length Required",
+ 412: "Precondition Failed",
+ 413: "Request Entity Too Large",
+ 414: "Request-URI Too Long",
+ 415: "Unsupported Media Type",
+ 416: "Requested Range Not Satisfiable",
+ 417: "Expectation Failed",
+ 422: "Unprocessable Entity",
+ 500: "Internal Server Error",
+ 501: "Not Implemented",
+ 502: "Bad Gateway",
+ 503: "Service Unavailable",
+ 504: "Gateway Timeout",
+ 505: "HTTP Version Not Supported"
+ };
+
+ sinon.useFakeXMLHttpRequest = function () {
+ sinon.FakeXMLHttpRequest.restore = function restore(keepOnCreate) {
+ if (xhr.supportsXHR) {
+ global.XMLHttpRequest = xhr.GlobalXMLHttpRequest;
+ }
+
+ if (xhr.supportsActiveX) {
+ global.ActiveXObject = xhr.GlobalActiveXObject;
+ }
+
+ delete sinon.FakeXMLHttpRequest.restore;
+
+ if (keepOnCreate !== true) {
+ delete sinon.FakeXMLHttpRequest.onCreate;
+ }
+ };
+ if (xhr.supportsXHR) {
+ global.XMLHttpRequest = sinon.FakeXMLHttpRequest;
+ }
+
+ if (xhr.supportsActiveX) {
+ global.ActiveXObject = function ActiveXObject(objId) {
+ if (objId == "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) {
+
+ return new sinon.FakeXMLHttpRequest();
+ }
+
+ return new xhr.GlobalActiveXObject(objId);
+ };
+ }
+
+ return sinon.FakeXMLHttpRequest;
+ };
+
+ sinon.FakeXMLHttpRequest = FakeXMLHttpRequest;
+ })(this);
+
+ if (typeof module == "object" && typeof require == "function") {
+ module.exports = sinon;
+ }
+
+ /**
+ * @depend fake_xml_http_request.js
+ */
+ /*jslint eqeqeq: false, onevar: false, regexp: false, plusplus: false*/
+ /*global module, require, window*/
+ /**
+ * The Sinon "server" mimics a web server that receives requests from
+ * sinon.FakeXMLHttpRequest and provides an API to respond to those requests,
+ * both synchronously and asynchronously. To respond synchronuously, canned
+ * answers have to be provided upfront.
+ *
+ * @author Christian Johansen (christian(a)cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+ if (typeof sinon == "undefined") {
+ var sinon = {};
+ }
+
+ sinon.fakeServer = (function () {
+ var push = [].push;
+ function F() {}
+
+ function create(proto) {
+ F.prototype = proto;
+ return new F();
+ }
+
+ function responseArray(handler) {
+ var response = handler;
+
+ if (Object.prototype.toString.call(handler) != "[object Array]") {
+ response = [200, {}, handler];
+ }
+
+ if (typeof response[2] != "string") {
+ throw new TypeError("Fake server response body should be string, but was " +
+ typeof response[2]);
+ }
+
+ return response;
+ }
+
+ var wloc = typeof window !== "undefined" ? window.location : {};
+ var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host);
+
+ function matchOne(response, reqMethod, reqUrl) {
+ var rmeth = response.method;
+ var matchMethod = !rmeth || rmeth.toLowerCase() == reqMethod.toLowerCase();
+ var url = response.url;
+ var matchUrl = !url || url == reqUrl || (typeof url.test == "function" && url.test(reqUrl));
+
+ return matchMethod && matchUrl;
+ }
+
+ function match(response, request) {
+ var requestMethod = this.getHTTPMethod(request);
+ var requestUrl = request.url;
+
+ if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) {
+ requestUrl = requestUrl.replace(rCurrLoc, "");
+ }
+
+ if (matchOne(response, this.getHTTPMethod(request), requestUrl)) {
+ if (typeof response.response == "function") {
+ var ru = response.url;
+ var args = [request].concat(!ru ? [] : requestUrl.match(ru).slice(1));
+ return response.response.apply(response, args);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ function log(response, request) {
+ var str;
+
+ str = "Request:\n" + sinon.format(request) + "\n\n";
+ str += "Response:\n" + sinon.format(response) + "\n\n";
+
+ sinon.log(str);
+ }
+
+ return {
+ create: function () {
+ var server = create(this);
+ this.xhr = sinon.useFakeXMLHttpRequest();
+ server.requests = [];
+
+ this.xhr.onCreate = function (xhrObj) {
+ server.addRequest(xhrObj);
+ };
+
+ return server;
+ },
+
+ addRequest: function addRequest(xhrObj) {
+ var server = this;
+ push.call(this.requests, xhrObj);
+
+ xhrObj.onSend = function () {
+ server.handleRequest(this);
+ };
+
+ if (this.autoRespond && !this.responding) {
+ setTimeout(function () {
+ server.responding = false;
+ server.respond();
+ }, this.autoRespondAfter || 10);
+
+ this.responding = true;
+ }
+ },
+
+ getHTTPMethod: function getHTTPMethod(request) {
+ if (this.fakeHTTPMethods && /post/i.test(request.method)) {
+ var matches = (request.requestBody || "").match(/_method=([^\b;]+)/);
+ return !!matches ? matches[1] : request.method;
+ }
+
+ return request.method;
+ },
+
+ handleRequest: function handleRequest(xhr) {
+ if (xhr.async) {
+ if (!this.queue) {
+ this.queue = [];
+ }
+
+ push.call(this.queue, xhr);
+ } else {
+ this.processRequest(xhr);
+ }
+ },
+
+ respondWith: function respondWith(method, url, body) {
+ if (arguments.length == 1 && typeof method != "function") {
+ this.response = responseArray(method);
+ return;
+ }
+
+ if (!this.responses) { this.responses = []; }
+
+ if (arguments.length == 1) {
+ body = method;
+ url = method = null;
+ }
+
+ if (arguments.length == 2) {
+ body = url;
+ url = method;
+ method = null;
+ }
+
+ push.call(this.responses, {
+ method: method,
+ url: url,
+ response: typeof body == "function" ? body : responseArray(body)
+ });
+ },
+
+ respond: function respond() {
+ if (arguments.length > 0) this.respondWith.apply(this, arguments);
+ var queue = this.queue || [];
+ var request;
+
+ while(request = queue.shift()) {
+ this.processRequest(request);
+ }
+ },
+
+ processRequest: function processRequest(request) {
+ try {
+ if (request.aborted) {
+ return;
+ }
+
+ var response = this.response || [404, {}, ""];
+
+ if (this.responses) {
+ for (var i = 0, l = this.responses.length; i < l; i++) {
+ if (match.call(this, this.responses[i], request)) {
+ response = this.responses[i].response;
+ break;
+ }
+ }
+ }
+
+ if (request.readyState != 4) {
+ log(response, request);
+
+ request.respond(response[0], response[1], response[2]);
+ }
+ } catch (e) {
+ sinon.logError("Fake server request processing", e);
+ }
+ },
+
+ restore: function restore() {
+ return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments);
+ }
+ };
+ }());
+
+ if (typeof module == "object" && typeof require == "function") {
+ module.exports = sinon;
+ }
+
+ /**
+ * @depend fake_server.js
+ * @depend fake_timers.js
+ */
+ /*jslint browser: true, eqeqeq: false, onevar: false*/
+ /*global sinon*/
+ /**
+ * Add-on for sinon.fakeServer that automatically handles a fake timer along with
+ * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery
+ * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead,
+ * it polls the object for completion with setInterval. Dispite the direct
+ * motivation, there is nothing jQuery-specific in this file, so it can be used
+ * in any environment where the ajax implementation depends on setInterval or
+ * setTimeout.
+ *
+ * @author Christian Johansen (christian(a)cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+ (function () {
+ function Server() {}
+ Server.prototype = sinon.fakeServer;
+
+ sinon.fakeServerWithClock = new Server();
+
+ sinon.fakeServerWithClock.addRequest = function addRequest(xhr) {
+ if (xhr.async) {
+ if (typeof setTimeout.clock == "object") {
+ this.clock = setTimeout.clock;
+ } else {
+ this.clock = sinon.useFakeTimers();
+ this.resetClock = true;
+ }
+
+ if (!this.longestTimeout) {
+ var clockSetTimeout = this.clock.setTimeout;
+ var clockSetInterval = this.clock.setInterval;
+ var server = this;
+
+ this.clock.setTimeout = function (fn, timeout) {
+ server.longestTimeout = Math.max(timeout, server.longestTimeout || 0);
+
+ return clockSetTimeout.apply(this, arguments);
+ };
+
+ this.clock.setInterval = function (fn, timeout) {
+ server.longestTimeout = Math.max(timeout, server.longestTimeout || 0);
+
+ return clockSetInterval.apply(this, arguments);
+ };
+ }
+ }
+
+ return sinon.fakeServer.addRequest.call(this, xhr);
+ };
+
+ sinon.fakeServerWithClock.respond = function respond() {
+ var returnVal = sinon.fakeServer.respond.apply(this, arguments);
+
+ if (this.clock) {
+ this.clock.tick(this.longestTimeout || 0);
+ this.longestTimeout = 0;
+
+ if (this.resetClock) {
+ this.clock.restore();
+ this.resetClock = false;
+ }
+ }
+
+ return returnVal;
+ };
+
+ sinon.fakeServerWithClock.restore = function restore() {
+ if (this.clock) {
+ this.clock.restore();
+ }
+
+ return sinon.fakeServer.restore.apply(this, arguments);
+ };
+ }());
+
+ /**
+ * @depend ../sinon.js
+ * @depend collection.js
+ * @depend util/fake_timers.js
+ * @depend util/fake_server_with_clock.js
+ */
+ /*jslint eqeqeq: false, onevar: false, plusplus: false*/
+ /*global require, module*/
+ /**
+ * Manages fake collections as well as fake utilities such as Sinon's
+ * timers and fake XHR implementation in one convenient object.
+ *
+ * @author Christian Johansen (christian(a)cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+ if (typeof module == "object" && typeof require == "function") {
+ var sinon = require("../sinon");
+ sinon.extend(sinon, require("./util/fake_timers"));
+ }
+
+ (function () {
+ var push = [].push;
+
+ function exposeValue(sandbox, config, key, value) {
+ if (!value) {
+ return;
+ }
+
+ if (config.injectInto) {
+ config.injectInto[key] = value;
+ } else {
+ push.call(sandbox.args, value);
+ }
+ }
+
+ function prepareSandboxFromConfig(config) {
+ var sandbox = sinon.create(sinon.sandbox);
+
+ if (config.useFakeServer) {
+ if (typeof config.useFakeServer == "object") {
+ sandbox.serverPrototype = config.useFakeServer;
+ }
+
+ sandbox.useFakeServer();
+ }
+
+ if (config.useFakeTimers) {
+ if (typeof config.useFakeTimers == "object") {
+ sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers);
+ } else {
+ sandbox.useFakeTimers();
+ }
+ }
+
+ return sandbox;
+ }
+
+ sinon.sandbox = sinon.extend(sinon.create(sinon.collection), {
+ useFakeTimers: function useFakeTimers() {
+ this.clock = sinon.useFakeTimers.apply(sinon, arguments);
+
+ return this.add(this.clock);
+ },
+
+ serverPrototype: sinon.fakeServer,
+
+ useFakeServer: function useFakeServer() {
+ var proto = this.serverPrototype || sinon.fakeServer;
+
+ if (!proto || !proto.create) {
+ return null;
+ }
+
+ this.server = proto.create();
+ return this.add(this.server);
+ },
+
+ inject: function (obj) {
+ sinon.collection.inject.call(this, obj);
+
+ if (this.clock) {
+ obj.clock = this.clock;
+ }
+
+ if (this.server) {
+ obj.server = this.server;
+ obj.requests = this.server.requests;
+ }
+
+ return obj;
+ },
+
+ create: function (config) {
+ if (!config) {
+ return sinon.create(sinon.sandbox);
+ }
+
+ var sandbox = prepareSandboxFromConfig(config);
+ sandbox.args = sandbox.args || [];
+ var prop, value, exposed = sandbox.inject({});
+
+ if (config.properties) {
+ for (var i = 0, l = config.properties.length; i < l; i++) {
+ prop = config.properties[i];
+ value = exposed[prop] || prop == "sandbox" && sandbox;
+ exposeValue(sandbox, config, prop, value);
+ }
+ } else {
+ exposeValue(sandbox, config, "sandbox", value);
+ }
+
+ return sandbox;
+ }
+ });
+
+ sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer;
+
+ if (typeof module == "object" && typeof require == "function") {
+ module.exports = sinon.sandbox;
+ }
+ }());
+
+ /**
+ * @depend ../sinon.js
+ * @depend stub.js
+ * @depend mock.js
+ * @depend sandbox.js
+ */
+ /*jslint eqeqeq: false, onevar: false, forin: true, plusplus: false*/
+ /*global module, require, sinon*/
+ /**
+ * Test function, sandboxes fakes
+ *
+ * @author Christian Johansen (christian(a)cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+ (function (sinon) {
+ var commonJSModule = typeof module == "object" && typeof require == "function";
+
+ if (!sinon && commonJSModule) {
+ sinon = require("../sinon");
+ }
+
+ if (!sinon) {
+ return;
+ }
+
+ function test(callback) {
+ var type = typeof callback;
+
+ if (type != "function") {
+ throw new TypeError("sinon.test needs to wrap a test function, got " + type);
+ }
+
+ return function () {
+ var config = sinon.getConfig(sinon.config);
+ config.injectInto = config.injectIntoThis && this || config.injectInto;
+ var sandbox = sinon.sandbox.create(config);
+ var exception, result;
+ var args = Array.prototype.slice.call(arguments).concat(sandbox.args);
+
+ try {
+ result = callback.apply(this, args);
+ } catch (e) {
+ exception = e;
+ }
+
+ if (typeof exception !== "undefined") {
+ sandbox.restore();
+ throw exception;
+ }
+ else {
+ sandbox.verifyAndRestore();
+ }
+
+ return result;
+ };
+ }
+
+ test.config = {
+ injectIntoThis: true,
+ injectInto: null,
+ properties: ["spy", "stub", "mock", "clock", "server", "requests"],
+ useFakeTimers: true,
+ useFakeServer: true
+ };
+
+ if (commonJSModule) {
+ module.exports = test;
+ } else {
+ sinon.test = test;
+ }
+ }(typeof sinon == "object" && sinon || null));
+
+ /**
+ * @depend ../sinon.js
+ * @depend test.js
+ */
+ /*jslint eqeqeq: false, onevar: false, eqeqeq: false*/
+ /*global module, require, sinon*/
+ /**
+ * Test case, sandboxes all test functions
+ *
+ * @author Christian Johansen (christian(a)cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+ (function (sinon) {
+ var commonJSModule = typeof module == "object" && typeof require == "function";
+
+ if (!sinon && commonJSModule) {
+ sinon = require("../sinon");
+ }
+
+ if (!sinon || !Object.prototype.hasOwnProperty) {
+ return;
+ }
+
+ function createTest(property, setUp, tearDown) {
+ return function () {
+ if (setUp) {
+ setUp.apply(this, arguments);
+ }
+
+ var exception, result;
+
+ try {
+ result = property.apply(this, arguments);
+ } catch (e) {
+ exception = e;
+ }
+
+ if (tearDown) {
+ tearDown.apply(this, arguments);
+ }
+
+ if (exception) {
+ throw exception;
+ }
+
+ return result;
+ };
+ }
+
+ function testCase(tests, prefix) {
+ /*jsl:ignore*/
+ if (!tests || typeof tests != "object") {
+ throw new TypeError("sinon.testCase needs an object with test functions");
+ }
+ /*jsl:end*/
+
+ prefix = prefix || "test";
+ var rPrefix = new RegExp("^" + prefix);
+ var methods = {}, testName, property, method;
+ var setUp = tests.setUp;
+ var tearDown = tests.tearDown;
+
+ for (testName in tests) {
+ if (tests.hasOwnProperty(testName)) {
+ property = tests[testName];
+
+ if (/^(setUp|tearDown)$/.test(testName)) {
+ continue;
+ }
+
+ if (typeof property == "function" && rPrefix.test(testName)) {
+ method = property;
+
+ if (setUp || tearDown) {
+ method = createTest(property, setUp, tearDown);
+ }
+
+ methods[testName] = sinon.test(method);
+ } else {
+ methods[testName] = tests[testName];
+ }
+ }
+ }
+
+ return methods;
+ }
+
+ if (commonJSModule) {
+ module.exports = testCase;
+ } else {
+ sinon.testCase = testCase;
+ }
+ }(typeof sinon == "object" && sinon || null));
+
+ /**
+ * @depend ../sinon.js
+ * @depend stub.js
+ */
+ /*jslint eqeqeq: false, onevar: false, nomen: false, plusplus: false*/
+ /*global module, require, sinon*/
+ /**
+ * Assertions matching the test spy retrieval interface.
+ *
+ * @author Christian Johansen (christian(a)cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+ (function (sinon, global) {
+ var commonJSModule = typeof module == "object" && typeof require == "function";
+ var slice = Array.prototype.slice;
+ var assert;
+
+ if (!sinon && commonJSModule) {
+ sinon = require("../sinon");
+ }
+
+ if (!sinon) {
+ return;
+ }
+
+ function verifyIsStub() {
+ var method;
+
+ for (var i = 0, l = arguments.length; i < l; ++i) {
+ method = arguments[i];
+
+ if (!method) {
+ assert.fail("fake is not a spy");
+ }
+
+ if (typeof method != "function") {
+ assert.fail(method + " is not a function");
+ }
+
+ if (typeof method.getCall != "function") {
+ assert.fail(method + " is not stubbed");
+ }
+ }
+ }
+
+ function failAssertion(object, msg) {
+ object = object || global;
+ var failMethod = object.fail || assert.fail;
+ failMethod.call(object, msg);
+ }
+
+ function mirrorPropAsAssertion(name, method, message) {
+ if (arguments.length == 2) {
+ message = method;
+ method = name;
+ }
+
+ assert[name] = function (fake) {
+ verifyIsStub(fake);
+
+ var args = slice.call(arguments, 1);
+ var failed = false;
+
+ if (typeof method == "function") {
+ failed = !method(fake);
+ } else {
+ failed = typeof fake[method] == "function" ?
+ !fake[method].apply(fake, args) : !fake[method];
+ }
+
+ if (failed) {
+ failAssertion(this, fake.printf.apply(fake, [message].concat(args)));
+ } else {
+ assert.pass(name);
+ }
+ };
+ }
+
+ function exposedName(prefix, prop) {
+ return !prefix || /^fail/.test(prop) ? prop :
+ prefix + prop.slice(0, 1).toUpperCase() + prop.slice(1);
+ };
+
+ assert = {
+ failException: "AssertError",
+
+ fail: function fail(message) {
+ var error = new Error(message);
+ error.name = this.failException || assert.failException;
+
+ throw error;
+ },
+
+ pass: function pass(assertion) {},
+
+ callOrder: function assertCallOrder() {
+ verifyIsStub.apply(null, arguments);
+ var expected = "", actual = "";
+
+ if (!sinon.calledInOrder(arguments)) {
+ try {
+ expected = [].join.call(arguments, ", ");
+ var calls = slice.call(arguments);
+ var i = calls.length;
+ while (i) {
+ if (!calls[--i].called) {
+ calls.splice(i, 1);
+ }
+ }
+ actual = sinon.orderByFirstCall(calls).join(", ");
+ } catch (e) {
+ // If this fails, we'll just fall back to the blank string
+ }
+
+ failAssertion(this, "expected " + expected + " to be " +
+ "called in order but were called as " + actual);
+ } else {
+ assert.pass("callOrder");
+ }
+ },
+
+ callCount: function assertCallCount(method, count) {
+ verifyIsStub(method);
+
+ if (method.callCount != count) {
+ var msg = "expected %n to be called " + sinon.timesInWords(count) +
+ " but was called %c%C";
+ failAssertion(this, method.printf(msg));
+ } else {
+ assert.pass("callCount");
+ }
+ },
+
+ expose: function expose(target, options) {
+ if (!target) {
+ throw new TypeError("target is null or undefined");
+ }
+
+ var o = options || {};
+ var prefix = typeof o.prefix == "undefined" && "assert" || o.prefix;
+ var includeFail = typeof o.includeFail == "undefined" || !!o.includeFail;
+
+ for (var method in this) {
+ if (method != "export" && (includeFail || !/^(fail)/.test(method))) {
+ target[exposedName(prefix, method)] = this[method];
+ }
+ }
+
+ return target;
+ }
+ };
+
+ mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called");
+ mirrorPropAsAssertion("notCalled", function (spy) { return !spy.called; },
+ "expected %n to not have been called but was called %c%C");
+ mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C");
+ mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C");
+ mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C");
+ mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t");
+ mirrorPropAsAssertion("alwaysCalledOn", "expected %n to always be called with %1 as this but was called with %t");
+ mirrorPropAsAssertion("calledWithNew", "expected %n to be called with new");
+ mirrorPropAsAssertion("alwaysCalledWithNew", "expected %n to always be called with new");
+ mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %*%C");
+ mirrorPropAsAssertion("calledWithMatch", "expected %n to be called with match %*%C");
+ mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %*%C");
+ mirrorPropAsAssertion("alwaysCalledWithMatch", "expected %n to always be called with match %*%C");
+ mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %*%C");
+ mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %*%C");
+ mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C");
+ mirrorPropAsAssertion("neverCalledWithMatch", "expected %n to never be called with match %*%C");
+ mirrorPropAsAssertion("threw", "%n did not throw exception%C");
+ mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C");
+
+ if (commonJSModule) {
+ module.exports = assert;
+ } else {
+ sinon.assert = assert;
+ }
+ }(typeof sinon == "object" && sinon || null, typeof window != "undefined" ? window : (typeof self != "undefined") ? self : global));
+
+ return sinon;}.call(typeof window != 'undefined' && window || {}));
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/underscore.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/underscore.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/lib/underscore.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,6 @@
+// Underscore.js 1.5.2
+// http://underscorejs.org
+// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+// Underscore may be freely distributed under the MIT license.
+(function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,g=e.filter,d=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,w=Object.keys,_=i.bind,j=function(n){return n instanceof j?n:this instanceof j?(this._wrapped=n,void 0):new j(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=j),exports._=j):n._=j,j.VERSION="1.5.2";var A=j.each=j.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a=j.keys(n),u=0,i=a.length;i>u;u++)if(t.call(e,n[a[u]],a[u],n)===r)return};j.map=j.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e.push(t.call(r,n,u,i))}),e)};var E="Reduce of empty array with no initial value";j.reduce=j.foldl=j.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=j.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(E);return r},j.reduceRight=j.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=j.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=j.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(E);return r},j.find=j.detect=function(n,t,r){var e;return O(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},j.filter=j.select=function(n,t,r){var e=[];return null==n?e:g&&n.filter===g?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&e.push(n)}),e)},j.reject=function(n,t,r){return j.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},j.every=j.all=function(n,t,e){t||(t=j.identity);var u=!0;return null==n?u:d&&n.every===d?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var O=j.some=j.any=function(n,t,e){t||(t=j.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};j.contains=j.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:O(n,function(n){return n===t})},j.invoke=function(n,t){var r=o.call(arguments,2),e=j.isFunction(t);return j.map(n,function(n){return(e?t:n[t]).apply(n,r)})},j.pluck=function(n,t){return j.map(n,function(n){return n[t]})},j.where=function(n,t,r){return j.isEmpty(t)?r?void 0:[]:j[r?"find":"filter"](n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},j.findWhere=function(n,t){return j.where(n,t,!0)},j.max=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.max.apply(Math,n);if(!t&&j.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>e.computed&&(e={value:n,computed:a})}),e.value},j.min=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.min.apply(Math,n);if(!t&&j.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a<e.computed&&(e={value:n,computed:a})}),e.value},j.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=j.random(r++),e[r-1]=e[t],e[t]=n}),e},j.sample=function(n,t,r){return arguments.length<2||r?n[j.random(n.length-1)]:j.shuffle(n).slice(0,Math.max(0,t))};var k=function(n){return j.isFunction(n)?n:function(t){return t[n]}};j.sortBy=function(n,t,r){var e=k(t);return j.pluck(j.map(n,function(n,t,u){return{value:n,index:t,criteria:e.call(r,n,t,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={},i=null==r?j.identity:k(r);return A(t,function(r,a){var o=i.call(e,r,a,t);n(u,o,r)}),u}};j.groupBy=F(function(n,t,r){(j.has(n,t)?n[t]:n[t]=[]).push(r)}),j.indexBy=F(function(n,t,r){n[t]=r}),j.countBy=F(function(n,t){j.has(n,t)?n[t]++:n[t]=1}),j.sortedIndex=function(n,t,r,e){r=null==r?j.identity:k(r);for(var u=r.call(e,t),i=0,a=n.length;a>i;){var o=i+a>>>1;r.call(e,n[o])<u?i=o+1:a=o}return i},j.toArray=function(n){return n?j.isArray(n)?o.call(n):n.length===+n.length?j.map(n,j.identity):j.values(n):[]},j.size=function(n){return null==n?0:n.length===+n.length?n.length:j.keys(n).length},j.first=j.head=j.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:o.call(n,0,t)},j.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},j.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},j.rest=j.tail=j.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},j.compact=function(n){return j.filter(n,j.identity)};var M=function(n,t,r){return t&&j.every(n,j.isArray)?c.apply(r,n):(A(n,function(n){j.isArray(n)||j.isArguments(n)?t?a.apply(r,n):M(n,t,r):r.push(n)}),r)};j.flatten=function(n,t){return M(n,t,[])},j.without=function(n){return j.difference(n,o.call(arguments,1))},j.uniq=j.unique=function(n,t,r,e){j.isFunction(t)&&(e=r,r=t,t=!1);var u=r?j.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:j.contains(a,r))||(a.push(r),i.push(n[e]))}),i},j.union=function(){return j.uniq(j.flatten(arguments,!0))},j.intersection=function(n){var t=o.call(arguments,1);return j.filter(j.uniq(n),function(n){return j.every(t,function(t){return j.indexOf(t,n)>=0})})},j.difference=function(n){var t=c.apply(e,o.call(arguments,1));return j.filter(n,function(n){return!j.contains(t,n)})},j.zip=function(){for(var n=j.max(j.pluck(arguments,"length").concat(0)),t=new Array(n),r=0;n>r;r++)t[r]=j.pluck(arguments,""+r);return t},j.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},j.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=j.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},j.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},j.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=new Array(e);e>u;)i[u++]=n,n+=r;return i};var R=function(){};j.bind=function(n,t){var r,e;if(_&&n.bind===_)return _.apply(n,o.call(arguments,1));if(!j.isFunction(n))throw new TypeError;return r=o.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(o.call(arguments)));R.prototype=n.prototype;var u=new R;R.prototype=null;var i=n.apply(u,r.concat(o.call(arguments)));return Object(i)===i?i:u}},j.partial=function(n){var t=o.call(arguments,1);return function(){return n.apply(this,t.concat(o.call(arguments)))}},j.bindAll=function(n){var t=o.call(arguments,1);if(0===t.length)throw new Error("bindAll must be passed function names");return A(t,function(t){n[t]=j.bind(n[t],n)}),n},j.memoize=function(n,t){var r={};return t||(t=j.identity),function(){var e=t.apply(this,arguments);return j.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},j.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},j.defer=function(n){return j.delay.apply(j,[n,1].concat(o.call(arguments,1)))},j.throttle=function(n,t,r){var e,u,i,a=null,o=0;r||(r={});var c=function(){o=r.leading===!1?0:new Date,a=null,i=n.apply(e,u)};return function(){var l=new Date;o||r.leading!==!1||(o=l);var f=t-(l-o);return e=this,u=arguments,0>=f?(clearTimeout(a),a=null,o=l,i=n.apply(e,u)):a||r.trailing===!1||(a=setTimeout(c,f)),i}},j.debounce=function(n,t,r){var e,u,i,a,o;return function(){i=this,u=arguments,a=new Date;var c=function(){var l=new Date-a;t>l?e=setTimeout(c,t-l):(e=null,r||(o=n.apply(i,u)))},l=r&&!e;return e||(e=setTimeout(c,t)),l&&(o=n.apply(i,u)),o}},j.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},j.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},j.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},j.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},j.keys=w||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)j.has(n,r)&&t.push(r);return t},j.values=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},j.pairs=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},j.invert=function(n){for(var t={},r=j.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},j.functions=j.methods=function(n){var t=[];for(var r in n)j.isFunction(n[r])&&t.push(r);return t.sort()},j.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},j.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},j.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)j.contains(r,u)||(t[u]=n[u]);return t},j.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]===void 0&&(n[r]=t[r])}),n},j.clone=function(n){return j.isObject(n)?j.isArray(n)?n.slice():j.extend({},n):n},j.tap=function(n,t){return t(n),n};var S=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof j&&(n=n._wrapped),t instanceof j&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==String(t);case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;var a=n.constructor,o=t.constructor;if(a!==o&&!(j.isFunction(a)&&a instanceof a&&j.isFunction(o)&&o instanceof o))return!1;r.push(n),e.push(t);var c=0,f=!0;if("[object Array]"==u){if(c=n.length,f=c==t.length)for(;c--&&(f=S(n[c],t[c],r,e)););}else{for(var s in n)if(j.has(n,s)&&(c++,!(f=j.has(t,s)&&S(n[s],t[s],r,e))))break;if(f){for(s in t)if(j.has(t,s)&&!c--)break;f=!c}}return r.pop(),e.pop(),f};j.isEqual=function(n,t){return S(n,t,[],[])},j.isEmpty=function(n){if(null==n)return!0;if(j.isArray(n)||j.isString(n))return 0===n.length;for(var t in n)if(j.has(n,t))return!1;return!0},j.isElement=function(n){return!(!n||1!==n.nodeType)},j.isArray=x||function(n){return"[object Array]"==l.call(n)},j.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){j["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),j.isArguments(arguments)||(j.isArguments=function(n){return!(!n||!j.has(n,"callee"))}),"function"!=typeof/./&&(j.isFunction=function(n){return"function"==typeof n}),j.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},j.isNaN=function(n){return j.isNumber(n)&&n!=+n},j.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},j.isNull=function(n){return null===n},j.isUndefined=function(n){return n===void 0},j.has=function(n,t){return f.call(n,t)},j.noConflict=function(){return n._=t,this},j.identity=function(n){return n},j.times=function(n,t,r){for(var e=Array(Math.max(0,n)),u=0;n>u;u++)e[u]=t.call(r,u);return e},j.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))};var I={escape:{"&":"&","<":"<",">":">",'"':""","'":"'"}};I.unescape=j.invert(I.escape);var T={escape:new RegExp("["+j.keys(I.escape).join("")+"]","g"),unescape:new RegExp("("+j.keys(I.unescape).join("|")+")","g")};j.each(["escape","unescape"],function(n){j[n]=function(t){return null==t?"":(""+t).replace(T[n],function(t){return I[n][t]})}}),j.result=function(n,t){if(null==n)return void 0;var r=n[t];return j.isFunction(r)?r.call(n):r},j.mixin=function(n){A(j.functions(n),function(t){var r=j[t]=n[t];j.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),z.call(this,r.apply(j,n))}})};var N=0;j.uniqueId=function(n){var t=++N+"";return n?n+t:t},j.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var q=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\t|\u2028|\u2029/g;j.template=function(n,t,r){var e;r=j.defaults({},r,j.templateSettings);var u=new RegExp([(r.escape||q).source,(r.interpolate||q).source,(r.evaluate||q).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(D,function(n){return"\\"+B[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=new Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,j);var c=function(n){return e.call(this,n,j)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},j.chain=function(n){return j(n).chain()};var z=function(n){return this._chain?j(n).chain():n};j.mixin(j),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];j.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],z.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];j.prototype[n]=function(){return z.call(this,t.apply(this._wrapped,arguments))}}),j.extend(j.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this);
+//# sourceMappingURL=underscore-min.map
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/spec/bindingSpec.js
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/spec/bindingSpec.js (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test/spec/bindingSpec.js 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,120 @@
+// use http://jonathan.tang.name/files/js_keycode/test_keycode.html
+// to discover key codes
+describe("binding functions to key combinations", function() {
+
+ beforeEach(function() {
+
+ this.callbackFn = sinon.spy();
+
+ this.fixture = $('<div id="container"></div>');
+ $('body').append(this.fixture);
+
+ this.createInputEl = function(type, id) {
+ var $el = $('<input type="' + type + '" id="' + id + '"/>');
+ this.fixture.append($el);
+ return $el;
+ };
+
+ this.text_input_types = ["text", "password", "number", "email", "url", "range", "date", "month", "week",
+ "time", "datetime", "datetime-local", "search", "color", "tel"];
+
+ // creates new key event
+ this.createKeyUpEvent = function(keyCode) {
+
+ var event = jQuery.Event('keyup');
+ event.keyCode = keyCode;
+ event.which = keyCode;
+
+ return event;
+ };
+ });
+
+ afterEach(function(){
+ this.fixture.remove();
+ $(document).unbind();
+ });
+
+ it("should bind the 'return' key to the document and trigger the bound callback", function() {
+
+ $(document).bind('keyup', 'return', this.callbackFn);
+
+ var event = this.createKeyUpEvent(13);
+ $(document).trigger(event);
+ sinon.assert.calledOnce(this.callbackFn);
+
+ });
+
+ it("should bind the 'alt+s' keys and call the callback handler function", function() {
+
+ $(document).bind('keyup', 'alt+a', this.callbackFn);
+ var event = this.createKeyUpEvent(65);
+ event.altKey = true;
+ $(document).trigger(event);
+ sinon.assert.calledOnce(this.callbackFn);
+ });
+
+ it("should bind the 'shift+pagedown' keys and call the callback handler function", function() {
+
+ $(document).bind('keyup', 'shift+pagedown', this.callbackFn);
+ var event = this.createKeyUpEvent(34);
+ event.shiftKey = true;
+ $(document).trigger(event);
+ sinon.assert.calledOnce(this.callbackFn);
+ });
+
+ it("should bind the 'alt+shift+a' with a namespace, trigger the callback handler and unbind correctly", function() {
+
+ var spy = sinon.spy();
+
+ $(document).bind('keyup.a', 'alt+shift+a', spy);
+ $(document).bind('keyup.b', 'alt+shift+a', spy);
+ $(document).unbind('keyup.a'); // remove first binding, leaving only second
+
+ var event = this.createKeyUpEvent(65);
+ event.altKey = true;
+ event.shiftKey = true;
+ $(document).trigger(event);
+
+ // ensure only second binding is still in effect
+ sinon.assert.calledOnce(spy);
+ });
+
+ it("should not trigger event handler callbacks bound to any standard input types if not bound directly", function() {
+
+ var i = 0;
+
+ _.each(this.text_input_types, function(input_type) {
+ var spy = sinon.spy();
+
+ var $el = this.createInputEl(input_type, ++i);
+ $(document).bind('keyup', 'a', spy);
+
+ var event = this.createKeyUpEvent('65');
+ $el.trigger(event);
+
+ sinon.assert.notCalled(spy);
+ $(document).unbind();
+ $el.remove();
+
+ }, this);
+ });
+
+ it("should bind and trigger events from an input element if bound directly", function() {
+
+ var i = 0;
+
+ _.each(this.text_input_types, function(input_type) {
+ var spy = sinon.spy();
+
+ var $el = this.createInputEl(input_type, ++i);
+ $el.bind('keyup', 'a', spy);
+
+ var event = this.createKeyUpEvent('65');
+ $el.trigger(event);
+
+ sinon.assert.calledOnce(spy);
+ $el.remove();
+
+ }, this);
+ });
+});
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-01.html
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-01.html (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-01.html 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,149 @@
+<html>
+ <head>
+ <style>
+ * {font-family: Helvetica, Verdana, Arial; font-size:0.95em}
+ .eventNotifier{width: 100px; float: left; color:navy; border: dotted 1px navy; padding: 4px; background-color:white; margin:3px}
+ .dirty{border: solid 1px #0ca2ff; color:white; background-color:#0ca2ff}
+
+ </style>
+ <script src="jquery-1.4.2.js"></script>
+ <script src="jquery.hotkeys.js"></script>
+ <script>
+ //This page is a result of an autogenerated content made by running test.html with firefox.
+ function domo(){
+ jQuery('#platform-details').html('<code>' + navigator.userAgent + '</code>');
+
+ var elements = [
+ "esc","tab","space","return","backspace","scroll","capslock","numlock","insert","home","del","end","pageup","pagedown",
+ "left","up","right","down",
+ "f1","f2","f3","f4","f5","f6","f7","f8","f9","f10","f11","f12",
+ "1","2","3","4","5","6","7","8","9","0",
+ "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
+ "Ctrl+a","Ctrl+b","Ctrl+c","Ctrl+d","Ctrl+e","Ctrl+f","Ctrl+g","Ctrl+h","Ctrl+i","Ctrl+j","Ctrl+k","Ctrl+l","Ctrl+m",
+ "Ctrl+n","Ctrl+o","Ctrl+p","Ctrl+q","Ctrl+r","Ctrl+s","Ctrl+t","Ctrl+u","Ctrl+v","Ctrl+w","Ctrl+x","Ctrl+y","Ctrl+z",
+ "Shift+a","Shift+b","Shift+c","Shift+d","Shift+e","Shift+f","Shift+g","Shift+h","Shift+i","Shift+j","Shift+k","Shift+l",
+ "Shift+m","Shift+n","Shift+o","Shift+p","Shift+q","Shift+r","Shift+s","Shift+t","Shift+u","Shift+v","Shift+w","Shift+x",
+ "Shift+y","Shift+z",
+ "Alt+a","Alt+b","Alt+c","Alt+d","Alt+e","Alt+f","Alt+g","Alt+h","Alt+i","Alt+j","Alt+k","Alt+l",
+ "Alt+m","Alt+n","Alt+o","Alt+p","Alt+q","Alt+r","Alt+s","Alt+t","Alt+u","Alt+v","Alt+w","Alt+x","Alt+y","Alt+z",
+ "Ctrl+esc","Ctrl+tab","Ctrl+space","Ctrl+return","Ctrl+backspace","Ctrl+scroll","Ctrl+capslock","Ctrl+numlock",
+ "Ctrl+insert","Ctrl+home","Ctrl+del","Ctrl+end","Ctrl+pageup","Ctrl+pagedown","Ctrl+left","Ctrl+up","Ctrl+right",
+ "Ctrl+down",
+ "Ctrl+f1","Ctrl+f2","Ctrl+f3","Ctrl+f4","Ctrl+f5","Ctrl+f6","Ctrl+f7","Ctrl+f8","Ctrl+f9","Ctrl+f10","Ctrl+f11","Ctrl+f12",
+ "Shift+esc","Shift+tab","Shift+space","Shift+return","Shift+backspace","Shift+scroll","Shift+capslock","Shift+numlock",
+ "Shift+insert","Shift+home","Shift+del","Shift+end","Shift+pageup","Shift+pagedown","Shift+left","Shift+up",
+ "Shift+right","Shift+down",
+ "Shift+f1","Shift+f2","Shift+f3","Shift+f4","Shift+f5","Shift+f6","Shift+f7","Shift+f8","Shift+f9","Shift+f10","Shift+f11","Shift+f12",
+ "Alt+esc","Alt+tab","Alt+space","Alt+return","Alt+backspace","Alt+scroll","Alt+capslock","Alt+numlock",
+ "Alt+insert","Alt+home","Alt+del","Alt+end","Alt+pageup","Alt+pagedown","Alt+left","Alt+up","Alt+right","Alt+down",
+ "Alt+f1","Alt+f2","Alt+f3","Alt+f4","Alt+f5","Alt+f6","Alt+f7","Alt+f8","Alt+f9","Alt+f10","Alt+f11","Alt+f12"
+ ];
+
+ // the fetching...
+ $.each(elements, function(i, e) { // i is element index. e is element as text.
+ var newElement = ( /[\+]+/.test(elements[i]) ) ? elements[i].replace("+","_") : elements[i];
+
+ // Binding keys
+ $(document).bind('keydown', elements[i], function assets() {
+ $('#_'+ newElement).addClass("dirty");
+ return false;
+ });
+ });
+
+ }
+
+ jQuery(document).ready(domo);
+
+ </script>
+ </head>
+
+ <body>
+ <h1>jQuery.HotKeys.Testing.</h1>
+ <h2>Testing Platform:</h2>
+ <div id="platform-details"></div>
+ <h2>Project Home: <a href="http://code.google.com/p/js-hotkeys/">http://code.google.com/p/js-hotkeys/</a></h2>
+ <div id="report-area">
+
+ <h2>Special Keys</h2><div id="_esc" class="eventNotifier">esc</div><div id="_tab" class="eventNotifier">tab</div><div id="_space" class="eventNotifier">space</div><div id="_return" class="eventNotifier">return</div><div
+ id="_backspace" class="eventNotifier">backspace</div><div id="_scroll" class="eventNotifier">scroll</div><div id="_capslock" class="eventNotifier">capslock</div><div id="_numlock" class="eventNotifier">numlock</div><div id=
+"_pause" class="eventNotifier">pause</div><div id="_insert" class="eventNotifier">insert</div><div id="_home" class="eventNotifier">home</div><div id="_del" class="eventNotifier">del</div><div id="_end" class="eventNotifier">
+
+end</div><div id="_pageup" class="eventNotifier">pageup</div><div id="_pagedown" class="eventNotifier">pagedown</div><div id="_left" class="eventNotifier">left</div><div id="_up" class="eventNotifier">up</div><div id="_right"
+class="eventNotifier">right</div><div id="_down" class="eventNotifier">down</div><div id="_f1" class="eventNotifier">f1</div><div id="_f2" class="eventNotifier">f2</div><div id="_f3" class="eventNotifier">f3</div><div id="_f4"
+class="eventNotifier">f4</div><div id="_f5" class="eventNotifier">f5</div><div id="_f6" class="eventNotifier">f6</div><div id="_f7" class="eventNotifier">f7</div><div id="_f8" class="eventNotifier">f8</div><div id="_f9" class=
+"eventNotifier">f9</div><div id="_f10" class="eventNotifier">f10</div><div id="_f11" class="eventNotifier">f11</div><div id="_f12" class="eventNotifier">f12</div><div style="clear: both;"/>
+
+<h2>0-9 Digits</h2>
+<div id="_1" class="eventNotifier">1</div>
+<div id="_2" class="eventNotifier">2</div>
+<div id="_3" class="eventNotifier">3</div>
+<div id="_4" class="eventNotifier">4</div>
+<div id="_5" class="eventNotifier">5</div>
+<div id="_6" class="eventNotifier">6</div>
+<div id="_7" class="eventNotifier">7</div>
+<div id="_8" class="eventNotifier">8</div>
+<div id="_9" class="eventNotifier">9</div>
+<div id="_0" class="eventNotifier">0</div>
+<div style="clear: both;"/>
+<h2>A-Z Letters</h2>
+
+<div id="_a" class="eventNotifier">a</div>
+<div id="_b" class="eventNotifier">b</div><div id="_c" class="eventNotifier">c</div><div id="_d" class="eventNotifier">d</div><div id="_e" class="eventNotifier">e</div><div id="_f" class="eventNotifier">
+
+f</div><div id="_g" class="eventNotifier">g</div><div id="_h" class="eventNotifier">h</div><div id="_i" class="eventNotifier">i</div><div id="_j" class="eventNotifier">j</div><div id="_k" class="eventNotifier">k</div><div id=
+"_l" class="eventNotifier">l</div><div id="_m" class="eventNotifier">m</div><div id="_n" class="eventNotifier">n</div><div id="_o" class="eventNotifier">o</div><div id="_p" class="eventNotifier">p</div><div id="_q" class=
+"eventNotifier">q</div><div id="_r" class="eventNotifier">r</div><div id="_s" class="eventNotifier">s</div><div id="_t" class="eventNotifier">t</div><div id="_u" class="eventNotifier">u</div><div id="_v" class="eventNotifier">v
+
+</div><div id="_w" class="eventNotifier">w</div><div id="_x" class="eventNotifier">x</div><div id="_y" class="eventNotifier">y</div><div id="_z" class="eventNotifier">z</div><div style="clear: both;"/><h2>Special Modifiers</h2
+><div style="clear: both;"/><strong>Ctrl</strong><div id="_Ctrl_a" class="eventNotifier">Ctrl+a</div><div id="_Ctrl_b" class="eventNotifier">Ctrl+b</div><div id="_Ctrl_c" class="eventNotifier">Ctrl+c</div><div id="_Ctrl_d"
+class="eventNotifier">Ctrl+d</div><div id="_Ctrl_e" class="eventNotifier">Ctrl+e</div><div id="_Ctrl_f" class="eventNotifier">Ctrl+f</div><div id="_Ctrl_g" class="eventNotifier">Ctrl+g</div><div id="_Ctrl_h" class=
+"eventNotifier">Ctrl+h</div><div id="_Ctrl_i" class="eventNotifier">Ctrl+i</div><div id="_Ctrl_j" class="eventNotifier">Ctrl+j</div><div id="_Ctrl_k" class="eventNotifier">Ctrl+k</div><div id="_Ctrl_l" class="eventNotifier">
+
+Ctrl+l</div><div id="_Ctrl_m" class="eventNotifier">Ctrl+m</div><div id="_Ctrl_n" class="eventNotifier">Ctrl+n</div><div id="_Ctrl_o" class="eventNotifier">Ctrl+o</div><div id="_Ctrl_p" class="eventNotifier">Ctrl+p</div><div id
+="_Ctrl_q" class="eventNotifier">Ctrl+q</div><div id="_Ctrl_r" class="eventNotifier">Ctrl+r</div><div id="_Ctrl_s" class="eventNotifier">Ctrl+s</div><div id="_Ctrl_t" class="eventNotifier">Ctrl+t</div><div id="_Ctrl_u" class=
+"eventNotifier">Ctrl+u</div><div id="_Ctrl_v" class="eventNotifier">Ctrl+v</div><div id="_Ctrl_w" class="eventNotifier">Ctrl+w</div><div id="_Ctrl_x" class="eventNotifier">Ctrl+x</div><div id="_Ctrl_y" class="eventNotifier">
+Ctrl+y</div><div id="_Ctrl_z" class="eventNotifier">Ctrl+z</div><div style="clear: both;"/><strong>Shift</strong><div id="_Shift_a" class="eventNotifier">Shift+a</div><div id="_Shift_b" class="eventNotifier">Shift+b</div><div
+id="_Shift_c" class="eventNotifier">Shift+c</div><div id="_Shift_d" class="eventNotifier">Shift+d</div><div id="_Shift_e" class="eventNotifier">Shift+e</div><div id="_Shift_f" class="eventNotifier">Shift+f</div><div id=
+"_Shift_g" class="eventNotifier">Shift+g</div><div id="_Shift_h" class="eventNotifier">Shift+h</div><div id="_Shift_i" class="eventNotifier">Shift+i</div><div id="_Shift_j" class="eventNotifier">Shift+j</div><div id="_Shift_k"
+class="eventNotifier">Shift+k</div><div id="_Shift_l" class="eventNotifier">Shift+l</div><div id="_Shift_m" class="eventNotifier">Shift+m</div><div id="_Shift_n" class="eventNotifier">Shift+n</div><div id="_Shift_o" class=
+"eventNotifier">Shift+o</div><div id="_Shift_p" class="eventNotifier">Shift+p</div><div id="_Shift_q" class="eventNotifier">Shift+q</div><div id="_Shift_r" class="eventNotifier">Shift+r</div><div id="_Shift_s" class=
+"eventNotifier">Shift+s</div><div id="_Shift_t" class="eventNotifier">Shift+t</div><div id="_Shift_u" class="eventNotifier">Shift+u</div><div id="_Shift_v" class="eventNotifier">Shift+v</div><div id="_Shift_w" class=
+"eventNotifier">Shift+w</div><div id="_Shift_x" class="eventNotifier">Shift+x</div><div id="_Shift_y" class="eventNotifier">Shift+y</div><div id="_Shift_z" class="eventNotifier">Shift+z</div><div style="clear: both;"/><strong>
+
+Alt</strong><div id="_Alt_a" class="eventNotifier">Alt+a</div><div id="_Alt_b" class="eventNotifier">Alt+b</div><div id="_Alt_c" class="eventNotifier">Alt+c</div><div id="_Alt_d" class="eventNotifier">Alt+d</div><div id=
+"_Alt_e" class="eventNotifier">Alt+e</div><div id="_Alt_f" class="eventNotifier">Alt+f</div><div id="_Alt_g" class="eventNotifier">Alt+g</div><div id="_Alt_h" class="eventNotifier">Alt+h</div><div id="_Alt_i" class=
+"eventNotifier">Alt+i</div><div id="_Alt_j" class="eventNotifier">Alt+j</div><div id="_Alt_k" class="eventNotifier">Alt+k</div><div id="_Alt_l" class="eventNotifier">Alt+l</div><div id="_Alt_m" class="eventNotifier">Alt+m</div
+><div id="_Alt_n" class="eventNotifier">Alt+n</div><div id="_Alt_o" class="eventNotifier">Alt+o</div><div id="_Alt_p" class="eventNotifier">Alt+p</div><div id="_Alt_q" class="eventNotifier">Alt+q</div><div id="_Alt_r" class=
+"eventNotifier">Alt+r</div><div id="_Alt_s" class="eventNotifier">Alt+s</div><div id="_Alt_t" class="eventNotifier">Alt+t</div><div id="_Alt_u" class="eventNotifier">Alt+u</div><div id="_Alt_v" class="eventNotifier">Alt+v</div
+><div id="_Alt_w" class="eventNotifier">Alt+w</div><div id="_Alt_x" class="eventNotifier">Alt+x</div><div id="_Alt_y" class="eventNotifier">Alt+y</div><div id="_Alt_z" class="eventNotifier">Alt+z</div><div style="clear: both;"
+/><h2>Special Modifiers + Special Keys</h2><div style="clear: both;"/><strong>Ctrl</strong><div id="_Ctrl_esc" class="eventNotifier">Ctrl+esc</div><div id="_Ctrl_tab" class="eventNotifier">Ctrl+tab</div><div id="_Ctrl_space"
+class="eventNotifier">Ctrl+space</div><div id="_Ctrl_return" class="eventNotifier">Ctrl+return</div><div id="_Ctrl_backspace" class="eventNotifier">Ctrl+backspace</div><div id="_Ctrl_scroll" class="eventNotifier">Ctrl+scroll
+
+</div><div id="_Ctrl_capslock" class="eventNotifier">Ctrl+capslock</div><div id="_Ctrl_numlock" class="eventNotifier">Ctrl+numlock</div><div id="_Ctrl_pause" class="eventNotifier">Ctrl+pause</div><div id="_Ctrl_insert" class=
+"eventNotifier">Ctrl+insert</div><div id="_Ctrl_home" class="eventNotifier">Ctrl+home</div><div id="_Ctrl_del" class="eventNotifier">Ctrl+del</div><div id="_Ctrl_end" class="eventNotifier">Ctrl+end</div><div id="_Ctrl_pageup"
+class="eventNotifier">Ctrl+pageup</div><div id="_Ctrl_pagedown" class="eventNotifier">Ctrl+pagedown</div><div id="_Ctrl_left" class="eventNotifier">Ctrl+left</div><div id="_Ctrl_up" class="eventNotifier">Ctrl+up</div><div id=
+"_Ctrl_right" class="eventNotifier">Ctrl+right</div><div id="_Ctrl_down" class="eventNotifier">Ctrl+down</div><div id="_Ctrl_f1" class="eventNotifier">Ctrl+f1</div><div id="_Ctrl_f2" class="eventNotifier">Ctrl+f2</div><div id=
+"_Ctrl_f3" class="eventNotifier">Ctrl+f3</div><div id="_Ctrl_f4" class="eventNotifier">Ctrl+f4</div><div id="_Ctrl_f5" class="eventNotifier">Ctrl+f5</div><div id="_Ctrl_f6" class="eventNotifier">Ctrl+f6</div><div id="_Ctrl_f7"
+class="eventNotifier">Ctrl+f7</div><div id="_Ctrl_f8" class="eventNotifier">Ctrl+f8</div><div id="_Ctrl_f9" class="eventNotifier">Ctrl+f9</div><div id="_Ctrl_f10" class="eventNotifier">Ctrl+f10</div><div id="_Ctrl_f11" class=
+"eventNotifier">Ctrl+f11</div><div id="_Ctrl_f12" class="eventNotifier">Ctrl+f12</div><div style="clear: both;"/><strong>Shift</strong><div id="_Shift_esc" class="eventNotifier">Shift+esc</div><div id="_Shift_tab" class=
+"eventNotifier">Shift+tab</div><div id="_Shift_space" class="eventNotifier">Shift+space</div><div id="_Shift_return" class="eventNotifier">Shift+return</div><div id="_Shift_backspace" class="eventNotifier">Shift+backspace</div
+><div id="_Shift_scroll" class="eventNotifier">Shift+scroll</div><div id="_Shift_capslock" class="eventNotifier">Shift+capslock</div><div id="_Shift_numlock" class="eventNotifier">Shift+numlock</div><div id="_Shift_pause" class
+="eventNotifier">Shift+pause</div><div id="_Shift_insert" class="eventNotifier">Shift+insert</div><div id="_Shift_home" class="eventNotifier">Shift+home</div><div id="_Shift_del" class="eventNotifier">Shift+del</div><div id=
+"_Shift_end" class="eventNotifier">Shift+end</div><div id="_Shift_pageup" class="eventNotifier">Shift+pageup</div><div id="_Shift_pagedown" class="eventNotifier">Shift+pagedown</div><div id="_Shift_left" class="eventNotifier">
+
+Shift+left</div><div id="_Shift_up" class="eventNotifier">Shift+up</div><div id="_Shift_right" class="eventNotifier">Shift+right</div><div id="_Shift_down" class="eventNotifier">Shift+down</div><div id="_Shift_f1" class=
+"eventNotifier">Shift+f1</div><div id="_Shift_f2" class="eventNotifier">Shift+f2</div><div id="_Shift_f3" class="eventNotifier">Shift+f3</div><div id="_Shift_f4" class="eventNotifier">Shift+f4</div><div id="_Shift_f5" class=
+"eventNotifier">Shift+f5</div><div id="_Shift_f6" class="eventNotifier">Shift+f6</div><div id="_Shift_f7" class="eventNotifier">Shift+f7</div><div id="_Shift_f8" class="eventNotifier">Shift+f8</div><div id="_Shift_f9" class=
+"eventNotifier">Shift+f9</div><div id="_Shift_f10" class="eventNotifier">Shift+f10</div><div id="_Shift_f11" class="eventNotifier">Shift+f11</div><div id="_Shift_f12" class="eventNotifier">Shift+f12</div><div style="clear:
+both;"/><strong>Alt</strong><div id="_Alt_esc" class="eventNotifier">Alt+esc</div><div id="_Alt_tab" class="eventNotifier">Alt+tab</div><div id="_Alt_space" class="eventNotifier">Alt+space</div><div id="_Alt_return" class=
+"eventNotifier">Alt+return</div><div id="_Alt_backspace" class="eventNotifier">Alt+backspace</div><div id="_Alt_scroll" class="eventNotifier">Alt+scroll</div><div id="_Alt_capslock" class="eventNotifier">Alt+capslock</div><div
+id="_Alt_numlock" class="eventNotifier">Alt+numlock</div><div id="_Alt_pause" class="eventNotifier">Alt+pause</div><div id="_Alt_insert" class="eventNotifier">Alt+insert</div><div id="_Alt_home" class="eventNotifier">Alt+home
+
+</div><div id="_Alt_del" class="eventNotifier">Alt+del</div><div id="_Alt_end" class="eventNotifier">Alt+end</div><div id="_Alt_pageup" class="eventNotifier">Alt+pageup</div><div id="_Alt_pagedown" class="eventNotifier">Alt+
+pagedown</div><div id="_Alt_left" class="eventNotifier">Alt+left</div><div id="_Alt_up" class="eventNotifier">Alt+up</div><div id="_Alt_right" class="eventNotifier">Alt+right</div><div id="_Alt_down" class="eventNotifier">Alt+
+down</div><div id="_Alt_f1" class="eventNotifier">Alt+f1</div><div id="_Alt_f2" class="eventNotifier">Alt+f2</div><div id="_Alt_f3" class="eventNotifier">Alt+f3</div><div id="_Alt_f4" class="eventNotifier">Alt+f4</div><div id=
+"_Alt_f5" class="eventNotifier">Alt+f5</div><div id="_Alt_f6" class="eventNotifier">Alt+f6</div><div id="_Alt_f7" class="eventNotifier">Alt+f7</div><div id="_Alt_f8" class="eventNotifier">Alt+f8</div><div id="_Alt_f9" class=
+"eventNotifier">Alt+f9</div><div id="_Alt_f10" class="eventNotifier">Alt+f10</div><div id="_Alt_f11" class="eventNotifier">Alt+f11</div><div id="_Alt_f12" class="eventNotifier">Alt+f12</div></div>
+
+
+ </body></html>
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-02.html
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-02.html (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-02.html 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,88 @@
+
+<html>
+ <head>
+ <style>
+ * {font-family: mono; font-size:0.95em}
+ .eventNotifier{width: 100px; float: left; color:navy; border: dotted 1px navy; padding: 4px; background-color:white; margin:3px}
+ .dirty{border: solid 1px #0ca2ff; color:white; background-color:#0ca2ff}
+
+ </style>
+ </head>
+ <body>
+ <h3>Test #01</h3>
+ <input type='text' id='input_01'/>
+ <p>
+ type 'ctrl+l ' to focus.<br/>
+ type 'shift+3' to insert 'Shift#' into the text box.<br/>
+ type 'a' inside the textbox and have 'b' inserted instead.
+ </p>
+ <hr />
+ <h3>Test #02</h3>
+ <table>
+ <tbody>
+ <tr>
+ <td><input type='text' id='input_02' class='foo'></td>
+ </tr>
+ <tr>
+ <td><input type='text' id='input_03' class='foo'></td>
+ </tr>
+ <tr>
+ <td><input type='text' id='input_04' class='foo'></td>
+ </tr>
+ <tr>
+ <td><input type='text' id='input_05' class='foo'></td>
+ </tr>
+ <tr>
+ <td><input type='text' id='input_06' class='foo'></td>
+ </tr>
+ </tbody>
+ </table>
+ <input type='button' value='UnBind Click' onclick="unbindClick()" />
+ <input type='button' value='UnBind Keyup' onclick="unbindKeyup()" />
+ </body>
+ <hr />
+ <div id="logger"></div>
+ <script src="jquery-1.4.2.js"></script>
+ <script src="jquery.hotkeys.js"></script>
+ <script>
+ $(document).ready(function(){
+ $(document).bind('keydown', 'ctrl+l', function(){$('#input_01')[0].focus();})
+ .bind('keydown', 'shift+#', function(){$('#input_01')[0].value = "Shift#";})
+ //.bind('keyup', function () { alert (arguments); })
+ .bind('click', function (event){
+ if (event.target == $('html')[0]){
+ alert("save the planet, don't waste energy over meaningless clicking");
+ }
+ });
+
+ $('#input_01').bind('keyup', 'a', function(event){
+ this.value = this.value.replace(/a/g, "b");
+ });
+
+ $('input.foo').bind('keydown', 'ctrl+k', function(event){
+ log('binding keydown/ctrl+k to <b>input</b> applied on <b>#' + event.target.id + '</b>');
+ return false;
+ });
+
+ $('table').bind('keydown click keyup', 'ctrl+l', clickHandler);
+ });
+
+ function clickHandler(event){
+ log('binding ' + event.type + ' with(ctrl+l) to <b>table</b> applied on <b>#' + event.target.id + '</b>');
+ return false;
+ }
+
+ function unbindClick(){
+ $('table').unbind('click', clickHandler).unbind('keyup', 'ctrl+l', clickHandler);
+ }
+
+ function unbindKeyup(){
+ $('table').unbind('keyup', 'ctrl+l', clickHandler);
+ }
+
+
+ function log(msg){
+ $('#logger').html(msg);
+ }
+ </script>
+</html>
\ No newline at end of file
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-03.html
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-03.html (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-03.html 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,42 @@
+
+<html>
+ <head>
+ <style>
+ * {font-family: mono; font-size:0.95em}
+ .eventNotifier{width: 100px; float: left; color:navy; border: dotted 1px navy; padding: 4px; background-color:white; margin:3px}
+ .dirty{border: solid 1px #0ca2ff; color:white; background-color:#0ca2ff}
+
+ </style>
+ </head>
+ <body>
+ <h3>Test #01</h3>
+ <table>
+ <tbody>
+ <tr>
+ <td><input type='text' id='input_02' class='foo'></td>
+ </tr>
+ <tr>
+ <td><input type='text' id='input_03' class='foo'></td>
+ </tr>
+ <tr>
+ <td><input type='text' id='input_04' class='foo'></td>
+ </tr>
+ <tr>
+ <td><input type='text' id='input_05' class='foo'></td>
+ </tr>
+ <tr>
+ <td><input type='text' id='input_06' class='foo'></td>
+ </tr>
+ </tbody>
+ </table>
+ </body>
+ <script src="jquery-1.4.2.js"></script>
+ <script src="jquery.hotkeys.js"></script>
+ <script>
+ $(document).ready(function(){
+ $('input.foo').bind('keyup', '$', function(){
+ this.value = this.value.replace('$', '€');
+ });
+ });
+ </script>
+</html>
\ No newline at end of file
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-04.html
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-04.html (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-04.html 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,40 @@
+<html>
+ <head>
+ <style>
+ * {font-family: Helvetica, Verdana, Arial; font-size:0.95em}
+ .eventNotifier{width: 100px; float: left; color:navy; border: dotted 1px navy; padding: 4px; background-color:white; margin:3px}
+ .dirty{border: solid 1px #0ca2ff; color:white; background-color:#0ca2ff}
+
+ </style>
+ <script src="jquery-1.4.2.js"></script>
+ <script src="jquery.hotkeys.js"></script>
+ <script>
+ //This page is a result of an autogenerated content made by running test.html with firefox.
+ function domo(){
+ jQuery('#platform-details').html('<code>' + navigator.userAgent + '</code>');
+ jQuery(document).bind('keydown', '/', function (evt){
+ alert("Hello Slash");
+ return false;
+ });
+ jQuery(document).bind('keydown', 'ctrl+p meta+p', function (evt){
+ alert("think green-don't print");
+ return false;
+ });
+ }
+
+
+ jQuery(document).ready(domo);
+
+ </script>
+ </head>
+
+ <body>
+ <h1>jQuery.HotKeys.Testing.</h1>
+ <h2>Testing Platform:</h2>
+ <div id="platform-details"></div>
+ <h2>Project Home: <a href="http://code.google.com/p/js-hotkeys/">http://code.google.com/p/js-hotkeys/</a></h2>
+ <div id="report-area">
+ </div>
+
+
+ </body></html>
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-05.html
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-05.html (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-05.html 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,350 @@
+<html>
+ <head>
+ <style>
+ * {font-family: Helvetica, Verdana, Arial; font-size:0.95em}
+ .eventNotifier{width: 100px; float: left; color:navy; border: dotted 1px navy; padding: 4px; background-color:white; margin:3px}
+ .dirty{border: solid 1px #0ca2ff; color:white; background-color:#0ca2ff}
+
+ </style>
+ <script src="jquery-1.4.2.js"></script>
+ <script src="jquery.hotkeys.js"></script>
+ <script>
+ //This page is a result of an autogenerated content made by running test.html with firefox.
+ function domo(){
+ jQuery('#platform-details').html('<code>' + navigator.userAgent + '</code>');
+ jQuery(document).bind('keypress', 'esc',function (evt){jQuery('#_esc').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'tab',function (evt){jQuery('#_tab').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'space',function (evt){jQuery('#_space').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'return',function (evt){jQuery('#_return').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'backspace',function (evt){jQuery('#_backspace').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'scroll',function (evt){jQuery('#_scroll').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'capslock',function (evt){jQuery('#_capslock').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'numlock',function (evt){jQuery('#_numlock').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'pause',function (evt){jQuery('#_pause').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'insert',function (evt){jQuery('#_insert').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'home',function (evt){jQuery('#_home').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'del',function (evt){jQuery('#_del').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'end',function (evt){jQuery('#_end').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'pageup',function (evt){jQuery('#_pageup').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'pagedown',function (evt){jQuery('#_pagedown').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'left',function (evt){jQuery('#_left').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'up',function (evt){jQuery('#_up').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'right',function (evt){jQuery('#_right').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'down',function (evt){jQuery('#_down').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'f1',function (evt){jQuery('#_f1').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'f2',function (evt){jQuery('#_f2').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'f3',function (evt){jQuery('#_f3').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'f4',function (evt){jQuery('#_f4').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'f5',function (evt){jQuery('#_f5').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'f6',function (evt){jQuery('#_f6').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'f7',function (evt){jQuery('#_f7').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'f8',function (evt){jQuery('#_f8').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'f9',function (evt){jQuery('#_f9').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'f10',function (evt){jQuery('#_f10').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'f11',function (evt){jQuery('#_f11').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'f12',function (evt){jQuery('#_f12').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', '1',function (evt){jQuery('#_1').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', '2',function (evt){jQuery('#_2').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', '3',function (evt){jQuery('#_3').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', '4',function (evt){jQuery('#_4').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', '5',function (evt){jQuery('#_5').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', '6',function (evt){jQuery('#_6').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', '7',function (evt){jQuery('#_7').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', '8',function (evt){jQuery('#_8').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', '9',function (evt){jQuery('#_9').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', '0',function (evt){jQuery('#_0').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'a',function (evt){jQuery('#_a').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'b',function (evt){jQuery('#_b').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'c',function (evt){jQuery('#_c').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'd',function (evt){jQuery('#_d').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'e',function (evt){jQuery('#_e').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'f',function (evt){jQuery('#_f').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'g',function (evt){jQuery('#_g').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'h',function (evt){jQuery('#_h').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'i',function (evt){jQuery('#_i').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'j',function (evt){jQuery('#_j').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'k',function (evt){jQuery('#_k').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'l',function (evt){jQuery('#_l').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'm',function (evt){jQuery('#_m').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'n',function (evt){jQuery('#_n').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'o',function (evt){jQuery('#_o').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'p',function (evt){jQuery('#_p').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'q',function (evt){jQuery('#_q').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'r',function (evt){jQuery('#_r').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 's',function (evt){jQuery('#_s').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 't',function (evt){jQuery('#_t').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'u',function (evt){jQuery('#_u').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'v',function (evt){jQuery('#_v').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'w',function (evt){jQuery('#_w').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'x',function (evt){jQuery('#_x').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'y',function (evt){jQuery('#_y').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'z',function (evt){jQuery('#_z').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+a',function (evt){jQuery('#_Ctrl_a').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+b',function (evt){jQuery('#_Ctrl_b').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+c',function (evt){jQuery('#_Ctrl_c').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+d',function (evt){jQuery('#_Ctrl_d').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+e',function (evt){jQuery('#_Ctrl_e').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+f',function (evt){jQuery('#_Ctrl_f').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+g',function (evt){jQuery('#_Ctrl_g').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+h',function (evt){jQuery('#_Ctrl_h').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+i',function (evt){jQuery('#_Ctrl_i').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+j',function (evt){jQuery('#_Ctrl_j').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+k',function (evt){jQuery('#_Ctrl_k').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+l',function (evt){jQuery('#_Ctrl_l').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+m',function (evt){jQuery('#_Ctrl_m').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+n',function (evt){jQuery('#_Ctrl_n').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+o',function (evt){jQuery('#_Ctrl_o').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+p',function (evt){jQuery('#_Ctrl_p').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+q',function (evt){jQuery('#_Ctrl_q').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+r',function (evt){jQuery('#_Ctrl_r').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+s',function (evt){jQuery('#_Ctrl_s').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+t',function (evt){jQuery('#_Ctrl_t').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+u',function (evt){jQuery('#_Ctrl_u').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+v',function (evt){jQuery('#_Ctrl_v').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+w',function (evt){jQuery('#_Ctrl_w').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+x',function (evt){jQuery('#_Ctrl_x').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+y',function (evt){jQuery('#_Ctrl_y').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+z',function (evt){jQuery('#_Ctrl_z').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+a',function (evt){jQuery('#_Shift_a').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+b',function (evt){jQuery('#_Shift_b').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+c',function (evt){jQuery('#_Shift_c').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+d',function (evt){jQuery('#_Shift_d').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+e',function (evt){jQuery('#_Shift_e').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+f',function (evt){jQuery('#_Shift_f').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+g',function (evt){jQuery('#_Shift_g').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+h',function (evt){jQuery('#_Shift_h').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+i',function (evt){jQuery('#_Shift_i').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+j',function (evt){jQuery('#_Shift_j').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+k',function (evt){jQuery('#_Shift_k').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+l',function (evt){jQuery('#_Shift_l').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+m',function (evt){jQuery('#_Shift_m').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+n',function (evt){jQuery('#_Shift_n').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+o',function (evt){jQuery('#_Shift_o').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+p',function (evt){jQuery('#_Shift_p').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+q',function (evt){jQuery('#_Shift_q').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+r',function (evt){jQuery('#_Shift_r').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+s',function (evt){jQuery('#_Shift_s').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+t',function (evt){jQuery('#_Shift_t').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+u',function (evt){jQuery('#_Shift_u').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+v',function (evt){jQuery('#_Shift_v').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+w',function (evt){jQuery('#_Shift_w').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+x',function (evt){jQuery('#_Shift_x').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+y',function (evt){jQuery('#_Shift_y').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+z',function (evt){jQuery('#_Shift_z').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+a',function (evt){jQuery('#_Alt_a').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+b',function (evt){jQuery('#_Alt_b').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+c',function (evt){jQuery('#_Alt_c').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+d',function (evt){jQuery('#_Alt_d').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+e',function (evt){jQuery('#_Alt_e').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+f',function (evt){jQuery('#_Alt_f').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+g',function (evt){jQuery('#_Alt_g').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+h',function (evt){jQuery('#_Alt_h').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+i',function (evt){jQuery('#_Alt_i').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+j',function (evt){jQuery('#_Alt_j').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+k',function (evt){jQuery('#_Alt_k').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+l',function (evt){jQuery('#_Alt_l').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+m',function (evt){jQuery('#_Alt_m').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+n',function (evt){jQuery('#_Alt_n').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+o',function (evt){jQuery('#_Alt_o').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+p',function (evt){jQuery('#_Alt_p').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+q',function (evt){jQuery('#_Alt_q').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+r',function (evt){jQuery('#_Alt_r').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+s',function (evt){jQuery('#_Alt_s').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+t',function (evt){jQuery('#_Alt_t').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+u',function (evt){jQuery('#_Alt_u').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+v',function (evt){jQuery('#_Alt_v').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+w',function (evt){jQuery('#_Alt_w').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+x',function (evt){jQuery('#_Alt_x').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+y',function (evt){jQuery('#_Alt_y').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+z',function (evt){jQuery('#_Alt_z').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+esc', function (evt){jQuery('#_Ctrl_esc').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+tab', function (evt){jQuery('#_Ctrl_tab').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+space', function (evt){jQuery('#_Ctrl_space').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+return', function (evt){jQuery('#_Ctrl_return').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+backspace', function (evt){jQuery('#_Ctrl_backspace').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+scroll', function (evt){jQuery('#_Ctrl_scroll').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+capslock', function (evt){jQuery('#_Ctrl_capslock').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+numlock', function (evt){jQuery('#_Ctrl_numlock').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+pause', function (evt){jQuery('#_Ctrl_pause').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+insert', function (evt){jQuery('#_Ctrl_insert').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+home', function (evt){jQuery('#_Ctrl_home').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+del', function (evt){jQuery('#_Ctrl_del').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+end', function (evt){jQuery('#_Ctrl_end').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+pageup', function (evt){jQuery('#_Ctrl_pageup').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+pagedown', function (evt){jQuery('#_Ctrl_pagedown').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+left', function (evt){jQuery('#_Ctrl_left').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+up', function (evt){jQuery('#_Ctrl_up').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+right', function (evt){jQuery('#_Ctrl_right').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+down', function (evt){jQuery('#_Ctrl_down').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+f1', function (evt){jQuery('#_Ctrl_f1').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+f2', function (evt){jQuery('#_Ctrl_f2').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+f3', function (evt){jQuery('#_Ctrl_f3').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+f4', function (evt){jQuery('#_Ctrl_f4').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+f5', function (evt){jQuery('#_Ctrl_f5').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+f6', function (evt){jQuery('#_Ctrl_f6').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+f7', function (evt){jQuery('#_Ctrl_f7').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+f8', function (evt){jQuery('#_Ctrl_f8').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+f9', function (evt){jQuery('#_Ctrl_f9').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+f10', function (evt){jQuery('#_Ctrl_f10').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+f11', function (evt){jQuery('#_Ctrl_f11').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Ctrl+f12', function (evt){jQuery('#_Ctrl_f12').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+esc', function (evt){jQuery('#_Shift_esc').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+tab', function (evt){jQuery('#_Shift_tab').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+space', function (evt){jQuery('#_Shift_space').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+return', function (evt){jQuery('#_Shift_return').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+backspace', function (evt){jQuery('#_Shift_backspace').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+scroll', function (evt){jQuery('#_Shift_scroll').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+capslock', function (evt){jQuery('#_Shift_capslock').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+numlock', function (evt){jQuery('#_Shift_numlock').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+pause', function (evt){jQuery('#_Shift_pause').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+insert', function (evt){jQuery('#_Shift_insert').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+home', function (evt){jQuery('#_Shift_home').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+del', function (evt){jQuery('#_Shift_del').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+end', function (evt){jQuery('#_Shift_end').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+pageup', function (evt){jQuery('#_Shift_pageup').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+pagedown', function (evt){jQuery('#_Shift_pagedown').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+left', function (evt){jQuery('#_Shift_left').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+up', function (evt){jQuery('#_Shift_up').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+right', function (evt){jQuery('#_Shift_right').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+down', function (evt){jQuery('#_Shift_down').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+f1', function (evt){jQuery('#_Shift_f1').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+f2', function (evt){jQuery('#_Shift_f2').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+f3', function (evt){jQuery('#_Shift_f3').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+f4', function (evt){jQuery('#_Shift_f4').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+f5', function (evt){jQuery('#_Shift_f5').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+f6', function (evt){jQuery('#_Shift_f6').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+f7', function (evt){jQuery('#_Shift_f7').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+f8', function (evt){jQuery('#_Shift_f8').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+f9', function (evt){jQuery('#_Shift_f9').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+f10', function (evt){jQuery('#_Shift_f10').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+f11', function (evt){jQuery('#_Shift_f11').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Shift+f12', function (evt){jQuery('#_Shift_f12').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+esc', function (evt){jQuery('#_Alt_esc').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+tab', function (evt){jQuery('#_Alt_tab').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+space', function (evt){jQuery('#_Alt_space').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+return', function (evt){jQuery('#_Alt_return').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+backspace', function (evt){jQuery('#_Alt_backspace').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+scroll', function (evt){jQuery('#_Alt_scroll').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+capslock', function (evt){jQuery('#_Alt_capslock').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+numlock', function (evt){jQuery('#_Alt_numlock').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+pause', function (evt){jQuery('#_Alt_pause').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+insert', function (evt){jQuery('#_Alt_insert').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+home', function (evt){jQuery('#_Alt_home').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+del', function (evt){jQuery('#_Alt_del').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+end', function (evt){jQuery('#_Alt_end').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+pageup', function (evt){jQuery('#_Alt_pageup').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+pagedown', function (evt){jQuery('#_Alt_pagedown').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+left', function (evt){jQuery('#_Alt_left').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+up', function (evt){jQuery('#_Alt_up').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+right', function (evt){jQuery('#_Alt_right').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+down', function (evt){jQuery('#_Alt_down').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+f1', function (evt){jQuery('#_Alt_f1').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+f2', function (evt){jQuery('#_Alt_f2').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+f3', function (evt){jQuery('#_Alt_f3').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+f4', function (evt){jQuery('#_Alt_f4').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+f5', function (evt){jQuery('#_Alt_f5').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+f6', function (evt){jQuery('#_Alt_f6').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+f7', function (evt){jQuery('#_Alt_f7').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+f8', function (evt){jQuery('#_Alt_f8').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+f9', function (evt){jQuery('#_Alt_f9').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+f10', function (evt){jQuery('#_Alt_f10').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+f11', function (evt){jQuery('#_Alt_f11').addClass('dirty'); return false; });
+ jQuery(document).bind('keypress', 'Alt+f12', function (evt){jQuery('#_Alt_f12').addClass('dirty'); return false; });
+ }
+
+
+ jQuery(document).ready(domo);
+
+ </script>
+ </head>
+
+ <body>
+ <h1>jQuery.HotKeys.Testing.</h1>
+ <h2>Testing Platform:</h2>
+ <div id="platform-details"></div>
+ <h2>Project Home: <a href="http://code.google.com/p/js-hotkeys/">http://code.google.com/p/js-hotkeys/</a></h2>
+ <div id="report-area">
+
+ <h2>Special Keys</h2><div id="_esc" class="eventNotifier">esc</div><div id="_tab" class="eventNotifier">tab</div><div id="_space" class="eventNotifier">space</div><div id="_return" class="eventNotifier">return</div><div
+ id="_backspace" class="eventNotifier">backspace</div><div id="_scroll" class="eventNotifier">scroll</div><div id="_capslock" class="eventNotifier">capslock</div><div id="_numlock" class="eventNotifier">numlock</div><div id=
+"_pause" class="eventNotifier">pause</div><div id="_insert" class="eventNotifier">insert</div><div id="_home" class="eventNotifier">home</div><div id="_del" class="eventNotifier">del</div><div id="_end" class="eventNotifier">
+
+end</div><div id="_pageup" class="eventNotifier">pageup</div><div id="_pagedown" class="eventNotifier">pagedown</div><div id="_left" class="eventNotifier">left</div><div id="_up" class="eventNotifier">up</div><div id="_right"
+class="eventNotifier">right</div><div id="_down" class="eventNotifier">down</div><div id="_f1" class="eventNotifier">f1</div><div id="_f2" class="eventNotifier">f2</div><div id="_f3" class="eventNotifier">f3</div><div id="_f4"
+class="eventNotifier">f4</div><div id="_f5" class="eventNotifier">f5</div><div id="_f6" class="eventNotifier">f6</div><div id="_f7" class="eventNotifier">f7</div><div id="_f8" class="eventNotifier">f8</div><div id="_f9" class=
+"eventNotifier">f9</div><div id="_f10" class="eventNotifier">f10</div><div id="_f11" class="eventNotifier">f11</div><div id="_f12" class="eventNotifier">f12</div><div style="clear: both;"/>
+
+<h2>0-9 Digits</h2>
+<div id="_1" class="eventNotifier">1</div>
+<div id="_2" class="eventNotifier">2</div>
+<div id="_3" class="eventNotifier">3</div>
+<div id="_4" class="eventNotifier">4</div>
+<div id="_5" class="eventNotifier">5</div>
+<div id="_6" class="eventNotifier">6</div>
+<div id="_7" class="eventNotifier">7</div>
+<div id="_8" class="eventNotifier">8</div>
+<div id="_9" class="eventNotifier">9</div>
+<div id="_0" class="eventNotifier">0</div>
+<div style="clear: both;"/>
+<h2>A-Z Letters</h2>
+
+<div id="_a" class="eventNotifier">a</div>
+<div id="_b" class="eventNotifier">b</div><div id="_c" class="eventNotifier">c</div><div id="_d" class="eventNotifier">d</div><div id="_e" class="eventNotifier">e</div><div id="_f" class="eventNotifier">
+
+f</div><div id="_g" class="eventNotifier">g</div><div id="_h" class="eventNotifier">h</div><div id="_i" class="eventNotifier">i</div><div id="_j" class="eventNotifier">j</div><div id="_k" class="eventNotifier">k</div><div id=
+"_l" class="eventNotifier">l</div><div id="_m" class="eventNotifier">m</div><div id="_n" class="eventNotifier">n</div><div id="_o" class="eventNotifier">o</div><div id="_p" class="eventNotifier">p</div><div id="_q" class=
+"eventNotifier">q</div><div id="_r" class="eventNotifier">r</div><div id="_s" class="eventNotifier">s</div><div id="_t" class="eventNotifier">t</div><div id="_u" class="eventNotifier">u</div><div id="_v" class="eventNotifier">v
+
+</div><div id="_w" class="eventNotifier">w</div><div id="_x" class="eventNotifier">x</div><div id="_y" class="eventNotifier">y</div><div id="_z" class="eventNotifier">z</div><div style="clear: both;"/><h2>Special Modifiers</h2
+><div style="clear: both;"/><strong>Ctrl</strong><div id="_Ctrl_a" class="eventNotifier">Ctrl+a</div><div id="_Ctrl_b" class="eventNotifier">Ctrl+b</div><div id="_Ctrl_c" class="eventNotifier">Ctrl+c</div><div id="_Ctrl_d"
+class="eventNotifier">Ctrl+d</div><div id="_Ctrl_e" class="eventNotifier">Ctrl+e</div><div id="_Ctrl_f" class="eventNotifier">Ctrl+f</div><div id="_Ctrl_g" class="eventNotifier">Ctrl+g</div><div id="_Ctrl_h" class=
+"eventNotifier">Ctrl+h</div><div id="_Ctrl_i" class="eventNotifier">Ctrl+i</div><div id="_Ctrl_j" class="eventNotifier">Ctrl+j</div><div id="_Ctrl_k" class="eventNotifier">Ctrl+k</div><div id="_Ctrl_l" class="eventNotifier">
+
+Ctrl+l</div><div id="_Ctrl_m" class="eventNotifier">Ctrl+m</div><div id="_Ctrl_n" class="eventNotifier">Ctrl+n</div><div id="_Ctrl_o" class="eventNotifier">Ctrl+o</div><div id="_Ctrl_p" class="eventNotifier">Ctrl+p</div><div id
+="_Ctrl_q" class="eventNotifier">Ctrl+q</div><div id="_Ctrl_r" class="eventNotifier">Ctrl+r</div><div id="_Ctrl_s" class="eventNotifier">Ctrl+s</div><div id="_Ctrl_t" class="eventNotifier">Ctrl+t</div><div id="_Ctrl_u" class=
+"eventNotifier">Ctrl+u</div><div id="_Ctrl_v" class="eventNotifier">Ctrl+v</div><div id="_Ctrl_w" class="eventNotifier">Ctrl+w</div><div id="_Ctrl_x" class="eventNotifier">Ctrl+x</div><div id="_Ctrl_y" class="eventNotifier">
+Ctrl+y</div><div id="_Ctrl_z" class="eventNotifier">Ctrl+z</div><div style="clear: both;"/><strong>Shift</strong><div id="_Shift_a" class="eventNotifier">Shift+a</div><div id="_Shift_b" class="eventNotifier">Shift+b</div><div
+id="_Shift_c" class="eventNotifier">Shift+c</div><div id="_Shift_d" class="eventNotifier">Shift+d</div><div id="_Shift_e" class="eventNotifier">Shift+e</div><div id="_Shift_f" class="eventNotifier">Shift+f</div><div id=
+"_Shift_g" class="eventNotifier">Shift+g</div><div id="_Shift_h" class="eventNotifier">Shift+h</div><div id="_Shift_i" class="eventNotifier">Shift+i</div><div id="_Shift_j" class="eventNotifier">Shift+j</div><div id="_Shift_k"
+class="eventNotifier">Shift+k</div><div id="_Shift_l" class="eventNotifier">Shift+l</div><div id="_Shift_m" class="eventNotifier">Shift+m</div><div id="_Shift_n" class="eventNotifier">Shift+n</div><div id="_Shift_o" class=
+"eventNotifier">Shift+o</div><div id="_Shift_p" class="eventNotifier">Shift+p</div><div id="_Shift_q" class="eventNotifier">Shift+q</div><div id="_Shift_r" class="eventNotifier">Shift+r</div><div id="_Shift_s" class=
+"eventNotifier">Shift+s</div><div id="_Shift_t" class="eventNotifier">Shift+t</div><div id="_Shift_u" class="eventNotifier">Shift+u</div><div id="_Shift_v" class="eventNotifier">Shift+v</div><div id="_Shift_w" class=
+"eventNotifier">Shift+w</div><div id="_Shift_x" class="eventNotifier">Shift+x</div><div id="_Shift_y" class="eventNotifier">Shift+y</div><div id="_Shift_z" class="eventNotifier">Shift+z</div><div style="clear: both;"/><strong>
+
+Alt</strong><div id="_Alt_a" class="eventNotifier">Alt+a</div><div id="_Alt_b" class="eventNotifier">Alt+b</div><div id="_Alt_c" class="eventNotifier">Alt+c</div><div id="_Alt_d" class="eventNotifier">Alt+d</div><div id=
+"_Alt_e" class="eventNotifier">Alt+e</div><div id="_Alt_f" class="eventNotifier">Alt+f</div><div id="_Alt_g" class="eventNotifier">Alt+g</div><div id="_Alt_h" class="eventNotifier">Alt+h</div><div id="_Alt_i" class=
+"eventNotifier">Alt+i</div><div id="_Alt_j" class="eventNotifier">Alt+j</div><div id="_Alt_k" class="eventNotifier">Alt+k</div><div id="_Alt_l" class="eventNotifier">Alt+l</div><div id="_Alt_m" class="eventNotifier">Alt+m</div
+><div id="_Alt_n" class="eventNotifier">Alt+n</div><div id="_Alt_o" class="eventNotifier">Alt+o</div><div id="_Alt_p" class="eventNotifier">Alt+p</div><div id="_Alt_q" class="eventNotifier">Alt+q</div><div id="_Alt_r" class=
+"eventNotifier">Alt+r</div><div id="_Alt_s" class="eventNotifier">Alt+s</div><div id="_Alt_t" class="eventNotifier">Alt+t</div><div id="_Alt_u" class="eventNotifier">Alt+u</div><div id="_Alt_v" class="eventNotifier">Alt+v</div
+><div id="_Alt_w" class="eventNotifier">Alt+w</div><div id="_Alt_x" class="eventNotifier">Alt+x</div><div id="_Alt_y" class="eventNotifier">Alt+y</div><div id="_Alt_z" class="eventNotifier">Alt+z</div><div style="clear: both;"
+/><h2>Special Modifiers + Special Keys</h2><div style="clear: both;"/><strong>Ctrl</strong><div id="_Ctrl_esc" class="eventNotifier">Ctrl+esc</div><div id="_Ctrl_tab" class="eventNotifier">Ctrl+tab</div><div id="_Ctrl_space"
+class="eventNotifier">Ctrl+space</div><div id="_Ctrl_return" class="eventNotifier">Ctrl+return</div><div id="_Ctrl_backspace" class="eventNotifier">Ctrl+backspace</div><div id="_Ctrl_scroll" class="eventNotifier">Ctrl+scroll
+
+</div><div id="_Ctrl_capslock" class="eventNotifier">Ctrl+capslock</div><div id="_Ctrl_numlock" class="eventNotifier">Ctrl+numlock</div><div id="_Ctrl_pause" class="eventNotifier">Ctrl+pause</div><div id="_Ctrl_insert" class=
+"eventNotifier">Ctrl+insert</div><div id="_Ctrl_home" class="eventNotifier">Ctrl+home</div><div id="_Ctrl_del" class="eventNotifier">Ctrl+del</div><div id="_Ctrl_end" class="eventNotifier">Ctrl+end</div><div id="_Ctrl_pageup"
+class="eventNotifier">Ctrl+pageup</div><div id="_Ctrl_pagedown" class="eventNotifier">Ctrl+pagedown</div><div id="_Ctrl_left" class="eventNotifier">Ctrl+left</div><div id="_Ctrl_up" class="eventNotifier">Ctrl+up</div><div id=
+"_Ctrl_right" class="eventNotifier">Ctrl+right</div><div id="_Ctrl_down" class="eventNotifier">Ctrl+down</div><div id="_Ctrl_f1" class="eventNotifier">Ctrl+f1</div><div id="_Ctrl_f2" class="eventNotifier">Ctrl+f2</div><div id=
+"_Ctrl_f3" class="eventNotifier">Ctrl+f3</div><div id="_Ctrl_f4" class="eventNotifier">Ctrl+f4</div><div id="_Ctrl_f5" class="eventNotifier">Ctrl+f5</div><div id="_Ctrl_f6" class="eventNotifier">Ctrl+f6</div><div id="_Ctrl_f7"
+class="eventNotifier">Ctrl+f7</div><div id="_Ctrl_f8" class="eventNotifier">Ctrl+f8</div><div id="_Ctrl_f9" class="eventNotifier">Ctrl+f9</div><div id="_Ctrl_f10" class="eventNotifier">Ctrl+f10</div><div id="_Ctrl_f11" class=
+"eventNotifier">Ctrl+f11</div><div id="_Ctrl_f12" class="eventNotifier">Ctrl+f12</div><div style="clear: both;"/><strong>Shift</strong><div id="_Shift_esc" class="eventNotifier">Shift+esc</div><div id="_Shift_tab" class=
+"eventNotifier">Shift+tab</div><div id="_Shift_space" class="eventNotifier">Shift+space</div><div id="_Shift_return" class="eventNotifier">Shift+return</div><div id="_Shift_backspace" class="eventNotifier">Shift+backspace</div
+><div id="_Shift_scroll" class="eventNotifier">Shift+scroll</div><div id="_Shift_capslock" class="eventNotifier">Shift+capslock</div><div id="_Shift_numlock" class="eventNotifier">Shift+numlock</div><div id="_Shift_pause" class
+="eventNotifier">Shift+pause</div><div id="_Shift_insert" class="eventNotifier">Shift+insert</div><div id="_Shift_home" class="eventNotifier">Shift+home</div><div id="_Shift_del" class="eventNotifier">Shift+del</div><div id=
+"_Shift_end" class="eventNotifier">Shift+end</div><div id="_Shift_pageup" class="eventNotifier">Shift+pageup</div><div id="_Shift_pagedown" class="eventNotifier">Shift+pagedown</div><div id="_Shift_left" class="eventNotifier">
+
+Shift+left</div><div id="_Shift_up" class="eventNotifier">Shift+up</div><div id="_Shift_right" class="eventNotifier">Shift+right</div><div id="_Shift_down" class="eventNotifier">Shift+down</div><div id="_Shift_f1" class=
+"eventNotifier">Shift+f1</div><div id="_Shift_f2" class="eventNotifier">Shift+f2</div><div id="_Shift_f3" class="eventNotifier">Shift+f3</div><div id="_Shift_f4" class="eventNotifier">Shift+f4</div><div id="_Shift_f5" class=
+"eventNotifier">Shift+f5</div><div id="_Shift_f6" class="eventNotifier">Shift+f6</div><div id="_Shift_f7" class="eventNotifier">Shift+f7</div><div id="_Shift_f8" class="eventNotifier">Shift+f8</div><div id="_Shift_f9" class=
+"eventNotifier">Shift+f9</div><div id="_Shift_f10" class="eventNotifier">Shift+f10</div><div id="_Shift_f11" class="eventNotifier">Shift+f11</div><div id="_Shift_f12" class="eventNotifier">Shift+f12</div><div style="clear:
+both;"/><strong>Alt</strong><div id="_Alt_esc" class="eventNotifier">Alt+esc</div><div id="_Alt_tab" class="eventNotifier">Alt+tab</div><div id="_Alt_space" class="eventNotifier">Alt+space</div><div id="_Alt_return" class=
+"eventNotifier">Alt+return</div><div id="_Alt_backspace" class="eventNotifier">Alt+backspace</div><div id="_Alt_scroll" class="eventNotifier">Alt+scroll</div><div id="_Alt_capslock" class="eventNotifier">Alt+capslock</div><div
+id="_Alt_numlock" class="eventNotifier">Alt+numlock</div><div id="_Alt_pause" class="eventNotifier">Alt+pause</div><div id="_Alt_insert" class="eventNotifier">Alt+insert</div><div id="_Alt_home" class="eventNotifier">Alt+home
+
+</div><div id="_Alt_del" class="eventNotifier">Alt+del</div><div id="_Alt_end" class="eventNotifier">Alt+end</div><div id="_Alt_pageup" class="eventNotifier">Alt+pageup</div><div id="_Alt_pagedown" class="eventNotifier">Alt+
+pagedown</div><div id="_Alt_left" class="eventNotifier">Alt+left</div><div id="_Alt_up" class="eventNotifier">Alt+up</div><div id="_Alt_right" class="eventNotifier">Alt+right</div><div id="_Alt_down" class="eventNotifier">Alt+
+down</div><div id="_Alt_f1" class="eventNotifier">Alt+f1</div><div id="_Alt_f2" class="eventNotifier">Alt+f2</div><div id="_Alt_f3" class="eventNotifier">Alt+f3</div><div id="_Alt_f4" class="eventNotifier">Alt+f4</div><div id=
+"_Alt_f5" class="eventNotifier">Alt+f5</div><div id="_Alt_f6" class="eventNotifier">Alt+f6</div><div id="_Alt_f7" class="eventNotifier">Alt+f7</div><div id="_Alt_f8" class="eventNotifier">Alt+f8</div><div id="_Alt_f9" class=
+"eventNotifier">Alt+f9</div><div id="_Alt_f10" class="eventNotifier">Alt+f10</div><div id="_Alt_f11" class="eventNotifier">Alt+f11</div><div id="_Alt_f12" class="eventNotifier">Alt+f12</div></div>
+
+
+ </body></html>
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-06.html
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-06.html (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-06.html 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,41 @@
+<html>
+ <head>
+ <script src="jquery-1.4.2.js"></script>
+ </head>
+
+ <body>
+ <h1>KeyDown.</h1>
+ currentTarget: <input id="kd_currentTarget"><br/>
+ which: <input id="kd_which"></br>
+ shiftKey: <input id="kd_shiftKey"><br/>
+ ctrlKey: <input id="kd_ctrlKey"><br/>
+ altKey: <input id="kd_altKey"><br/>
+ <h1>KeyPress.</h1>
+ currentTarget: <input id="kp_currentTarget"><br/>
+ which: <input id="kp_which"></br>
+ shiftKey: <input id="kp_shiftKey"><br/>
+ ctrlKey: <input id="kp_ctrlKey"><br/>
+ altKey: <input id="kp_altKey"><br/>
+ <script>
+
+ $(document).bind('keydown', KdDescribeEvent);
+ $(document).bind('keypress', KpDescribeEvent);
+
+ function KdDescribeEvent(event){
+ $('#kd_currentTarget').val(event.currentTarget);
+ $('#kd_which').val(event.which);
+ $('#kd_shiftKey').val(event.shiftKey);
+ $('#kd_ctrlKey').val(event.ctrlKey);
+ $('#kd_altKey').val(event.altKey);
+ }
+ function KpDescribeEvent(event){
+ $('#kp_currentTarget').val(event.currentTarget);
+ $('#kp_which').val(event.which);
+ $('#kp_shiftKey').val(event.shiftKey);
+ $('#kp_ctrlKey').val(event.ctrlKey);
+ $('#kp_altKey').val(event.altKey);
+ }
+
+ </script>
+ </body>
+</html>
Added: trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-07.html
===================================================================
--- trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-07.html (rev 0)
+++ trunk/wao-web/src/main/webapp/js/jquery.hotkeys/test-static-07.html 2014-04-14 09:57:45 UTC (rev 1884)
@@ -0,0 +1,29 @@
+<html>
+ <head>
+ <script src="jquery-1.4.2.js"></script>
+ <script src="jquery.hotkeys.js"></script>
+ </head>
+
+ <body>
+ <input type="button" id="bindCtrlS" value="bind ctrl+s" />
+ <input type="button" id="unBindCtrlS" value="unbind ctrl+s" />
+ <script>
+ function bindCtrlS(){
+ jQuery(document).bind('keydown', 'ctrl+s',function (evt){
+ alert("Ctrl+S");
+ return false;
+ });
+ }
+
+ function unBindCtrlS(){
+ jQuery(document).unbind('keydown', 'ctrl+s');
+ }
+
+ $(document).ready(function(){
+ $('#bindCtrlS').bind('click', bindCtrlS);
+ $('#unBindCtrlS').bind('click', unBindCtrlS);
+ }
+ );
+
+ </script>
+ </body></html>
1
0
r1883 - in trunk/wao-web/src/main/webapp: . WEB-INF/content/obsmer
by sbavencoff@users.forge.codelutin.com 14 Apr '14
by sbavencoff@users.forge.codelutin.com 14 Apr '14
14 Apr '14
Author: sbavencoff
Date: 2014-04-14 11:55:25 +0200 (Mon, 14 Apr 2014)
New Revision: 1883
Url: http://forge.codelutin.com/projects/wao/repository/revisions/1883
Log:
refs #4553 : add edito wysiwyg
Modified:
trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/edit-news-input.jsp
trunk/wao-web/src/main/webapp/wao.css
Modified: trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/edit-news-input.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/edit-news-input.jsp 2014-04-14 08:47:03 UTC (rev 1882)
+++ trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/edit-news-input.jsp 2014-04-14 09:55:25 UTC (rev 1883)
@@ -23,16 +23,153 @@
<html>
<head>
+ <link href="http://netdna.bootstrapcdn.com/font-awesome/3.0.2/css/font-awesome.css" rel="stylesheet">
+ <script type="text/javascript" src="<s:url value='/js/jquery.hotkeys/jquery.hotkeys.js' />"></script>
+ <script type="text/javascript" src="<s:url value='/js/bootstrap-wysiwyg/bootstrap-wysiwyg.js' />"></script>
+ <script>
+
+
+ function initToolbarBootstrapBindings() {
+ var fonts = ['Serif', 'Sans', 'Arial', 'Arial Black', 'Courier',
+ 'Courier New', 'Comic Sans MS', 'Helvetica', 'Impact', 'Lucida Grande', 'Lucida Sans', 'Tahoma', 'Times',
+ 'Times New Roman', 'Verdana'],
+ fontTarget = $('#fontMenu').siblings('.dropdown-menu');
+ $.each(fonts, function (idx, fontName) {
+ fontTarget.append($('<li><a data-edit="fontName ' + fontName +'" style="font-family:\''+ fontName +'\'">'+fontName + '</a></li>'));
+ });
+
+ $('a[title]').tooltip({container:'body'});
+ $('.dropdown-menu input').click(function() {return false;})
+ .change(function () {$(this).parent('.dropdown-menu').siblings('.dropdown-toggle').dropdown('toggle');})
+ .keydown('esc', function () {this.value='';$(this).change();});
+
+ $('[data-role=magic-overlay]').each(function () {
+ var overlay = $(this), target = $(overlay.data('target'));
+ overlay.css('opacity', 0).css('position', 'absolute').offset(target.offset()).width(target.outerWidth()).height(target.outerHeight());
+ });
+ };
+
+ $(document).ready(function () {
+ initToolbarBootstrapBindings();
+ $("#editor").wysiwyg();
+ });
+
+ function save () {
+ var content = $('#editor').cleanHtml()
+ $('#edit-news_news_content').val(content);
+ }
+
+ </script>
</head>
- <s:form>
+ <s:form onsubmit="save();">
<s:hidden name="newsId" value="%{newsId}" />
<s:textfield name="news.title" label="%{getText('wao.ui.news.title')}" cssClass="input-xxlarge" />
- <s:textarea name="news.content" label="%{getText('wao.ui.news.content')}" />
+ <div class="control-group ">
+ <label class="control-label" for="editor"><s:property value="%{getText('wao.ui.news.content')}" /></label>
+ <div class="btn-toolbar" data-role="editor-toolbar" data-target="#editor">
+ <div class="btn-group">
+ <a class="btn dropdown-toggle"
+ data-toggle="dropdown"
+ data-original-title="Font"
+ id="fontMenu"
+ title="Polices"><i class="icon-font"></i><b class="caret"></b></a>
+ <ul class="dropdown-menu">
+ </ul>
+ </div>
+
+ <div class="btn-group">
+ <a class="btn dropdown-toggle"
+ data-toggle="dropdown"
+ data-original-title="Font Size"
+ title="Tailles"><i class="icon-text-height"></i> <b class="caret"></b></a>
+ <ul class="dropdown-menu">
+ <li><a data-edit="fontSize 5"><font size="5">Grand</font></a></li>
+ <li><a data-edit="fontSize 3"><font size="3">Normal</font></a></li>
+ <li><a data-edit="fontSize 1"><font size="1">Petit</font></a></li>
+ </ul>
+ </div>
+
+ <div class="btn-group">
+ <a class="btn dropdown-toggle"
+ data-toggle="dropdown"
+ data-original-title="Color"
+ title="Couleur"><i class="icon-tint"></i> <b class="caret"></b></a>
+ <ul class="dropdown-menu">
+ <li>
+ <a data-edit="foreColor #000000"> <font color="#000000" >Noir</font></a>
+ </li>
+ <li>
+ <a data-edit="foreColor #c0c0c0"> <font color="#c0c0c0" >Gris</font></a>
+ </li>
+ <li>
+ <a data-edit="foreColor #ff0000"> <font color="#ff0000" >Rouge</font></a>
+ </li>
+ <li>
+ <a data-edit="foreColor #00ff00"> <font color="#00ff00" >Vert</font></a>
+ </li>
+ <li>
+ <a data-edit="foreColor #0000ff"> <font color="#0000ff" >Bleu</font></a>
+ </li>
+ <li>
+ <a data-edit="foreColor #ffff00"> <font color="#ffff00" >Jaune</font></a>
+ </li>
+ <li>
+ <a data-edit="foreColor #00ffff"> <font color="#00ffff" >Cyan </font></a>
+ </li>
+ <li>
+ <a data-edit="foreColor #ff00ff"> <font color="#ff00ff" >Magenta</font></a>
+ </li>
+ <li>
+ </ul>
+ </div>
+
+ <div class="btn-group">
+ <a class="btn" data-edit="bold" title="Gras (Ctrl + B)"><i class="icon-bold"></i></a>
+ <a class="btn" data-edit="italic" title="Italic (Ctrl + I)"><i class="icon-italic"></i></a>
+ <a class="btn" data-edit="strikethrough" title="Barré"><i class="icon-strikethrough"></i></a>
+ <a class="btn" data-edit="underline" title="sous-ligné (Ctrl + U)"><i class="icon-underline"></i></a>
+ </div>
+ <div class="btn-group">
+ <a class="btn" data-edit="insertunorderedlist" title="Puces"><i class="icon-list-ul"></i></a>
+ <a class="btn" data-edit="insertorderedlist" title="Numérotation"><i class="icon-list-ol"></i></a>
+ <a class="btn" data-edit="outdent" title="Réduire de retrait (Maj + Tab)"><i class="icon-indent-left"></i></a>
+ <a class="btn" data-edit="indent" title="Augmenter le retrait (Tab)"><i class="icon-indent-right"></i></a>
+ </div>
+ <div class="btn-group">
+ <a class="btn" data-edit="justifyleft" title="Aligner à gauche (Ctrl + L)"><i class="icon-align-left"></i></a>
+ <a class="btn" data-edit="justifycenter" title="Centré (Ctrl + E)"><i class="icon-align-center"></i></a>
+ <a class="btn" data-edit="justifyright" title="Aligner à droite (Ctrl/Cmd+R)"><i class="icon-align-right"></i></a>
+ <a class="btn" data-edit="justifyfull" title="Justifié (Ctrl + J)"><i class="icon-align-justify"></i></a>
+ </div>
+ <div class="btn-group">
+ <a class="btn dropdown-toggle" data-toggle="dropdown" title="Hyperlien"><i class="icon-link"></i></a>
+ <div class="dropdown-menu input-append">
+ <input class="span2" placeholder="URL" type="text" data-edit="createLink"/>
+ <button class="btn" type="button">Add</button>
+ </div>
+ <a class="btn" data-edit="unlink" title="Supprimer l'hyperlien"><i class="icon-cut"></i></a>
+ </div>
+
+ <div class="btn-group">
+ <a class="btn" data-edit="undo" title="Annuler (Ctrl + Z)"><i class="icon-undo"></i></a>
+ <a class="btn" data-edit="redo" title="Rétablir (Ctrl + Y)"><i class="icon-repeat"></i></a>
+ </div>
+ </div>
+
+ <div class="controls">
+ <div id="editor">
+ <s:property value="news.content" escapeHtml="false"/>
+ </div>
+ </div>
+ </div>
+
+ <s:hidden name="news.content"/>
+
<div class="form-actions">
<s:url action="news" id="newsUrl" />
<s:a href="%{newsUrl}" cssClass="btn">
Modified: trunk/wao-web/src/main/webapp/wao.css
===================================================================
--- trunk/wao-web/src/main/webapp/wao.css 2014-04-14 08:47:03 UTC (rev 1882)
+++ trunk/wao-web/src/main/webapp/wao.css 2014-04-14 09:55:25 UTC (rev 1883)
@@ -298,3 +298,18 @@
/*max-width: 35%;*/
flex: 1;
}
+#editor {
+ max-height: 250px;
+ height: 250px;
+ background-color: white;
+ border-collapse: separate;
+ border: 1px solid rgb(204, 204, 204);
+ padding: 4px;
+ box-sizing: content-box;
+ box-shadow: rgba(0, 0, 0, 0.0745098) 0px 1px 1px 0px inset;
+ border-top-right-radius: 3px; border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px; border-top-left-radius: 3px;
+ overflow: scroll;
+ outline: none;
+}
+
1
0
r1882 - in trunk/wao-web/src/main: java/fr/ifremer/wao/web/action/obsmer webapp/WEB-INF/content/obsmer
by bleny@users.forge.codelutin.com 14 Apr '14
by bleny@users.forge.codelutin.com 14 Apr '14
14 Apr '14
Author: bleny
Date: 2014-04-14 10:47:03 +0200 (Mon, 14 Apr 2014)
New Revision: 1882
Url: http://forge.codelutin.com/projects/wao/repository/revisions/1882
Log:
refs #4490 tune boat details
Modified:
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/BoatDetailsAction.java
trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/boat-details.jsp
Modified: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/BoatDetailsAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/BoatDetailsAction.java 2014-04-11 09:50:25 UTC (rev 1881)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/obsmer/BoatDetailsAction.java 2014-04-14 08:47:03 UTC (rev 1882)
@@ -77,6 +77,7 @@
startBoatSelectionForSampleRow = obsMerSamplingPlanService.getSampleRow(startBoatSelectionForSampleRowId);
}
+ // TODO brendan 14/04/14 really useful ?
sampleRowsToSetElligible = obsMerSamplingPlanService.getUnfinishedSampleRows(getAuthenticatedWaoUser(), companyId);
return SUCCESS;
Modified: trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/boat-details.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/boat-details.jsp 2014-04-11 09:50:25 UTC (rev 1881)
+++ trunk/wao-web/src/main/webapp/WEB-INF/content/obsmer/boat-details.jsp 2014-04-14 08:47:03 UTC (rev 1882)
@@ -34,7 +34,7 @@
$('#save-boat-infos').click(function () {
var data = {};
- $boatInfosForm.find('input').each(function (index, input) {
+ $boatInfosForm.find('input, textarea').each(function (index, input) {
var $input = $(input);
var parameterName = $input.prop('name');
if (parameterName.indexOf('__') === 0) {
@@ -55,7 +55,7 @@
});
$('#unlock-boat-infos-form').click(function () {
- $boatInfosForm.find('input[readonly], button').removeAttr('readonly').removeAttr('disabled');
+ $boatInfosForm.find('input[readonly], button, textarea').removeAttr('readonly').removeAttr('disabled');
$(this).attr('disabled', 'disabled');
});
});
@@ -70,16 +70,16 @@
<s:hidden name="boatId" value="%{boatDetails.boatInfos.boat.topiaId}" />
<s:hidden name="companyId" value="%{boatDetails.boatInfos.company.topiaId}" />
- <s:textfield name="boatInfos.contactFirstName" value="%{boatDetails.boatInfos.contactFirstName}" label="%{getText('wao.ui.field.BoatInfos.contactFirstName')}" readonly="true" />
- <s:textfield name="boatInfos.contactLastName" value="%{boatDetails.boatInfos.contactLastName}" label="%{getText('wao.ui.field.BoatInfos.contactLastName')}" readonly="true" />
- <s:textfield type="email" name="boatInfos.contactEmail" value="%{boatDetails.boatInfos.contactEmail}" label="%{getText('wao.ui.field.BoatInfos.contactEmail')}" readonly="true" />
- <s:textfield type="phone" name="boatInfos.contactPhoneNumber" value="%{boatDetails.boatInfos.contactPhoneNumber}" label="%{getText('wao.ui.field.BoatInfos.contactPhoneNumber')}" readonly="true" />
- <s:textfield type="number" name="boatInfos.dup" value="%{boatDetails.boatInfos.dup}" label="%{getText('wao.ui.field.BoatInfos.dup')}" readonly="true" />
- <s:textfield name="boatInfos.contactAddress1" value="%{boatDetails.boatInfos.contactAddress1}" label="%{getText('wao.ui.field.BoatInfos.contactAddress1')}" readonly="true" />
- <s:textfield name="boatInfos.contactAddress2" value="%{boatDetails.boatInfos.contactAddress2}" label="%{getText('wao.ui.field.BoatInfos.contactAddress2')}" readonly="true" />
- <s:textfield name="boatInfos.contactPostalCode" value="%{boatDetails.boatInfos.contactPostalCode}" label="%{getText('wao.ui.field.BoatInfos.contactPostalCode')}" readonly="true" />
- <s:textfield name="boatInfos.contactCity" value="%{boatDetails.boatInfos.contactCity}" label="%{getText('wao.ui.field.BoatInfos.contactCity')}" readonly="true" />
- <s:textfield name="boatInfos.comment" value="%{boatDetails.boatInfos.comment}" label="%{getText('wao.ui.field.BoatInfos.comment')}" readonly="true" />
+ <s:textfield name="boatInfos.contactFirstName" value="%{boatDetails.boatInfos.contactFirstName}" label="%{getText('wao.ui.field.BoatInfos.contactFirstName')}" readonly="true" cssClass="input-large" />
+ <s:textfield name="boatInfos.contactLastName" value="%{boatDetails.boatInfos.contactLastName}" label="%{getText('wao.ui.field.BoatInfos.contactLastName')}" readonly="true" cssClass="input-large" />
+ <s:textfield type="email" name="boatInfos.contactEmail" value="%{boatDetails.boatInfos.contactEmail}" label="%{getText('wao.ui.field.BoatInfos.contactEmail')}" readonly="true" cssClass="input-large" />
+ <s:textfield type="phone" name="boatInfos.contactPhoneNumber" value="%{boatDetails.boatInfos.contactPhoneNumber}" label="%{getText('wao.ui.field.BoatInfos.contactPhoneNumber')}" readonly="true" cssClass="input-large" />
+ <s:textfield type="number" name="boatInfos.dup" value="%{boatDetails.boatInfos.dup}" label="%{getText('wao.ui.field.BoatInfos.dup')}" readonly="true" cssClass="input-small" />
+ <s:textfield name="boatInfos.contactAddress1" value="%{boatDetails.boatInfos.contactAddress1}" label="%{getText('wao.ui.field.BoatInfos.contactAddress1')}" readonly="true" cssClass="input-xxlarge" />
+ <s:textfield name="boatInfos.contactAddress2" value="%{boatDetails.boatInfos.contactAddress2}" label="%{getText('wao.ui.field.BoatInfos.contactAddress2')}" readonly="true" cssClass="input-xxlarge" />
+ <s:textfield name="boatInfos.contactPostalCode" value="%{boatDetails.boatInfos.contactPostalCode}" label="%{getText('wao.ui.field.BoatInfos.contactPostalCode')}" readonly="true" cssClass="input-small" />
+ <s:textfield name="boatInfos.contactCity" value="%{boatDetails.boatInfos.contactCity}" label="%{getText('wao.ui.field.BoatInfos.contactCity')}" readonly="true" cssClass="input-large" />
+ <s:textarea name="boatInfos.comment" value="%{boatDetails.boatInfos.comment}" label="%{getText('wao.ui.field.BoatInfos.comment')}" readonly="true" cssClass="input-xxlarge" />
<div class="form-actions">
<button type="button" id="unlock-boat-infos-form" class="btn">
@@ -232,9 +232,6 @@
</s:else>
</s:else>
- <s:iterator value="sampleRowsToSetElligible">
- <s:property value="code"/>
- </s:iterator>
</s:if>
<s:else>
<div class="alert">
1
0
r1881 - trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration
by bleny@users.forge.codelutin.com 11 Apr '14
by bleny@users.forge.codelutin.com 11 Apr '14
11 Apr '14
Author: bleny
Date: 2014-04-11 11:50:25 +0200 (Fri, 11 Apr 2014)
New Revision: 1881
Url: http://forge.codelutin.com/projects/wao/repository/revisions/1881
Log:
refs #4560 do not sort user by active criteria
Modified:
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java 2014-04-11 09:36:02 UTC (rev 1880)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/administration/WaoUsersService.java 2014-04-11 09:50:25 UTC (rev 1881)
@@ -63,7 +63,7 @@
query.addTopiaIdEquals(WaoUser.PROPERTY_COMPANY, optionalCompanyId.get());
}
- query.setOrderByArguments(WaoUser.PROPERTY_ACTIVE + " desc", WaoUser.PROPERTY_LOGIN);
+ query.setOrderByArguments(WaoUser.PROPERTY_LOGIN);
List<WaoUser> waoUsers = query.findAll();
1
0
r1880 - in trunk/wao-web/src/main: java/fr/ifremer/wao/web/action/authentication webapp webapp/WEB-INF/content/authentication
by bleny@users.forge.codelutin.com 11 Apr '14
by bleny@users.forge.codelutin.com 11 Apr '14
11 Apr '14
Author: bleny
Date: 2014-04-11 11:36:02 +0200 (Fri, 11 Apr 2014)
New Revision: 1880
Url: http://forge.codelutin.com/projects/wao/repository/revisions/1880
Log:
fixes #4496 prevent user need to double input credentials
Modified:
trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/authentication/LoginAction.java
trunk/wao-web/src/main/webapp/WEB-INF/content/authentication/login.jsp
trunk/wao-web/src/main/webapp/wao.css
Modified: trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/authentication/LoginAction.java
===================================================================
--- trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/authentication/LoginAction.java 2014-04-11 09:00:25 UTC (rev 1879)
+++ trunk/wao-web/src/main/java/fr/ifremer/wao/web/action/authentication/LoginAction.java 2014-04-11 09:36:02 UTC (rev 1880)
@@ -48,6 +48,8 @@
protected transient WaoUsersService service;
+ protected boolean credentialsAsked = true;
+
protected String login;
protected String password;
@@ -93,6 +95,8 @@
waoUser = service.authenticate(login, password);
+ credentialsAsked = false;
+
if (cguAccepted) {
service.acceptCgu(waoUser);
}
@@ -177,4 +181,8 @@
public Map<String, String> getUserProfiles() {
return userProfiles;
}
+
+ public boolean isCredentialsAsked() {
+ return credentialsAsked;
+ }
}
Modified: trunk/wao-web/src/main/webapp/WEB-INF/content/authentication/login.jsp
===================================================================
--- trunk/wao-web/src/main/webapp/WEB-INF/content/authentication/login.jsp 2014-04-11 09:00:25 UTC (rev 1879)
+++ trunk/wao-web/src/main/webapp/WEB-INF/content/authentication/login.jsp 2014-04-11 09:36:02 UTC (rev 1880)
@@ -38,12 +38,23 @@
<fieldset>
- <s:textfield name="login"
- label="%{getText('wao.ui.field.WaoUser.login')}" />
+ <s:if test="credentialsAsked">
- <s:password name="password"
- label="%{getText('wao.ui.field.WaoUser.password')}" />
+ <s:textfield name="login"
+ label="%{getText('wao.ui.field.WaoUser.login')}"/>
+ <s:password name="password"
+ label="%{getText('wao.ui.field.WaoUser.password')}"/>
+
+ </s:if>
+ <s:else>
+
+ <s:hidden name="login" label="%{login}"/>
+
+ <s:hidden name="password" value="%{password}"/>
+
+ </s:else>
+
<s:if test="userProfileAsked">
<s:radio name="userProfileId" list="userProfiles" label="%{getText('wao.ui.form.authentication.chooseUserProfile')}" />
@@ -58,10 +69,17 @@
</fieldset>
- <s:submit type="button" cssClass="btn btn-primary">
- <s:text name="wao.ui.form.authentication.action.submit" />
- </s:submit>
+ <div class="form-actions">
+ <s:submit type="button" cssClass="btn btn-primary">
+ <s:text name="wao.ui.form.authentication.action.submit"/>
+ </s:submit>
+ <s:url namespace="/authentication" action="login!input" id="cancelUrl"/>
+ <s:a href="%{cancelUrl}" cssClass="btn">
+ <s:text name="wao.ui.action.cancel"/>
+ </s:a>
+ </div>
+
</s:form>
<s:if test="acceptCguAsked">
Modified: trunk/wao-web/src/main/webapp/wao.css
===================================================================
--- trunk/wao-web/src/main/webapp/wao.css 2014-04-11 09:00:25 UTC (rev 1879)
+++ trunk/wao-web/src/main/webapp/wao.css 2014-04-11 09:36:02 UTC (rev 1880)
@@ -140,15 +140,20 @@
margin-top: 75px;
}
+form#login .form-actions,
+form.filters-form .form-actions,
+#boat-details .form-actions {
+ clear: both;
+ margin-top: 0px;
+ border: none;
+ background-color: inherit;
+}
+
textarea {
width: 100%;
min-height: 150px;
}
-.form-actions button[type="submit"] {
- float: right;
-}
-
th.actions, td.actions {
white-space: nowrap;
}
@@ -184,13 +189,6 @@
margin-right: 5px;
}
-form.filters-form .form-actions {
- clear: both;
- margin-top: 0px;
- border: none;
- background-color: white;
-}
-
/******************************************************************************
* Styles spécifiques pour certaines pages de l'appli
*****************************************************************************/
@@ -204,10 +202,6 @@
text-align: right;
}
-form#login button[type="submit"] {
- margin-left: 180px;
-}
-
#cgu {
overflow-y: auto;
max-height: 300px;
@@ -304,9 +298,3 @@
/*max-width: 35%;*/
flex: 1;
}
-
-#boat-details .form-actions {
- margin-top: 15px;
- border: none;
- background-color: inherit;
-}
1
0
r1879 - trunk/wao-services/src/main/java/fr/ifremer/wao/services/service
by bleny@users.forge.codelutin.com 11 Apr '14
by bleny@users.forge.codelutin.com 11 Apr '14
11 Apr '14
Author: bleny
Date: 2014-04-11 11:00:25 +0200 (Fri, 11 Apr 2014)
New Revision: 1879
Url: http://forge.codelutin.com/projects/wao/repository/revisions/1879
Log:
refs #4483 consider a new sample row as recently updated
Modified:
trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlan.java
Modified: trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlan.java
===================================================================
--- trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlan.java 2014-04-10 15:57:31 UTC (rev 1878)
+++ trunk/wao-services/src/main/java/fr/ifremer/wao/services/service/ObsMerSamplingPlan.java 2014-04-11 09:00:25 UTC (rev 1879)
@@ -436,11 +436,12 @@
samplingStrategy = WaoUtils.l(locale,sampleRow.getSamplingStrategy());
sampleRowId = sampleRow.getTopiaId();
+ // consider a recently created sample row as recently updated
+ latestSampleLogCreateDate = sampleRow.getTopiaCreateDate();
// compute lastSampleLogCreateDate
if (sampleRow.isSampleRowLogNotEmpty()) {
for (SampleRowLog sampleRowLog : sampleRow.getSampleRowLog()) {
- if (latestSampleLogCreateDate == null ||
- sampleRowLog.getCreateDate().after(latestSampleLogCreateDate)) {
+ if (sampleRowLog.getCreateDate().after(latestSampleLogCreateDate)) {
latestSampleLogCreateDate = sampleRowLog.getCreateDate();
}
}
1
0
Jenkins build is back to normal : wao-nightly » Wao :: Persistence #32
by admin+ci-codelutin.com@codelutin.com 10 Apr '14
by admin+ci-codelutin.com@codelutin.com 10 Apr '14
10 Apr '14