aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.externalToolBuilders/docs.launch11
-rw-r--r--.externalToolBuilders/gen_docs.launch7
-rw-r--r--.idea/.gitignore1
-rw-r--r--.idea/.rakeTasks7
-rw-r--r--.idea/angular.js.iml (renamed from .idea/master.iml)2
-rw-r--r--.idea/misc.xml5
-rw-r--r--.idea/modules.xml2
-rw-r--r--.idea/projectCodeStyle.xml59
-rw-r--r--.idea/runConfigurations/gen_docs.xml15
-rw-r--r--.idea/runConfigurations/rake_compile.xml15
-rw-r--r--.idea/vcs.xml3
-rw-r--r--.idea/workspace.xml321
-rw-r--r--.project10
-rw-r--r--.settings/.jsdtscope2
-rw-r--r--Rakefile24
-rw-r--r--docs/callback.js66
-rw-r--r--docs/collect.js206
-rw-r--r--docs/docs-data.js1
-rw-r--r--docs/docs-scenario.html9
-rw-r--r--docs/docs-scenario.js9
-rw-r--r--docs/docs.js7
-rw-r--r--docs/filter.template33
-rw-r--r--docs/filter:currency.html21
-rw-r--r--docs/filter:currency.md20
-rw-r--r--docs/index.html25
-rw-r--r--docs/overview.template1
-rw-r--r--docs/wiki_widgets.css58
-rw-r--r--docs/wiki_widgets.js51
-rwxr-xr-xgen_docs.sh3
-rw-r--r--lib/markdown/Index.js1446
-rw-r--r--lib/mustache/LICENSE21
-rw-r--r--lib/mustache/index.js344
-rw-r--r--lib/nodeserver/server.js20
-rw-r--r--src/Angular.js10
-rw-r--r--src/filters.js30
-rw-r--r--src/widgets.js14
36 files changed, 2791 insertions, 88 deletions
diff --git a/.externalToolBuilders/docs.launch b/.externalToolBuilders/docs.launch
new file mode 100644
index 00000000..80cd1429
--- /dev/null
+++ b/.externalToolBuilders/docs.launch
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType">
+<stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;resources&gt;&#10;&lt;item path=&quot;/angular.js/docs&quot; type=&quot;2&quot;/&gt;&#10;&lt;/resources&gt;}"/>
+<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
+<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_BUILDER_ENABLED" value="false"/>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_BUILD_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;resources&gt;&#10;&lt;item path=&quot;/angular.js/docs/callback.js&quot; type=&quot;1&quot;/&gt;&#10;&lt;item path=&quot;/angular.js/docs/collect.js&quot; type=&quot;1&quot;/&gt;&#10;&lt;item path=&quot;/angular.js/docs/filter.template&quot; type=&quot;1&quot;/&gt;&#10;&lt;/resources&gt;}"/>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/angular.js/gen_docs.sh}"/>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,auto,"/>
+<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/angular.js}"/>
+</launchConfiguration>
diff --git a/.externalToolBuilders/gen_docs.launch b/.externalToolBuilders/gen_docs.launch
new file mode 100644
index 00000000..5898ce80
--- /dev/null
+++ b/.externalToolBuilders/gen_docs.launch
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.ui.externaltools.ProgramLaunchConfigurationType">
+<stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;resources&gt;&#10;&lt;item path=&quot;/angular.js/docs&quot; type=&quot;2&quot;/&gt;&#10;&lt;/resources&gt;}"/>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LAUNCH_CONFIGURATION_BUILD_SCOPE" value="${none}"/>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/angular.js/gen_docs.sh}"/>
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/angular.js}"/>
+</launchConfiguration>
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index a7c382ed..00000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-workspace.xml
diff --git a/.idea/.rakeTasks b/.idea/.rakeTasks
deleted file mode 100644
index 50fb6fec..00000000
--- a/.idea/.rakeTasks
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Settings><!--This file was automatically generated by Ruby plugin.
-You are allowed to:
-1. Remove rake task
-2. Add existing rake tasks
-To add existing rake tasks automatically delete this file and reload the project.
---><RakeGroup description="" fullCmd="" taksId="rake"><RakeTask description="Compile JavaScript" fullCmd="compile" taksId="compile" /><RakeTask description="Generate Externs" fullCmd="compileexterns" taksId="compileexterns" /><RakeTask description="Lint" fullCmd="lint" taksId="lint" /><RakeGroup description="" fullCmd="" taksId="server"><RakeTask description="Run JsTestDriver Server" fullCmd="server:start" taksId="start" /><RakeTask description="Run JavaScript tests against the server" fullCmd="server:test" taksId="test" /></RakeGroup><RakeTask description="Run JavaScript tests" fullCmd="test" taksId="test" /><RakeTask description="" fullCmd="default" taksId="default" /></RakeGroup></Settings>
diff --git a/.idea/master.iml b/.idea/angular.js.iml
index 8f7472a8..6b8184f8 100644
--- a/.idea/master.iml
+++ b/.idea/angular.js.iml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<module type="RUBY_MODULE" version="4">
+<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
diff --git a/.idea/misc.xml b/.idea/misc.xml
index bf08d02d..f7e602b4 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -3,10 +3,7 @@
<component name="DependencyValidationManager">
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</component>
- <component name="ProjectDetails">
- <option name="projectName" value="master" />
- </component>
- <component name="ProjectRootManager" version="2" project-jdk-name="Ruby SDK 1.8.7 (/usr/bin/ruby)" project-jdk-type="RUBY_SDK" />
+ <component name="ProjectRootManager" version="2" />
<component name="SvnBranchConfigurationManager">
<option name="mySupportsUserInfoFilter" value="true" />
</component>
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 12b24804..3bb010cf 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -2,7 +2,7 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
- <module fileurl="file://$PROJECT_DIR$/.idea/master.iml" filepath="$PROJECT_DIR$/.idea/master.iml" />
+ <module fileurl="file://$PROJECT_DIR$/.idea/angular.js.iml" filepath="$PROJECT_DIR$/.idea/angular.js.iml" />
</modules>
</component>
</project>
diff --git a/.idea/projectCodeStyle.xml b/.idea/projectCodeStyle.xml
new file mode 100644
index 00000000..a5c450f7
--- /dev/null
+++ b/.idea/projectCodeStyle.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="CodeStyleSettingsManager">
+ <option name="PER_PROJECT_SETTINGS">
+ <value>
+ <option name="USE_SAME_INDENTS" value="true" />
+ <option name="OTHER_INDENT_OPTIONS">
+ <value>
+ <option name="INDENT_SIZE" value="2" />
+ <option name="CONTINUATION_INDENT_SIZE" value="4" />
+ <option name="TAB_SIZE" value="2" />
+ <option name="USE_TAB_CHARACTER" value="false" />
+ <option name="SMART_TABS" value="false" />
+ <option name="LABEL_INDENT_SIZE" value="0" />
+ <option name="LABEL_INDENT_ABSOLUTE" value="false" />
+ </value>
+ </option>
+ <ADDITIONAL_INDENT_OPTIONS fileType="js">
+ <option name="INDENT_SIZE" value="4" />
+ <option name="CONTINUATION_INDENT_SIZE" value="8" />
+ <option name="TAB_SIZE" value="4" />
+ <option name="USE_TAB_CHARACTER" value="false" />
+ <option name="SMART_TABS" value="false" />
+ <option name="LABEL_INDENT_SIZE" value="0" />
+ <option name="LABEL_INDENT_ABSOLUTE" value="false" />
+ </ADDITIONAL_INDENT_OPTIONS>
+ <ADDITIONAL_INDENT_OPTIONS fileType="sass">
+ <option name="INDENT_SIZE" value="2" />
+ <option name="CONTINUATION_INDENT_SIZE" value="8" />
+ <option name="TAB_SIZE" value="4" />
+ <option name="USE_TAB_CHARACTER" value="false" />
+ <option name="SMART_TABS" value="false" />
+ <option name="LABEL_INDENT_SIZE" value="0" />
+ <option name="LABEL_INDENT_ABSOLUTE" value="false" />
+ </ADDITIONAL_INDENT_OPTIONS>
+ <ADDITIONAL_INDENT_OPTIONS fileType="xml">
+ <option name="INDENT_SIZE" value="4" />
+ <option name="CONTINUATION_INDENT_SIZE" value="8" />
+ <option name="TAB_SIZE" value="4" />
+ <option name="USE_TAB_CHARACTER" value="false" />
+ <option name="SMART_TABS" value="false" />
+ <option name="LABEL_INDENT_SIZE" value="0" />
+ <option name="LABEL_INDENT_ABSOLUTE" value="false" />
+ </ADDITIONAL_INDENT_OPTIONS>
+ <ADDITIONAL_INDENT_OPTIONS fileType="yml">
+ <option name="INDENT_SIZE" value="2" />
+ <option name="CONTINUATION_INDENT_SIZE" value="8" />
+ <option name="TAB_SIZE" value="4" />
+ <option name="USE_TAB_CHARACTER" value="false" />
+ <option name="SMART_TABS" value="false" />
+ <option name="LABEL_INDENT_SIZE" value="0" />
+ <option name="LABEL_INDENT_ABSOLUTE" value="false" />
+ </ADDITIONAL_INDENT_OPTIONS>
+ </value>
+ </option>
+ <option name="USE_PER_PROJECT_SETTINGS" value="true" />
+ </component>
+</project>
+
diff --git a/.idea/runConfigurations/gen_docs.xml b/.idea/runConfigurations/gen_docs.xml
new file mode 100644
index 00000000..23afac13
--- /dev/null
+++ b/.idea/runConfigurations/gen_docs.xml
@@ -0,0 +1,15 @@
+<component name="ProjectRunConfigurationManager">
+ <configuration default="false" name="gen_docs" type="BashConfigurationType" factoryName="Bash">
+ <option name="INTERPRETER_OPTIONS" value="" />
+ <option name="INTERPRETER_PATH" value="/bin/bash" />
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+ <option name="PARENT_ENVS" value="true" />
+ <envs />
+ <module name="" />
+ <option name="SCRIPT_NAME" value="$PROJECT_DIR$/gen_docs.sh" />
+ <option name="PARAMETERS" value="" />
+ <RunnerSettings RunnerId="BashRunner" />
+ <ConfigurationWrapper RunnerId="BashRunner" />
+ <method />
+ </configuration>
+</component> \ No newline at end of file
diff --git a/.idea/runConfigurations/rake_compile.xml b/.idea/runConfigurations/rake_compile.xml
new file mode 100644
index 00000000..d69f3466
--- /dev/null
+++ b/.idea/runConfigurations/rake_compile.xml
@@ -0,0 +1,15 @@
+<component name="ProjectRunConfigurationManager">
+ <configuration default="false" name="rake compile" type="BashConfigurationType" factoryName="Bash">
+ <option name="INTERPRETER_OPTIONS" value="" />
+ <option name="INTERPRETER_PATH" value="/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby" />
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+ <option name="PARENT_ENVS" value="true" />
+ <envs />
+ <module name="" />
+ <option name="SCRIPT_NAME" value="/usr/bin/rake" />
+ <option name="PARAMETERS" value="compile" />
+ <RunnerSettings RunnerId="BashRunner" />
+ <ConfigurationWrapper RunnerId="BashRunner" />
+ <method />
+ </configuration>
+</component> \ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 9d32e507..275077f8 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
- <mapping directory="" vcs="" />
- <mapping directory="$PROJECT_DIR$" vcs="Git" />
+ <mapping directory="" vcs="Git" />
</component>
</project>
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 00000000..62adcd33
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,321 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ChangeListManager">
+ <list default="true" readonly="true" id="2e561485-a685-4e95-bea4-cabeda87d953" name="Default" comment="" />
+ <ignored path=".idea/workspace.xml" />
+ <ignored path="angular.js.iws" />
+ <option name="TRACKING_ENABLED" value="true" />
+ <option name="SHOW_DIALOG" value="false" />
+ <option name="HIGHLIGHT_CONFLICTS" value="true" />
+ <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
+ <option name="LAST_RESOLUTION" value="IGNORE" />
+ </component>
+ <component name="ChangesViewManager" flattened_view="true" show_ignored="false" />
+ <component name="CreatePatchCommitExecutor">
+ <option name="PATCH_PATH" value="" />
+ <option name="REVERSE_PATCH" value="false" />
+ </component>
+ <component name="DaemonCodeAnalyzer">
+ <disable_hints />
+ </component>
+ <component name="FavoritesManager">
+ <favorites_list name="angular.js" />
+ </component>
+ <component name="FileColors" enabled="true" enabledForTabs="true" />
+ <component name="FileEditorManager">
+ <leaf />
+ </component>
+ <component name="FindManager">
+ <FindUsagesManager>
+ <setting name="OPEN_NEW_TAB" value="false" />
+ </FindUsagesManager>
+ </component>
+ <component name="Git.Settings">
+ <option name="GIT_EXECUTABLE" value="/usr/local/git/bin/git" />
+ <option name="CHECKOUT_INCLUDE_TAGS" value="false" />
+ </component>
+ <component name="IdeDocumentHistory">
+ <option name="changedFiles">
+ <list>
+ <option value="$PROJECT_DIR$/lib/nodeserver/server.js" />
+ <option value="$PROJECT_DIR$/Rakefile" />
+ <option value="$PROJECT_DIR$/docs/collect.js" />
+ </list>
+ </option>
+ </component>
+ <component name="ProjectLevelVcsManager">
+ <OptionsSetting value="true" id="Add" />
+ <OptionsSetting value="true" id="Remove" />
+ <OptionsSetting value="true" id="Checkout" />
+ <OptionsSetting value="true" id="Update" />
+ <OptionsSetting value="true" id="Status" />
+ <OptionsSetting value="true" id="Edit" />
+ <ConfirmationsSetting value="0" id="Add" />
+ <ConfirmationsSetting value="0" id="Remove" />
+ </component>
+ <component name="ProjectReloadState">
+ <option name="STATE" value="0" />
+ </component>
+ <component name="ProjectView">
+ <navigator currentView="ProjectPane" proportions="" version="1" splitterProportion="0.5">
+ <flattenPackages />
+ <showMembers />
+ <showModules />
+ <showLibraryContents />
+ <hideEmptyPackages />
+ <abbreviatePackageNames />
+ <autoscrollToSource />
+ <autoscrollFromSource />
+ <sortByType />
+ </navigator>
+ <panes>
+ <pane id="Favorites" />
+ <pane id="Scope" />
+ <pane id="ProjectPane">
+ <subPane>
+ <PATH>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="angular.js" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+ </PATH_ELEMENT>
+ </PATH>
+ <PATH>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="angular.js" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+ </PATH_ELEMENT>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="angular.js" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+ </PATH_ELEMENT>
+ </PATH>
+ <PATH>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="angular.js" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+ </PATH_ELEMENT>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="angular.js" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+ </PATH_ELEMENT>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="build" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+ </PATH_ELEMENT>
+ </PATH>
+ <PATH>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="angular.js" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+ </PATH_ELEMENT>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="angular.js" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+ </PATH_ELEMENT>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="build" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+ </PATH_ELEMENT>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="pkg" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+ </PATH_ELEMENT>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="angular-0.9.2-a838b3ef" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+ </PATH_ELEMENT>
+ </PATH>
+ <PATH>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="angular.js" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+ </PATH_ELEMENT>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="angular.js" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+ </PATH_ELEMENT>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="build" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+ </PATH_ELEMENT>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="docs" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+ </PATH_ELEMENT>
+ </PATH>
+ </subPane>
+ </pane>
+ </panes>
+ </component>
+ <component name="PropertiesComponent">
+ <property name="options.splitter.main.proportions" value="0.3" />
+ <property name="WebServerToolWindowFactoryState" value="false" />
+ <property name="recentsLimit" value="5" />
+ <property name="options.lastSelected" value="project.propVCSSupport.VCSs.Git" />
+ <property name="GoToClass.includeJavaFiles" value="false" />
+ <property name="options.splitter.details.proportions" value="0.2" />
+ <property name="options.searchVisible" value="true" />
+ </component>
+ <component name="RunManager" selected="Bash.gen_docs.sh">
+ <configuration default="false" name="gen_docs.sh" type="BashConfigurationType" factoryName="Bash" temporary="true">
+ <option name="INTERPRETER_OPTIONS" value="" />
+ <option name="INTERPRETER_PATH" value="/bin/bash" />
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+ <option name="PARENT_ENVS" value="true" />
+ <envs />
+ <module name="angular.js" />
+ <option name="SCRIPT_NAME" value="$PROJECT_DIR$/gen_docs.sh" />
+ <option name="PARAMETERS" value="" />
+ <RunnerSettings RunnerId="BashRunner" />
+ <ConfigurationWrapper RunnerId="BashRunner" />
+ <method />
+ </configuration>
+ <configuration default="true" type="BashConfigurationType" factoryName="Bash">
+ <option name="INTERPRETER_OPTIONS" value="" />
+ <option name="INTERPRETER_PATH" value="/bin/bash" />
+ <option name="WORKING_DIRECTORY" value="" />
+ <option name="PARENT_ENVS" value="true" />
+ <envs />
+ <module name="angular.js" />
+ <option name="SCRIPT_NAME" value="" />
+ <option name="PARAMETERS" value="" />
+ <method />
+ </configuration>
+ <list size="1">
+ <item index="0" class="java.lang.String" itemvalue="Bash.gen_docs.sh" />
+ </list>
+ </component>
+ <component name="ShelveChangesManager" show_recycled="false" />
+ <component name="SvnConfiguration" maxAnnotateRevisions="500">
+ <option name="USER" value="" />
+ <option name="PASSWORD" value="" />
+ <option name="LAST_MERGED_REVISION" />
+ <option name="UPDATE_RUN_STATUS" value="false" />
+ <option name="MERGE_DRY_RUN" value="false" />
+ <option name="MERGE_DIFF_USE_ANCESTRY" value="true" />
+ <option name="UPDATE_LOCK_ON_DEMAND" value="false" />
+ <option name="IGNORE_SPACES_IN_MERGE" value="false" />
+ <option name="DETECT_NESTED_COPIES" value="true" />
+ <option name="CHECK_NESTED_FOR_QUICK_MERGE" value="false" />
+ <option name="IGNORE_SPACES_IN_ANNOTATE" value="true" />
+ <option name="SHOW_MERGE_SOURCES_IN_ANNOTATE" value="true" />
+ <configuration useDefault="true">$PROJECT_DIR$/../../.subversion_IDEA</configuration>
+ <myIsUseDefaultProxy>false</myIsUseDefaultProxy>
+ <supportedVersion>125</supportedVersion>
+ </component>
+ <component name="TaskManager">
+ <task active="true" id="Default" summary="Default task">
+ <changelist id="2e561485-a685-4e95-bea4-cabeda87d953" name="Default" comment="" />
+ <created>1288738700234</created>
+ <updated>1288738700234</updated>
+ </task>
+ <servers />
+ </component>
+ <component name="ToolWindowManager">
+ <frame x="0" y="22" width="2560" height="1574" extended-state="6" />
+ <editor active="false" />
+ <layout>
+ <window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
+ <window_info id="Changes" active="true" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.32943603" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
+ <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
+ <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="true" content_ui="tabs" />
+ <window_info id="Dependency Viewer" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
+ <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.18020505" sideWeight="0.66574967" order="0" side_tool="false" content_ui="tabs" />
+ <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
+ <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32943603" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
+ <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
+ <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
+ <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
+ <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
+ <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
+ <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
+ <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
+ <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
+ </layout>
+ </component>
+ <component name="VcsManagerConfiguration">
+ <option name="OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT" value="true" />
+ <option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="true" />
+ <option name="PERFORM_UPDATE_IN_BACKGROUND" value="true" />
+ <option name="PERFORM_COMMIT_IN_BACKGROUND" value="true" />
+ <option name="PERFORM_EDIT_IN_BACKGROUND" value="true" />
+ <option name="PERFORM_CHECKOUT_IN_BACKGROUND" value="true" />
+ <option name="PERFORM_ADD_REMOVE_IN_BACKGROUND" value="true" />
+ <option name="PERFORM_ROLLBACK_IN_BACKGROUND" value="false" />
+ <option name="CHECK_LOCALLY_CHANGED_CONFLICTS_IN_BACKGROUND" value="false" />
+ <option name="ENABLE_BACKGROUND_PROCESSES" value="false" />
+ <option name="CHANGED_ON_SERVER_INTERVAL" value="60" />
+ <option name="FORCE_NON_EMPTY_COMMENT" value="false" />
+ <option name="LAST_COMMIT_MESSAGE" value="updated Rakefile to support packaging of the docs" />
+ <option name="MAKE_NEW_CHANGELIST_ACTIVE" value="true" />
+ <option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="false" />
+ <option name="CHECK_FILES_UP_TO_DATE_BEFORE_COMMIT" value="false" />
+ <option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="false" />
+ <option name="REFORMAT_BEFORE_FILE_COMMIT" value="false" />
+ <option name="FILE_HISTORY_DIALOG_COMMENTS_SPLITTER_PROPORTION" value="0.8" />
+ <option name="FILE_HISTORY_DIALOG_SPLITTER_PROPORTION" value="0.5" />
+ <option name="ACTIVE_VCS_NAME" />
+ <option name="UPDATE_GROUP_BY_PACKAGES" value="false" />
+ <option name="UPDATE_GROUP_BY_CHANGELIST" value="false" />
+ <option name="SHOW_FILE_HISTORY_AS_TREE" value="false" />
+ <option name="FILE_HISTORY_SPLITTER_PROPORTION" value="0.6" />
+ <MESSAGE value="move the output of generation to build" />
+ <MESSAGE value="updated Rakefile to support packaging of the docs" />
+ </component>
+ <component name="XDebuggerManager">
+ <breakpoint-manager />
+ </component>
+ <component name="XSLT-Support.FileAssociations.UIState">
+ <PATH>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="angular.js" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+ </PATH_ELEMENT>
+ </PATH>
+ </component>
+ <component name="editorHistoryManager">
+ <entry file="file://$PROJECT_DIR$/version.yaml">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="2" column="23" selection-start="50" selection-end="58" vertical-scroll-proportion="0.025559105">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/docs/collect.js">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="78" column="0" selection-start="2539" selection-end="2539" vertical-scroll-proportion="0.99680513">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/Rakefile">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="184" column="5" selection-start="5171" selection-end="5171" vertical-scroll-proportion="0.47603834">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/src/Resource.js">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="37" column="1" selection-start="1038" selection-end="1038" vertical-scroll-proportion="0.47284344">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/test/ResourceSpec.js">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="13" column="35" selection-start="399" selection-end="399" vertical-scroll-proportion="0.16613418">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/lib/nodeserver/server.js">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="37" column="0" selection-start="864" selection-end="864" vertical-scroll-proportion="0.4345048">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ </component>
+</project>
+
diff --git a/.project b/.project
index 0fb4c323..3bb6ad77 100644
--- a/.project
+++ b/.project
@@ -20,6 +20,16 @@
</dictionary>
</arguments>
</buildCommand>
+ <buildCommand>
+ <name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
+ <triggers>auto,full,incremental,</triggers>
+ <arguments>
+ <dictionary>
+ <key>LaunchConfigHandle</key>
+ <value>&lt;project&gt;/.externalToolBuilders/docs.launch</value>
+ </dictionary>
+ </arguments>
+ </buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
diff --git a/.settings/.jsdtscope b/.settings/.jsdtscope
index 7beec24e..a8f1ce8f 100644
--- a/.settings/.jsdtscope
+++ b/.settings/.jsdtscope
@@ -1,9 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry excluding="test/" kind="src" path="src"/>
+ <classpathentry kind="src" path="docs"/>
<classpathentry kind="src" path="src/test"/>
<classpathentry excluding="test/" kind="src" path="test"/>
<classpathentry kind="src" path="test/test"/>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
+ <classpathentry kind="output" path=""/>
</classpath>
diff --git a/Rakefile b/Rakefile
index a83b53ad..9a9835ba 100644
--- a/Rakefile
+++ b/Rakefile
@@ -156,8 +156,14 @@ task :compile => [:init, :compile_scenario, :generate_ie_compat] do
end
+desc 'Generate docs'
+task :docs do
+ `node docs/collect.js`
+end
+
+
desc 'Create angular distribution'
-task :package => [:clean, :compile] do
+task :package => [:clean, :compile, :docs] do
v = YAML::load( File.open( 'version.yaml' ) )['version']
match = v.match(/^([^-]*)(-snapshot)?$/)
version = match[1] + (match[2] ? ('-' + %x(git rev-parse HEAD)[0..7]) : '')
@@ -178,6 +184,20 @@ task :package => [:clean, :compile] do
FileUtils.cp(src, pkg_dir + '/' + dest)
end
+ FileUtils.cp_r path_to('docs'), "#{pkg_dir}/docs-#{version}"
+
+ File.open("#{pkg_dir}/docs-#{version}/index.html", File::RDWR) do |f|
+ text = f.read
+ f.rewind
+ f.write text.sub('angular.min.js', "angular-#{version}.min.js")
+ end
+
+ File.open("#{pkg_dir}/docs-#{version}/docs-scenario.html", File::RDWR) do |f|
+ text = f.read
+ f.rewind
+ f.write text.sub('angular-scenario.js', "angular-scenario-#{version}.js")
+ end
+
%x(tar -czf #{path_to(tarball)} -C #{path_to('pkg')} .)
puts "Package created: #{path_to(tarball)}"
@@ -256,5 +276,5 @@ end
# returns path to the file in the build directory
#
def path_to(filename)
- return File.join(BUILD_DIR, filename)
+ return File.join(BUILD_DIR, *filename)
end
diff --git a/docs/callback.js b/docs/callback.js
new file mode 100644
index 00000000..0d0669d1
--- /dev/null
+++ b/docs/callback.js
@@ -0,0 +1,66 @@
+function noop(){}
+
+function chain(delegateFn, explicitDone){
+ var onDoneFn = noop;
+ var onErrorFn = noop;
+ var waitForCount = 1;
+ delegateFn = delegateFn || noop;
+ var stackError = new Error('capture stack');
+
+ function decrementWaitFor() {
+ waitForCount--;
+ if (waitForCount == 0)
+ onDoneFn();
+ }
+
+ function self(){
+ try {
+ return delegateFn.apply(self, arguments);
+ } catch (error) {
+ self.error(error);
+ } finally {
+ if (!explicitDone)
+ decrementWaitFor();
+ }
+ };
+ self.onDone = function(callback){
+ onDoneFn = callback;
+ return self;
+ };
+ self.onError = function(callback){
+ onErrorFn = callback;
+ return self;
+ };
+ self.waitFor = function(callback){
+ if (waitForCount == 0)
+ throw new Error("Can not wait on already called callback.");
+ waitForCount++;
+ return chain(callback).onDone(decrementWaitFor).onError(self.error);
+ };
+
+ self.waitMany = function(callback){
+ if (waitForCount == 0)
+ throw new Error("Can not wait on already called callback.");
+ waitForCount++;
+ return chain(callback, true).onDone(decrementWaitFor).onError(self.error);
+ };
+
+ self.done = function(callback){
+ decrementWaitFor();
+ };
+
+ self.error = function(error) {
+ var stack = stackError.stack.split(/\n\r?/).splice(2);
+ var nakedStack = [];
+ stack.forEach(function(frame){
+ if (!frame.match(/callback\.js:\d+:\d+\)$/))
+ nakedStack.push(frame);
+ });
+ error.stack = error.stack + '\nCalled from:\n' + nakedStack.join('\n');
+ onErrorFn(error);
+ };
+
+ return self;
+}
+
+exports.chain = chain;
diff --git a/docs/collect.js b/docs/collect.js
new file mode 100644
index 00000000..a3c2a25c
--- /dev/null
+++ b/docs/collect.js
@@ -0,0 +1,206 @@
+var fs = require('fs'),
+ spawn = require('child_process').spawn,
+ mustache = require('../lib/mustache'),
+ callback = require('./callback'),
+ markdown = require('../lib/markdown');
+
+var documentation = {
+ section:{},
+ all:[]
+};
+
+var SRC_DIR = "docs/";
+var OUTPUT_DIR = "build/docs/";
+
+var work = callback.chain(function () {
+ console.log('Parsing Angular Reference Documentation');
+ mkdirPath(OUTPUT_DIR, work.waitFor(function(){
+ findJsFiles('src', work.waitMany(function(file) {
+ //console.log('reading', file, '...');
+ findNgDoc(file, work.waitMany(function(doc) {
+ parseNgDoc(doc);
+ if (doc.ngdoc) {
+ delete doc.raw.text;
+ var section = documentation.section;
+ (section[doc.ngdoc] = section[doc.ngdoc] || []).push(doc);
+ documentation.all.push(doc);
+ console.log('Found:', doc.ngdoc + ':' + doc.shortName);
+ mergeTemplate(
+ doc.ngdoc + '.template',
+ doc.name + '.html', doc, work.waitFor());
+ }
+ }));
+ }));
+ }));
+}).onError(function(err){
+ console.log('ERROR:', err.stack || err);
+}).onDone(function(){
+ mergeTemplate('docs-data.js', 'docs-data.js', {JSON:JSON.stringify(documentation)}, callback.chain());
+ mergeTemplate('docs-scenario.js', 'docs-scenario.js', documentation, callback.chain());
+ copy('docs-scenario.html', callback.chain());
+ copy('index.html', callback.chain());
+ mergeTemplate('docs.js', 'docs.js', documentation, callback.chain());
+ mergeTemplate('wiki_widgets.css', 'wiki_widgets.css', documentation, callback.chain());
+ mergeTemplate('wiki_widgets.js', 'wiki_widgets.js', documentation, callback.chain());
+ console.log('DONE');
+});
+work();
+////////////////////
+
+function noop(){}
+function mkdirPath(path, callback) {
+ var parts = path.split(/\//);
+ path = '.';
+ (function next(){
+ if (parts.length) {
+ path += '/' + parts.shift();
+ fs.mkdir(path, 0777, next);
+ } else {
+ callback();
+ }
+ })();
+}
+
+function copy(name, callback){
+ fs.readFile(SRC_DIR + name, callback.waitFor(function(err, content){
+ if (err) return this.error(err);
+ fs.writeFile(OUTPUT_DIR + name, content, callback);
+ }));
+}
+
+function mergeTemplate(template, output, doc, callback){
+ fs.readFile(SRC_DIR + template,
+ callback.waitFor(function(err, template){
+ if (err) return this.error(err);
+ var content = mustache.to_html(template.toString(), doc);
+ fs.writeFile(OUTPUT_DIR + output, content, callback);
+ }));
+}
+
+
+function unknownTag(doc, name) {
+ var error = "[" + doc.raw.file + ":" + doc.raw.line + "]: unknown tag: " + name;
+ console.log(error);
+ throw new Error(error);
+}
+
+function valueTag(doc, name, value) {
+ doc[name] = value;
+}
+
+function markdownTag(doc, name, value) {
+ doc[name] = markdown.toHTML(value);
+}
+
+var TAG = {
+ ngdoc: valueTag,
+ example: valueTag,
+ scenario: valueTag,
+ namespace: valueTag,
+ css: valueTag,
+ see: valueTag,
+ 'function': valueTag,
+ description: markdownTag,
+ returns: markdownTag,
+ name: function(doc, name, value) {
+ doc.name = value;
+ doc.shortName = value.split(/\./).pop();
+ },
+ param: function(doc, name, value){
+ doc.param = doc.param || [];
+ doc.paramRest = doc.paramRest || [];
+ var match = value.match(/^({([^\s=]+)(=([^\s]+))?}\s*)?([^\s]+)\s*(.*)/);
+ if (match) {
+ var param = {
+ type: match[2],
+ 'default':match[4],
+ name: match[5],
+ description:match[6]};
+ doc.param.push(param);
+ if (!doc.paramFirst) {
+ doc.paramFirst = param;
+ } else {
+ doc.paramRest.push(param);
+ }
+ } else {
+ throw "[" + doc.raw.file + ":" + doc.raw.line +
+ "]: @param must be in format '{type} name=value description' got: " + value;
+ }
+ }
+};
+
+function parseNgDoc(doc){
+ var atName;
+ var atText;
+ var match;
+ doc.raw.text.split(/\n/).forEach(function(line, lineNumber){
+ if (match = line.match(/^@(\w+)(\s+(.*))?/)) {
+ // we found @name ...
+ // if we have existing name
+ if (atName) {
+ (TAG[atName] || unknownTag)(doc, atName, atText.join('\n'));
+ }
+ atName = match[1];
+ atText = [];
+ if(match[3]) atText.push(match[3]);
+ } else {
+ if (atName) {
+ atText.push(line);
+ } else {
+ // ignore
+ }
+ }
+ });
+ if (atName) {
+ (TAG[atName] || unknownTag)(doc, atName, atText.join('\n'));
+ }
+}
+
+function findNgDoc(file, callback) {
+ fs.readFile(file, callback.waitFor(function(err, content){
+ var lines = content.toString().split(/\n\r?/);
+ var doc;
+ var match;
+ var inDoc = false;
+ lines.forEach(function(line, lineNumber){
+ lineNumber++;
+ // is the comment starting?
+ if (!inDoc && (match = line.match(/^\s*\/\*\*\s*(.*)$/))) {
+ line = match[1];
+ inDoc = true;
+ doc = {raw:{file:file, line:lineNumber, text:[]}};
+ }
+ // are we done?
+ if (inDoc && line.match(/\*\//)) {
+ doc.raw.text = doc.raw.text.join('\n');
+ doc.raw.text = doc.raw.text.replace(/^\n/, '');
+ if (doc.raw.text.match(/@ngdoc/))
+ callback(doc);
+ doc = null;
+ inDoc = false;
+ }
+ // is the comment add text
+ if (inDoc){
+ doc.raw.text.push(line.replace(/^\s*\*\s?/, ''));
+ }
+ });
+ callback.done();
+ }));
+}
+
+function findJsFiles(dir, callback){
+ fs.readdir(dir, callback.waitFor(function(err, files){
+ if (err) return this.error(err);
+ files.forEach(function(file){
+ var path = dir + '/' + file;
+ fs.lstat(path, callback.waitFor(function(err, stat){
+ if (err) return this.error(err);
+ if (stat.isDirectory())
+ findJsFiles(path, callback.waitMany(callback));
+ else if (/\.js$/.test(path))
+ callback(path);
+ }));
+ });
+ callback.done();
+ }));
+}
diff --git a/docs/docs-data.js b/docs/docs-data.js
new file mode 100644
index 00000000..27b3fab5
--- /dev/null
+++ b/docs/docs-data.js
@@ -0,0 +1 @@
+NG_DOC={{{JSON}}}; \ No newline at end of file
diff --git a/docs/docs-scenario.html b/docs/docs-scenario.html
new file mode 100644
index 00000000..83ca6fdf
--- /dev/null
+++ b/docs/docs-scenario.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html xmlns:ng="http://angularjs.org" wiki:ng="http://angularjs.org">
+<head>
+ <script type="text/javascript" src="../angular-scenario.js" ng:autobind></script>
+ <script type="text/javascript" src="docs-scenario.js"></script>
+</head>
+<body>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/docs-scenario.js b/docs/docs-scenario.js
new file mode 100644
index 00000000..f0c6edb9
--- /dev/null
+++ b/docs/docs-scenario.js
@@ -0,0 +1,9 @@
+{{#all}}
+describe('{{name}}', function(){
+ beforeEach(function(){
+ navigateTo('index.html#{{name}}');
+ });
+ // {{raw.file}}:{{raw.line}}
+{{{scenario}}}
+});
+{{/all}} \ No newline at end of file
diff --git a/docs/docs.js b/docs/docs.js
new file mode 100644
index 00000000..6f5c5034
--- /dev/null
+++ b/docs/docs.js
@@ -0,0 +1,7 @@
+function DocController($resource, $location){
+ this.docs = $resource('documentation.json').get();
+ this.getPartialDoc = function(){
+ return encodeURIComponent($location.hashPath) + '.html';
+ };
+}
+DocController.$inject=['$resource', '$location']; \ No newline at end of file
diff --git a/docs/filter.template b/docs/filter.template
new file mode 100644
index 00000000..677a8785
--- /dev/null
+++ b/docs/filter.template
@@ -0,0 +1,33 @@
+<h1><tt>{{name}}</tt></h1>
+<h2>Usage</h2>
+<h3>In HTML Template Binding</h3>
+<tt>
+ <span>{{</span>
+ {{paramFirst.name}}_expression
+ | {{shortName}}{{#paramRest}}{{^default}}:{{name}}{{/default}}{{#default}}<i>[:{{name}}={{default}}]</i>{{/default}}{{/paramRest}}
+ <span> }}</span>
+</tt>
+<h3>In JavaScript</h3>
+<tt ng:non-bindable>
+angular.filter.{{shortName}}({{paramFirst.name}}{{#paramRest}}, {{name}}{{/paramRest}} );
+</tt>
+
+<h3>Parameters</h3>
+<ul>
+ {{#param}}
+ <li><tt>{{name}}{{#type}}({{type}}){{/type}}</tt>: {{description}}</li>
+ {{/param}}
+</ul>
+
+<h3>Returns</h3>
+{{{returns}}}
+
+<h3>CSS</h3>
+{{{css}}}
+
+<h2>Description</h2>
+{{{description}}}
+
+<WIKI:SOURCE style="display:block;">
+{{example}}
+</WIKI:SOURCE> \ No newline at end of file
diff --git a/docs/filter:currency.html b/docs/filter:currency.html
deleted file mode 100644
index bd277bb8..00000000
--- a/docs/filter:currency.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<h1>filter:currency</h1>
-
-<p>formats a number as a currency (ie $1,234.56)</p>
-
-<p>@format <em>expression</em> | currency</p>
-
-<p><example>
-<input type="text" name="amount" value="1234.56"/> <br/>
-{{amount | currency}}
-</example></p>
-
-<p><test>
- it('should init with 1234.56', function(){
- expect(bind('amount')).toEqual('$1,234.56');
- });
- it('should update', function(){
- element(':input[name=amount]').value('-1234');
- expect(bind('amount')).toEqual('-$1,234.00');
- expect(bind('amount')).toHaveColor('red');
- });
-</test></p>
diff --git a/docs/filter:currency.md b/docs/filter:currency.md
deleted file mode 100644
index 44687a91..00000000
--- a/docs/filter:currency.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# filter:currency
-formats a number as a currency (ie $1,234.56)
-
-@format _expression_ | currency
-
-<example>
-<input type="text" name="amount" value="1234.56"/> <br/>
-{{amount | currency}}
-</example>
-
-<test>
- it('should init with 1234.56', function(){
- expect(bind('amount')).toEqual('$1,234.56');
- });
- it('should update', function(){
- element(':input[name=amount]').value('-1234');
- expect(bind('amount')).toEqual('-$1,234.00');
- expect(bind('amount')).toHaveColor('red');
- });
-</test> \ No newline at end of file
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 00000000..f61893a4
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html xmlns:ng="http://angularjs.org" wiki:ng="http://angularjs.org">
+<head>
+ <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
+ <script type="text/javascript" src="docs-data.js"></script>
+ <script type="text/javascript" src="../angular.min.js" ng:autobind></script>
+ <script type="text/javascript" src="http://angularjs.org/extensions/wiki_widgets.js"></script>
+ <link rel="stylesheet" href="http://angularjs.org/extensions/wiki_widgets.css" type="text/css" media="screen" />
+</head>
+<body ng:init="docs=$window.NG_DOC; $window.$root = $root">
+ <table>
+ <tr>
+ <td valign="top">
+ <div ng:repeat="(name, type) in docs.section">
+ <b>{{name}}</b>
+ <div ng:repeat="page in type">
+ <a href="#{{page.name}}"><tt>{{page.shortName}}</tt></a>
+ </div>
+ </div>
+ </td>
+ <td valign="top"><ng:include src="$location.hashPath + '.html' "></ng:include></td>
+ </tr>
+ </table>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/overview.template b/docs/overview.template
new file mode 100644
index 00000000..dbb91e2a
--- /dev/null
+++ b/docs/overview.template
@@ -0,0 +1 @@
+{{{description}}} \ No newline at end of file
diff --git a/docs/wiki_widgets.css b/docs/wiki_widgets.css
new file mode 100644
index 00000000..a1f6e939
--- /dev/null
+++ b/docs/wiki_widgets.css
@@ -0,0 +1,58 @@
+WIKI\:SOURCE,
+WIKI\:SOURCE>ul.tabs,
+WIKI\:SOURCE>ul.tabs>li {
+ display: block;
+ margin: 0;
+ padding: 0;
+}
+WIKI\:SOURCE>ul.tabs {
+ margin: 0 !important;
+ padding: 0 !important;
+}
+
+WIKI\:SOURCE > ul.tabs > li.tab {
+ margin: 0 0 0 .8em !important;
+ display: inline-block;
+ border: 1px solid gray;
+ padding: .1em .8em .4em .8em !important;
+ -moz-border-radius-topleft: 5px;
+ -moz-border-radius-topright: 5px;
+ -webkit-border-top-left-radius: 5px;
+ -webkit-border-top-right-radius: 5px;
+ border-bottom: none;
+ cursor: pointer;
+ background-color: lightgray;
+ margin-bottom: -2px;
+ position: relative;
+ z-index: 0;
+}
+
+WIKI\:SOURCE > ul.tabs > li.tab.selected {
+ z-index: 2;
+ background-color: white;
+ font-weight: bold;
+ border-width: 2px;
+}
+
+WIKI\:SOURCE > ul.tabs > li.pane {
+ border: 2px solid gray;
+ border-radius: 5px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ background-color: white;
+ padding: 5px !important;
+}
+
+WIKI\:SOURCE > ul.tabs > li.pane {
+ display:none;
+}
+
+WIKI\:SOURCE > ul.tabs > li.pane.selected {
+ position: relative;
+ z-index: 1;
+ display:block;
+}
+WIKI\:SOURCE > ul.tabs > li.pane.source {
+ font-size: .8em !important;
+}
+////////////////// \ No newline at end of file
diff --git a/docs/wiki_widgets.js b/docs/wiki_widgets.js
new file mode 100644
index 00000000..0147536b
--- /dev/null
+++ b/docs/wiki_widgets.js
@@ -0,0 +1,51 @@
+(function(){
+ var HTML_TEMPLATE =
+ '<!DOCTYPE HTML>\n' +
+ '<html xmlns:ng="http://angularjs.org">\n' +
+ ' <head>\n' +
+ ' <script type="text/javascript"\n' +
+ ' src="http://angularjs.org/ng/js/angular-debug.js" ng:autobind></script>\n' +
+ ' </head>\n' +
+ ' <body>\n' +
+ '_HTML_SOURCE_\n' +
+ ' </body>\n' +
+ '</html>';
+
+ angular.widget('WIKI:SOURCE', function(element){
+ this.descend(true);
+ var html = element.text();
+ element.show();
+ var tabs = angular.element(
+ '<ul class="tabs">' +
+ '<li class="tab selected" to="angular">&lt;angular/&gt;</li>' +
+ '<li class="tab" to="plain">plain</li>' +
+ '<li class="tab" to="source">source</li>' +
+ '<li class="pane selected angular">' + html + '</li>' +
+ '<li class="pane plain" ng:non-bindable>' + html + '</li>' +
+ '<li class="pane source" ng:non-bindable><pre class="brush: js; html-script: true"></pre></li>' +
+ '</ul>');
+ var pre = tabs.
+ find('>li.source>pre').
+ text(HTML_TEMPLATE.replace('_HTML_SOURCE_', html));
+ var color = element.attr('color') || 'white';
+ element.html('');
+ element.append(tabs);
+ element.find('>ul.tabs>li.pane').css('background-color', color);
+ var script = (html.match(/<script[^\>]*>([\s\S]*)<\/script>/) || [])[1] || '';
+ try {
+ eval(script);
+ } catch (e) {
+ alert(e);
+ }
+ return function(element){
+ element.find('>ul.tabs>li.tab').click(function(){
+ if ($(this).is(".selected")) return;
+ element.
+ find('>ul.tabs>li.selected').
+ add(this).
+ add(element.find('>ul>li.pane.' + angular.element(this).attr('to'))).
+ toggleClass('selected');
+ });
+ };
+ });
+})(); \ No newline at end of file
diff --git a/gen_docs.sh b/gen_docs.sh
new file mode 100755
index 00000000..d883fd34
--- /dev/null
+++ b/gen_docs.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+/usr/local/bin/node docs/collect.js
diff --git a/lib/markdown/Index.js b/lib/markdown/Index.js
new file mode 100644
index 00000000..c834c2d6
--- /dev/null
+++ b/lib/markdown/Index.js
@@ -0,0 +1,1446 @@
+// Released under MIT license
+// Copyright (c) 2009-2010 Dominic Baggott
+// Copyright (c) 2009-2010 Ash Berlin
+
+(function( expose ) {
+
+/**
+ * class Markdown
+ *
+ * Markdown processing in Javascript done right. We have very particular views
+ * on what constitutes 'right' which include:
+ *
+ * - produces well-formed HTML (this means that em and strong nesting is
+ * important)
+ *
+ * - has an intermediate representation to allow processing of parsed data (We
+ * in fact have two, both as [JsonML]: a markdown tree and an HTML tree).
+ *
+ * - is easily extensible to add new dialects without having to rewrite the
+ * entire parsing mechanics
+ *
+ * - has a good test suite
+ *
+ * This implementation fulfills all of these (except that the test suite could
+ * do with expanding to automatically run all the fixtures from other Markdown
+ * implementations.)
+ *
+ * ##### Intermediate Representation
+ *
+ * *TODO* Talk about this :) Its JsonML, but document the node names we use.
+ *
+ * [JsonML]: http://jsonml.org/ "JSON Markup Language"
+ **/
+var Markdown = expose.Markdown = function Markdown(dialect) {
+ switch (typeof dialect) {
+ case "undefined":
+ this.dialect = Markdown.dialects.Gruber;
+ break;
+ case "object":
+ this.dialect = dialect;
+ break;
+ default:
+ if (dialect in Markdown.dialects) {
+ this.dialect = Markdown.dialects[dialect];
+ }
+ else {
+ throw new Error("Unknown Markdown dialect '" + String(dialect) + "'");
+ }
+ break;
+ }
+ this.em_state = [];
+ this.strong_state = [];
+ this.debug_indent = "";
+}
+
+/**
+ * parse( markdown, [dialect] ) -> JsonML
+ * - markdown (String): markdown string to parse
+ * - dialect (String | Dialect): the dialect to use, defaults to gruber
+ *
+ * Parse `markdown` and return a markdown document as a Markdown.JsonML tree.
+ **/
+expose.parse = function( source, dialect ) {
+ // dialect will default if undefined
+ var md = new Markdown( dialect );
+ return md.toTree( source );
+}
+
+/**
+ * toHTML( markdown ) -> String
+ * toHTML( md_tree ) -> String
+ * - markdown (String): markdown string to parse
+ * - md_tree (Markdown.JsonML): parsed markdown tree
+ *
+ * Take markdown (either as a string or as a JsonML tree) and run it through
+ * [[toHTMLTree]] then turn it into a well-formated HTML fragment.
+ **/
+expose.toHTML = function toHTML( source ) {
+ var input = expose.toHTMLTree( source );
+
+ return expose.renderJsonML( input );
+}
+
+/**
+ * toHTMLTree( markdown, [dialect] ) -> JsonML
+ * toHTMLTree( md_tree ) -> JsonML
+ * - markdown (String): markdown string to parse
+ * - dialect (String | Dialect): the dialect to use, defaults to gruber
+ * - md_tree (Markdown.JsonML): parsed markdown tree
+ *
+ * Turn markdown into HTML, represented as a JsonML tree. If a string is given
+ * to this function, it is first parsed into a markdown tree by calling
+ * [[parse]].
+ **/
+expose.toHTMLTree = function toHTMLTree( input, dialect ) {
+ // convert string input to an MD tree
+ if ( typeof input ==="string" ) input = this.parse( input, dialect );
+
+ // Now convert the MD tree to an HTML tree
+
+ // remove references from the tree
+ var attrs = extract_attr( input ),
+ refs = {};
+
+ if ( attrs && attrs.references ) {
+ refs = attrs.references;
+ }
+
+ var html = convert_tree_to_html( input, refs );
+ merge_text_nodes( html );
+ return html;
+}
+
+var mk_block = Markdown.mk_block = function(block, trail, line) {
+ // Be helpful for default case in tests.
+ if ( arguments.length == 1 ) trail = "\n\n";
+
+ var s = new String(block);
+ s.trailing = trail;
+ // To make it clear its not just a string
+ s.toSource = function() {
+ return "Markdown.mk_block( " +
+ uneval(block) +
+ ", " +
+ uneval(trail) +
+ ", " +
+ uneval(line) +
+ " )"
+ }
+
+ if (line != undefined)
+ s.lineNumber = line;
+
+ return s;
+}
+
+function count_lines( str ) {
+ var n = 0, i = -1;;
+ while ( ( i = str.indexOf('\n', i+1) ) != -1) n++;
+ return n;
+}
+
+// Internal - split source into rough blocks
+Markdown.prototype.split_blocks = function splitBlocks( input, startLine ) {
+ // [\s\S] matches _anything_ (newline or space)
+ var re = /([\s\S]+?)($|\n(?:\s*\n|$)+)/g,
+ blocks = [],
+ m;
+
+ var line_no = 1;
+
+ if ( ( m = (/^(\s*\n)/)(input) ) != null ) {
+ // skip (but count) leading blank lines
+ line_no += count_lines( m[0] );
+ re.lastIndex = m[0].length;
+ }
+
+ while ( ( m = re(input) ) != null ) {
+ blocks.push( mk_block( m[1], m[2], line_no ) );
+ line_no += count_lines( m[0] );
+ }
+
+ return blocks;
+}
+
+/**
+ * Markdown#processBlock( block, next ) -> undefined | [ JsonML, ... ]
+ * - block (String): the block to process
+ * - next (Array): the following blocks
+ *
+ * Process `block` and return an array of JsonML nodes representing `block`.
+ *
+ * It does this by asking each block level function in the dialect to process
+ * the block until one can. Succesful handling is indicated by returning an
+ * array (with zero or more JsonML nodes), failure by a false value.
+ *
+ * Blocks handlers are responsible for calling [[Markdown#processInline]]
+ * themselves as appropriate.
+ *
+ * If the blocks were split incorrectly or adjacent blocks need collapsing you
+ * can adjust `next` in place using shift/splice etc.
+ *
+ * If any of this default behaviour is not right for the dialect, you can
+ * define a `__call__` method on the dialect that will get invoked to handle
+ * the block processing.
+ */
+Markdown.prototype.processBlock = function processBlock( block, next ) {
+ var cbs = this.dialect.block,
+ ord = cbs.__order__;
+
+ if ( "__call__" in cbs ) {
+ return cvs.__call__.call(this, block, next);
+ }
+
+ for ( var i = 0; i < ord.length; i++ ) {
+ //D:this.debug( "Testing", ord[i] );
+ var res = cbs[ ord[i] ].call( this, block, next );
+ if ( res ) {
+ //D:this.debug(" matched");
+ if ( !res instanceof Array || ( res.length > 0 && !( res[0] instanceof Array ) ) )
+ this.debug(ord[i], "didn't return a proper array");
+ //D:this.debug( "" );
+ return res;
+ }
+ }
+
+ // Uhoh! no match! Should we throw an error?
+ return [];
+}
+
+Markdown.prototype.processInline = function processInline( block ) {
+ return this.dialect.inline.__call__.call( this, String( block ) );
+}
+
+/**
+ * Markdown#toTree( source ) -> JsonML
+ * - source (String): markdown source to parse
+ *
+ * Parse `source` into a JsonML tree representing the markdown document.
+ **/
+// custom_tree means set this.tree to `custom_tree` and restore old value on return
+Markdown.prototype.toTree = function toTree( source, custom_root ) {
+ var blocks = source instanceof Array
+ ? source
+ : this.split_blocks( source );
+
+ // Make tree a member variable so its easier to mess with in extensions
+ var old_tree = this.tree;
+ try {
+ this.tree = custom_root || this.tree || [ "markdown" ];
+
+ blocks:
+ while ( blocks.length ) {
+ var b = this.processBlock( blocks.shift(), blocks );
+
+ // Reference blocks and the like won't return any content
+ if ( !b.length ) continue blocks;
+
+ this.tree.push.apply( this.tree, b );
+ }
+ return this.tree;
+ }
+ finally {
+ if ( custom_root )
+ this.tree = old_tree;
+ }
+
+}
+
+// Noop by default
+Markdown.prototype.debug = function () {
+ var args = Array.prototype.slice.call( arguments);
+ args.unshift(this.debug_indent);
+ print.apply( print, args );
+}
+
+Markdown.prototype.loop_re_over_block = function( re, block, cb ) {
+ // Dont use /g regexps with this
+ var m,
+ b = block.valueOf();
+
+ while ( b.length && (m = re(b) ) != null) {
+ b = b.substr( m[0].length );
+ cb.call(this, m);
+ }
+ return b;
+}
+
+/**
+ * Markdown.dialects
+ *
+ * Namespace of built-in dialects.
+ **/
+Markdown.dialects = {};
+
+/**
+ * Markdown.dialects.Gruber
+ *
+ * The default dialect that follows the rules set out by John Gruber's
+ * markdown.pl as closely as possible. Well actually we follow the behaviour of
+ * that script which in some places is not exactly what the syntax web page
+ * says.
+ **/
+Markdown.dialects.Gruber = {
+ block: {
+ atxHeader: function atxHeader( block, next ) {
+ var m = block.match( /^(#{1,6})\s*(.*?)\s*#*\s*(?:\n|$)/ );
+
+ if ( !m ) return undefined;
+
+ var header = [ "header", { level: m[ 1 ].length }, m[ 2 ] ];
+
+ if ( m[0].length < block.length )
+ next.unshift( mk_block( block.substr( m[0].length ), block.trailing, block.lineNumber + 2 ) );
+
+ return [ header ];
+ },
+
+ setextHeader: function setextHeader( block, next ) {
+ var m = block.match( /^(.*)\n([-=])\2\2+(?:\n|$)/ );
+
+ if ( !m ) return undefined;
+
+ var level = ( m[ 2 ] === "=" ) ? 1 : 2;
+ var header = [ "header", { level : level }, m[ 1 ] ];
+
+ if ( m[0].length < block.length )
+ next.unshift( mk_block( block.substr( m[0].length ), block.trailing, block.lineNumber + 2 ) );
+
+ return [ header ];
+ },
+
+ code: function code( block, next ) {
+ // | Foo
+ // |bar
+ // should be a code block followed by a paragraph. Fun
+ //
+ // There might also be adjacent code block to merge.
+
+ var ret = [],
+ re = /^(?: {0,3}\t| {4})(.*)\n?/,
+ lines;
+
+ // 4 spaces + content
+ var m = block.match( re );
+
+ if ( !m ) return undefined;
+
+ block_search:
+ do {
+ // Now pull out the rest of the lines
+ var b = this.loop_re_over_block(
+ re, block.valueOf(), function( m ) { ret.push( m[1] ) } );
+
+ if (b.length) {
+ // Case alluded to in first comment. push it back on as a new block
+ next.unshift( mk_block(b, block.trailing) );
+ break block_search;
+ }
+ else if (next.length) {
+ // Check the next block - it might be code too
+ var m = next[0].match( re );
+
+ if ( !m ) break block_search;
+
+ // Pull how how many blanks lines follow - minus two to account for .join
+ ret.push ( block.trailing.replace(/[^\n]/g, '').substring(2) );
+
+ block = next.shift();
+ }
+ else
+ break block_search;
+ } while (true);
+
+ return [ [ "code_block", ret.join("\n") ] ];
+ },
+
+ horizRule: function horizRule( block, next ) {
+ // this needs to find any hr in the block to handle abutting blocks
+ var m = block.match( /^(?:([\s\S]*?)\n)?[ \t]*([-_*])(?:[ \t]*\2){2,}[ \t]*(?:\n([\s\S]*))?$/ );
+
+ if ( !m ) {
+ return undefined;
+ }
+
+ var jsonml = [ [ "hr" ] ];
+
+ // if there's a leading abutting block, process it
+ if ( m[ 1 ] ) {
+ jsonml.unshift.apply( jsonml, this.processBlock( m[ 1 ], [] ) );
+ }
+
+ // if there's a trailing abutting block, stick it into next
+ if ( m[ 3 ] ) {
+ next.unshift( mk_block( m[ 3 ] ) );
+ }
+
+ return jsonml;
+ },
+
+ // There are two types of lists. Tight and loose. Tight lists have no whitespace
+ // between the items (and result in text just in the <li>) and loose lists,
+ // which have an empty line between list items, resulting in (one or more)
+ // paragraphs inside the <li>.
+ //
+ // There are all sorts weird edge cases about the original markdown.pl's
+ // handling of lists:
+ //
+ // * Nested lists are supposed to be indented by four chars per level. But
+ // if they aren't, you can get a nested list by indenting by less than
+ // four so long as the indent doesn't match an indent of an existing list
+ // item in the 'nest stack'.
+ //
+ // * The type of the list (bullet or number) is controlled just by the
+ // first item at the indent. Subsequent changes are ignored unless they
+ // are for nested lists
+ //
+ lists: (function( ) {
+ // Use a closure to hide a few variables.
+ var any_list = "[*+-]|\\d\\.",
+ bullet_list = /[*+-]/,
+ number_list = /\d+\./,
+ // Capture leading indent as it matters for determining nested lists.
+ is_list_re = new RegExp( "^( {0,3})(" + any_list + ")[ \t]+" ),
+ indent_re = "(?: {0,3}\\t| {4})";
+
+ // TODO: Cache this regexp for certain depths.
+ // Create a regexp suitable for matching an li for a given stack depth
+ function regex_for_depth( depth ) {
+
+ return new RegExp(
+ // m[1] = indent, m[2] = list_type
+ "(?:^(" + indent_re + "{0," + depth + "} {0,3})(" + any_list + ")\\s+)|" +
+ // m[3] = cont
+ "(^" + indent_re + "{0," + (depth-1) + "}[ ]{0,4})"
+ );
+ }
+ function expand_tab( input ) {
+ return input.replace( / {0,3}\t/g, " " );
+ }
+
+ // Add inline content `inline` to `li`. inline comes from processInline
+ // so is an array of content
+ function add(li, loose, inline, nl) {
+ if (loose) {
+ li.push( [ "para" ].concat(inline) );
+ return;
+ }
+ // Hmmm, should this be any block level element or just paras?
+ var add_to = li[li.length -1] instanceof Array && li[li.length - 1][0] == "para"
+ ? li[li.length -1]
+ : li;
+
+ // If there is already some content in this list, add the new line in
+ if (nl && li.length > 1) inline.unshift(nl);
+
+ for (var i=0; i < inline.length; i++) {
+ var what = inline[i],
+ is_str = typeof what == "string";
+ if (is_str && add_to.length > 1 && typeof add_to[add_to.length-1] == "string" )
+ {
+ add_to[ add_to.length-1 ] += what;
+ }
+ else {
+ add_to.push( what );
+ }
+ }
+ }
+
+ // contained means have an indent greater than the current one. On
+ // *every* line in the block
+ function get_contained_blocks( depth, blocks ) {
+
+ var re = new RegExp( "^(" + indent_re + "{" + depth + "}.*?\\n?)*$" ),
+ replace = new RegExp("^" + indent_re + "{" + depth + "}", "gm"),
+ ret = [];
+
+ while ( blocks.length > 0 ) {
+ if ( re( blocks[0] ) ) {
+ var b = blocks.shift(),
+ // Now remove that indent
+ x = b.replace( replace, "");
+
+ ret.push( mk_block( x, b.trailing, b.lineNumber ) );
+ }
+ break;
+ }
+ return ret;
+ }
+
+ // passed to stack.forEach to turn list items up the stack into paras
+ function paragraphify(s, i, stack) {
+ var list = s.list;
+ var last_li = list[list.length-1];
+
+ if (last_li[1] instanceof Array && last_li[1][0] == "para") {
+ return;
+ }
+ if (i+1 == stack.length) {
+ // Last stack frame
+ // Keep the same array, but replace the contents
+ last_li.push( ["para"].concat( last_li.splice(1) ) );
+ }
+ else {
+ var sublist = last_li.pop();
+ last_li.push( ["para"].concat( last_li.splice(1) ), sublist );
+ }
+ }
+
+ // The matcher function
+ return function( block, next ) {
+ var m = block.match( is_list_re );
+ if ( !m ) return undefined;
+
+ function make_list( m ) {
+ var list = bullet_list( m[2] )
+ ? ["bulletlist"]
+ : ["numberlist"];
+
+ stack.push( { list: list, indent: m[1] } );
+ return list;
+ }
+
+
+ var stack = [], // Stack of lists for nesting.
+ list = make_list( m ),
+ last_li,
+ loose = false,
+ ret = [ stack[0].list ];
+
+ // Loop to search over block looking for inner block elements and loose lists
+ loose_search:
+ while( true ) {
+ // Split into lines preserving new lines at end of line
+ var lines = block.split( /(?=\n)/ );
+
+ // We have to grab all lines for a li and call processInline on them
+ // once as there are some inline things that can span lines.
+ var li_accumulate = "";
+
+ // Loop over the lines in this block looking for tight lists.
+ tight_search:
+ for (var line_no=0; line_no < lines.length; line_no++) {
+ var nl = "",
+ l = lines[line_no].replace(/^\n/, function(n) { nl = n; return "" });
+
+ // TODO: really should cache this
+ var line_re = regex_for_depth( stack.length );
+
+ m = l.match( line_re );
+ //print( "line:", uneval(l), "\nline match:", uneval(m) );
+
+ // We have a list item
+ if ( m[1] !== undefined ) {
+ // Process the previous list item, if any
+ if ( li_accumulate.length ) {
+ add( last_li, loose, this.processInline( li_accumulate ), nl );
+ // Loose mode will have been dealt with. Reset it
+ loose = false;
+ li_accumulate = "";
+ }
+
+ m[1] = expand_tab( m[1] );
+ var wanted_depth = Math.floor(m[1].length/4)+1;
+ //print( "want:", wanted_depth, "stack:", stack.length);
+ if ( wanted_depth > stack.length ) {
+ // Deep enough for a nested list outright
+ //print ( "new nested list" );
+ list = make_list( m );
+ last_li.push( list );
+ last_li = list[1] = [ "listitem" ];
+ }
+ else {
+ // We aren't deep enough to be strictly a new level. This is
+ // where Md.pl goes nuts. If the indent matches a level in the
+ // stack, put it there, else put it one deeper then the
+ // wanted_depth deserves.
+ var found = stack.some(function(s, i) {
+ if ( s.indent != m[1] ) return false;
+ list = s.list; // Found the level we want
+ stack.splice(i+1); // Remove the others
+ //print("found");
+ return true; // And stop looping
+ });
+
+ if (!found) {
+ //print("not found. l:", uneval(l));
+ wanted_depth++;
+ if (wanted_depth <= stack.length) {
+ stack.splice(wanted_depth);
+ //print("Desired depth now", wanted_depth, "stack:", stack.length);
+ list = stack[wanted_depth-1].list;
+ //print("list:", uneval(list) );
+ }
+ else {
+ //print ("made new stack for messy indent");
+ list = make_list(m);
+ last_li.push(list);
+ }
+ }
+
+ //print( uneval(list), "last", list === stack[stack.length-1].list );
+ last_li = [ "listitem" ];
+ list.push(last_li);
+ } // end depth of shenegains
+ nl = "";
+ }
+
+ // Add content
+ if (l.length > m[0].length) {
+ li_accumulate += nl + l.substr( m[0].length );
+ }
+ } // tight_search
+
+ if ( li_accumulate.length ) {
+ add( last_li, loose, this.processInline( li_accumulate ), nl );
+ // Loose mode will have been dealt with. Reset it
+ loose = false;
+ li_accumulate = "";
+ }
+
+ // Look at the next block - we might have a loose list. Or an extra
+ // paragraph for the current li
+ var contained = get_contained_blocks( stack.length, next );
+
+ // Deal with code blocks or properly nested lists
+ if (contained.length > 0) {
+ // Make sure all listitems up the stack are paragraphs
+ stack.forEach( paragraphify, this );
+
+ last_li.push.apply( last_li, this.toTree( contained, [] ) );
+ }
+
+ var next_block = next[0] && next[0].valueOf() || "";
+
+ if ( next_block.match(is_list_re) || next_block.match( /^ / ) ) {
+ block = next.shift();
+
+ // Check for an HR following a list: features/lists/hr_abutting
+ var hr = this.dialect.block.horizRule( block, next );
+
+ if (hr) {
+ ret.push.apply(ret, hr);
+ break;
+ }
+
+ // Make sure all listitems up the stack are paragraphs
+ stack.forEach( paragraphify , this );
+
+ loose = true;
+ continue loose_search;
+ }
+ break;
+ } // loose_search
+
+ return ret;
+ }
+ })(),
+
+ blockquote: function blockquote( block, next ) {
+ if ( !block.match( /^>/m ) )
+ return undefined;
+
+ var jsonml = [];
+
+ // separate out the leading abutting block, if any
+ if ( block[ 0 ] != ">" ) {
+ var lines = block.split( /\n/ ),
+ prev = [];
+
+ // keep shifting lines until you find a crotchet
+ while ( lines.length && lines[ 0 ][ 0 ] != ">" ) {
+ prev.push( lines.shift() );
+ }
+
+ // reassemble!
+ block = lines.join( "\n" );
+ jsonml.push.apply( jsonml, this.processBlock( prev.join( "\n" ), [] ) );
+ }
+
+ // if the next block is also a blockquote merge it in
+ while ( next.length && next[ 0 ][ 0 ] == ">" ) {
+ var b = next.shift();
+ block += block.trailing + b;
+ block.trailing = b.trailing;
+ }
+
+ // Strip off the leading "> " and re-process as a block.
+ var input = block.replace( /^> ?/gm, '' ),
+ old_tree = this.tree;
+ jsonml.push( this.toTree( input, [ "blockquote" ] ) );
+
+ return jsonml;
+ },
+
+ referenceDefn: function referenceDefn( block, next) {
+ var re = /^\s*\[(.*?)\]:\s*(\S+)(?:\s+(?:(['"])(.*?)\3|\((.*?)\)))?\n?/;
+ // interesting matches are [ , ref_id, url, , title, title ]
+
+ if ( !block.match(re) )
+ return undefined;
+
+ // make an attribute node if it doesn't exist
+ if ( !extract_attr( this.tree ) ) {
+ this.tree.splice( 1, 0, {} );
+ }
+
+ var attrs = extract_attr( this.tree );
+
+ // make a references hash if it doesn't exist
+ if ( attrs.references === undefined ) {
+ attrs.references = {};
+ }
+
+ var b = this.loop_re_over_block(re, block, function( m ) {
+
+ if ( m[2] && m[2][0] == '<' && m[2][m[2].length-1] == '>' )
+ m[2] = m[2].substring( 1, m[2].length - 1 );
+
+ var ref = attrs.references[ m[1].toLowerCase() ] = {
+ href: m[2]
+ };
+
+ if (m[4] !== undefined)
+ ref.title = m[4];
+ else if (m[5] !== undefined)
+ ref.title = m[5];
+
+ } );
+
+ if (b.length)
+ next.unshift( mk_block( b, block.trailing ) );
+
+ return [];
+ },
+
+ para: function para( block, next ) {
+ // everything's a para!
+ return [ ["para"].concat( this.processInline( block ) ) ];
+ }
+ }
+}
+
+Markdown.dialects.Gruber.inline = {
+ __call__: function inline( text, patterns ) {
+ // Hmmm - should this function be directly in Md#processInline, or
+ // conversely, should Md#processBlock be moved into block.__call__ too
+ var out = [ ],
+ m,
+ // Look for the next occurange of a special character/pattern
+ re = new RegExp( "([\\s\\S]*?)(" + (patterns.source || patterns) + ")", "g" ),
+ lastIndex = 0;
+
+ //D:var self = this;
+ //D:self.debug("processInline:", uneval(text) );
+ function add(x) {
+ //D:self.debug(" adding output", uneval(x));
+ if (typeof x == "string" && typeof out[out.length-1] == "string")
+ out[ out.length-1 ] += x;
+ else
+ out.push(x);
+ }
+
+ while ( ( m = re.exec(text) ) != null) {
+ if ( m[1] ) add( m[1] ); // Some un-interesting text matched
+ else m[1] = { length: 0 }; // Or there was none, but make m[1].length == 0
+
+ var res;
+ if ( m[2] in this.dialect.inline ) {
+ res = this.dialect.inline[ m[2] ].call(
+ this,
+ text.substr( m.index + m[1].length ), m, out );
+ }
+ // Default for now to make dev easier. just slurp special and output it.
+ res = res || [ m[2].length, m[2] ];
+
+ var len = res.shift();
+ // Update how much input was consumed
+ re.lastIndex += ( len - m[2].length );
+
+ // Add children
+ res.forEach(add);
+
+ lastIndex = re.lastIndex;
+ }
+
+ // Add last 'boring' chunk
+ if ( text.length > lastIndex )
+ add( text.substr( lastIndex ) );
+
+ return out;
+ },
+
+ "\\": function escaped( text ) {
+ // [ length of input processed, node/children to add... ]
+ // Only esacape: \ ` * _ { } [ ] ( ) # * + - . !
+ if ( text.match( /^\\[\\`\*_{}\[\]()#\+.!\-]/ ) )
+ return [ 2, text[1] ];
+ else
+ // Not an esacpe
+ return [ 1, "\\" ];
+ },
+
+ "![": function image( text ) {
+ // ![Alt text](/path/to/img.jpg "Optional title")
+ // 1 2 3 4 <--- captures
+ var m = text.match( /^!\[(.*?)\][ \t]*\([ \t]*(\S*)(?:[ \t]+(["'])(.*?)\3)?[ \t]*\)/ );
+
+ if ( m ) {
+ if ( m[2] && m[2][0] == '<' && m[2][m[2].length-1] == '>' )
+ m[2] = m[2].substring( 1, m[2].length - 1 );
+
+ m[2] == this.dialect.inline.__call__.call( this, m[2], /\\/ )[0];
+
+ var attrs = { alt: m[1], href: m[2] || "" };
+ if ( m[4] !== undefined)
+ attrs.title = m[4];
+
+ return [ m[0].length, [ "img", attrs ] ];
+ }
+
+ // ![Alt text][id]
+ m = text.match( /^!\[(.*?)\][ \t]*\[(.*?)\]/ );
+
+ if ( m ) {
+ // We can't check if the reference is known here as it likely wont be
+ // found till after. Check it in md tree->hmtl tree conversion
+ return [ m[0].length, [ "img_ref", { alt: m[1], ref: m[2].toLowerCase(), text: m[0] } ] ];
+ }
+
+ // Just consume the '!['
+ return [ 2, "![" ];
+ },
+
+ "[": function link( text ) {
+ // [link text](/path/to/img.jpg "Optional title")
+ // 1 2 3 4 <--- captures
+ var m = text.match( /^\[([\s\S]*?)\][ \t]*\([ \t]*(\S+)(?:[ \t]+(["'])(.*?)\3)?[ \t]*\)/ );
+
+ if ( m ) {
+ if ( m[2] && m[2][0] == '<' && m[2][m[2].length-1] == '>' )
+ m[2] = m[2].substring( 1, m[2].length - 1 );
+
+ // Process escapes only
+ m[2] = this.dialect.inline.__call__.call( this, m[2], /\\/ )[0];
+
+ var attrs = { href: m[2] || "" };
+ if ( m[4] !== undefined)
+ attrs.title = m[4];
+
+ return [ m[0].length, [ "link", attrs, m[1] ] ];
+ }
+
+ // [Alt text][id]
+ // [Alt text] [id]
+ // [id]
+ m = text.match( /^\[([\s\S]*?)\](?: ?\[(.*?)\])?/ );
+
+ if ( m ) {
+ // [id] case, text == id
+ if ( m[2] === undefined || m[2] === "" ) m[2] = m[1];
+
+ // We can't check if the reference is known here as it likely wont be
+ // found till after. Check it in md tree->hmtl tree conversion.
+ // Store the original so that conversion can revert if the ref isn't found.
+ return [
+ m[ 0 ].length,
+ [
+ "link_ref",
+ {
+ ref: m[ 2 ].toLowerCase(),
+ original: m[ 0 ]
+ },
+ m[ 1 ]
+ ]
+ ];
+ }
+
+ // Just consume the '['
+ return [ 1, "[" ];
+ },
+
+
+ "<": function autoLink( text ) {
+ var m;
+
+ if ( ( m = text.match( /^<(?:((https?|ftp|mailto):[^>]+)|(.*?@.*?\.[a-zA-Z]+))>/ ) ) != null ) {
+ if ( m[3] ) {
+ return [ m[0].length, [ "link", { href: "mailto:" + m[3] }, m[3] ] ];
+
+ }
+ else if ( m[2] == "mailto" ) {
+ return [ m[0].length, [ "link", { href: m[1] }, m[1].substr("mailto:".length ) ] ];
+ }
+ else
+ return [ m[0].length, [ "link", { href: m[1] }, m[1] ] ];
+ }
+
+ return [ 1, "<" ];
+ },
+
+ "`": function inlineCode( text ) {
+ // Inline code block. as many backticks as you like to start it
+ // Always skip over the opening ticks.
+ var m = text.match( /(`+)(([\s\S]*?)\1)/ );
+
+ if ( m && m[2] )
+ return [ m[1].length + m[2].length, [ "inlinecode", m[3] ] ];
+ else {
+ // TODO: No matching end code found - warn!
+ return [ 1, "`" ];
+ }
+ },
+
+ " \n": function lineBreak( text ) {
+ return [ 3, [ "linebreak" ] ];
+ }
+
+}
+
+// Meta Helper/generator method for em and strong handling
+function strong_em( tag, md ) {
+
+ var state_slot = tag + "_state",
+ other_slot = tag == "strong" ? "em_state" : "strong_state";
+
+ function CloseTag(len) {
+ this.len_after = len;
+ this.name = "close_" + md;
+ }
+
+ return function ( text, orig_match ) {
+
+ if (this[state_slot][0] == md) {
+ // Most recent em is of this type
+ //D:this.debug("closing", md);
+ this[state_slot].shift();
+
+ // "Consume" everything to go back to the recrusion in the else-block below
+ return[ text.length, new CloseTag(text.length-md.length) ];
+ }
+ else {
+ // Store a clone of the em/strong states
+ var other = this[other_slot].slice(),
+ state = this[state_slot].slice();
+
+ this[state_slot].unshift(md);
+
+ //D:this.debug_indent += " ";
+
+ // Recurse
+ var res = this.processInline( text.substr( md.length ) );
+ //D:this.debug_indent = this.debug_indent.substr(2);
+
+ var last = res[res.length - 1];
+
+ //D:this.debug("processInline from", tag + ": ", uneval( res ) );
+
+ var check = this[state_slot].shift();
+ if (last instanceof CloseTag) {
+ res.pop();
+ // We matched! Huzzah.
+ var consumed = text.length - last.len_after;
+ return [ consumed, [ tag ].concat(res) ];
+ }
+ else {
+ // Restore the state of the other kind. We might have mistakenly closed it.
+ this[other_slot] = other;
+ this[state_slot] = state;
+
+ // We can't reuse the processed result as it could have wrong parsing contexts in it.
+ return [ md.length, md ];
+ }
+ }
+ } // End returned function
+}
+
+Markdown.dialects.Gruber.inline["**"] = strong_em("strong", "**");
+Markdown.dialects.Gruber.inline["__"] = strong_em("strong", "__");
+Markdown.dialects.Gruber.inline["*"] = strong_em("em", "*");
+Markdown.dialects.Gruber.inline["_"] = strong_em("em", "_");
+
+
+// Build default order from insertion order.
+Markdown.buildBlockOrder = function(d) {
+ var ord = [];
+ for ( var i in d ) {
+ if ( i == "__order__" || i == "__call__" ) continue;
+ ord.push( i );
+ }
+ d.__order__ = ord;
+}
+
+// Build patterns for inline matcher
+Markdown.buildInlinePatterns = function(d) {
+ var patterns = [];
+
+ for ( var i in d ) {
+ if (i == "__call__") continue;
+ var l = i.replace( /([\\.*+?|()\[\]{}])/g, "\\$1" )
+ .replace( /\n/, "\\n" );
+ patterns.push( i.length == 1 ? l : "(?:" + l + ")" );
+ }
+
+ patterns = patterns.join("|");
+ //print("patterns:", uneval( patterns ) );
+
+ var fn = d.__call__;
+ d.__call__ = function(text, pattern) {
+ if (pattern != undefined)
+ return fn.call(this, text, pattern);
+ else
+ return fn.call(this, text, patterns);
+ }
+}
+
+// Helper function to make sub-classing a dialect easier
+Markdown.subclassDialect = function( d ) {
+ function Block() {};
+ Block.prototype = d.block;
+ function Inline() {};
+ Inline.prototype = d.inline;
+
+ return { block: new Block(), inline: new Inline() };
+}
+
+Markdown.buildBlockOrder ( Markdown.dialects.Gruber.block );
+Markdown.buildInlinePatterns( Markdown.dialects.Gruber.inline );
+
+Markdown.dialects.Maruku = Markdown.subclassDialect( Markdown.dialects.Gruber );
+
+Markdown.dialects.Maruku.block.document_meta = function document_meta( block, next ) {
+ // we're only interested in the first block
+ if ( block.lineNumber > 1 ) return undefined;
+
+ // document_meta blocks consist of one or more lines of `Key: Value\n`
+ if ( ! block.match( /^(?:\w+:.*\n)*\w+:.*$/ ) ) return undefined;
+
+ // make an attribute node if it doesn't exist
+ if ( !extract_attr( this.tree ) ) {
+ this.tree.splice( 1, 0, {} );
+ }
+
+ var pairs = block.split( /\n/ );
+ for ( p in pairs ) {
+ var m = pairs[ p ].match( /(\w+):\s*(.*)$/ ),
+ key = m[ 1 ].toLowerCase(),
+ value = m[ 2 ];
+
+ this.tree[ 1 ][ key ] = value;
+ }
+
+ // document_meta produces no content!
+ return [];
+}
+
+Markdown.dialects.Maruku.block.block_meta = function block_meta( block, next ) {
+ // check if the last line of the block is an meta hash
+ var m = block.match( /(^|\n) {0,3}\{:\s*((?:\\\}|[^\}])*)\s*\}$/ );
+ if ( !m ) return undefined;
+
+ // process the meta hash
+ var attr = process_meta_hash( m[ 2 ] );
+
+ // if we matched ^ then we need to apply meta to the previous block
+ if ( m[ 1 ] === "" ) {
+ var node = this.tree[ this.tree.length - 1 ],
+ hash = extract_attr( node );
+
+ // if the node is a string (rather than JsonML), bail
+ if ( typeof node === "string" ) return undefined;
+
+ // create the attribute hash if it doesn't exist
+ if ( !hash ) {
+ hash = {};
+ node.splice( 1, 0, hash );
+ }
+
+ // add the attributes in
+ for ( a in attr ) {
+ hash[ a ] = attr[ a ];
+ }
+
+ // return nothing so the meta hash is removed
+ return [];
+ }
+
+ // pull the meta hash off the block and process what's left
+ var b = block.replace( /\n.*$/, "" ),
+ result = this.processBlock( b, [] );
+
+ // get or make the attributes hash
+ var hash = extract_attr( result[ 0 ] );
+ if ( !hash ) {
+ hash = {};
+ result[ 0 ].splice( 1, 0, hash );
+ }
+
+ // attach the attributes to the block
+ for ( a in attr ) {
+ hash[ a ] = attr[ a ];
+ }
+
+ return result;
+}
+
+Markdown.dialects.Maruku.block.definition_list = function definition_list( block, next ) {
+ // one or more terms followed by one or more definitions, in a single block
+ var tight = /^((?:[^\s:].*\n)+):\s+([^]+)$/,
+ list = [ "dl" ];
+
+ // see if we're dealing with a tight or loose block
+ if ( ( m = block.match( tight ) ) ) {
+ // pull subsequent tight DL blocks out of `next`
+ var blocks = [ block ];
+ while ( next.length && tight.exec( next[ 0 ] ) ) {
+ blocks.push( next.shift() );
+ }
+
+ for ( var b = 0; b < blocks.length; ++b ) {
+ var m = blocks[ b ].match( tight ),
+ terms = m[ 1 ].replace( /\n$/, "" ).split( /\n/ ),
+ defns = m[ 2 ].split( /\n:\s+/ );
+
+ // print( uneval( m ) );
+
+ for ( var i = 0; i < terms.length; ++i ) {
+ list.push( [ "dt", terms[ i ] ] );
+ }
+
+ for ( var i = 0; i < defns.length; ++i ) {
+ // run inline processing over the definition
+ list.push( [ "dd" ].concat( this.processInline( defns[ i ].replace( /(\n)\s+/, "$1" ) ) ) );
+ }
+ }
+ }
+ else {
+ return undefined;
+ }
+
+ return [ list ];
+}
+
+Markdown.dialects.Maruku.inline[ "{:" ] = function inline_meta( text, matches, out ) {
+ if ( !out.length ) {
+ return [ 2, "{:" ];
+ }
+
+ // get the preceeding element
+ var before = out[ out.length - 1 ];
+
+ if ( typeof before === "string" ) {
+ return [ 2, "{:" ];
+ }
+
+ // match a meta hash
+ var m = text.match( /^\{:\s*((?:\\\}|[^\}])*)\s*\}/ );
+
+ // no match, false alarm
+ if ( !m ) {
+ return [ 2, "{:" ];
+ }
+
+ // attach the attributes to the preceeding element
+ var meta = process_meta_hash( m[ 1 ] ),
+ attr = extract_attr( before );
+
+ if ( !attr ) {
+ attr = {};
+ before.splice( 1, 0, attr );
+ }
+
+ for ( var k in meta ) {
+ attr[ k ] = meta[ k ];
+ }
+
+ // cut out the string and replace it with nothing
+ return [ m[ 0 ].length, "" ];
+}
+
+Markdown.buildBlockOrder ( Markdown.dialects.Maruku.block );
+Markdown.buildInlinePatterns( Markdown.dialects.Maruku.inline );
+
+function extract_attr( jsonml ) {
+ return jsonml instanceof Array
+ && jsonml.length > 1
+ && typeof jsonml[ 1 ] === "object"
+ && !( jsonml[ 1 ] instanceof Array )
+ ? jsonml[ 1 ]
+ : undefined;
+}
+
+function process_meta_hash( meta_string ) {
+ var meta = split_meta_hash( meta_string ),
+ attr = {};
+
+ for ( var i = 0; i < meta.length; ++i ) {
+ // id: #foo
+ if ( /^#/.test( meta[ i ] ) ) {
+ attr.id = meta[ i ].substring( 1 );
+ }
+ // class: .foo
+ else if ( /^\./.test( meta[ i ] ) ) {
+ // if class already exists, append the new one
+ if ( attr['class'] ) {
+ attr['class'] = attr['class'] + meta[ i ].replace( /./, " " );
+ }
+ else {
+ attr['class'] = meta[ i ].substring( 1 );
+ }
+ }
+ // attribute: foo=bar
+ else if ( /=/.test( meta[ i ] ) ) {
+ var s = meta[ i ].split( /=/ );
+ attr[ s[ 0 ] ] = s[ 1 ];
+ }
+ }
+
+ return attr;
+}
+
+function split_meta_hash( meta_string ) {
+ var meta = meta_string.split( "" ),
+ parts = [ "" ],
+ in_quotes = false;
+
+ while ( meta.length ) {
+ var letter = meta.shift();
+ switch ( letter ) {
+ case " " :
+ // if we're in a quoted section, keep it
+ if ( in_quotes ) {
+ parts[ parts.length - 1 ] += letter;
+ }
+ // otherwise make a new part
+ else {
+ parts.push( "" );
+ }
+ break;
+ case "'" :
+ case '"' :
+ // reverse the quotes and move straight on
+ in_quotes = !in_quotes;
+ break;
+ case "\\" :
+ // shift off the next letter to be used straight away.
+ // it was escaped so we'll keep it whatever it is
+ letter = meta.shift();
+ default :
+ parts[ parts.length - 1 ] += letter;
+ break;
+ }
+ }
+
+ return parts;
+}
+
+/**
+ * renderJsonML( jsonml[, options] ) -> String
+ * - jsonml (Array): JsonML array to render to XML
+ * - options (Object): options
+ *
+ * Converts the given JsonML into well-formed XML.
+ *
+ * The options currently understood are:
+ *
+ * - root (Boolean): wether or not the root node should be included in the
+ * output, or just its children. The default `false` is to not include the
+ * root itself.
+ */
+expose.renderJsonML = function( jsonml, options ) {
+ options = options || {};
+ // include the root element in the rendered output?
+ options.root = options.root || false;
+
+ var content = [];
+
+ if ( options.root ) {
+ content.push( render_tree( jsonml ) );
+ }
+ else {
+ jsonml.shift(); // get rid of the tag
+ if ( jsonml.length && typeof jsonml[ 0 ] === "object" && !( jsonml[ 0 ] instanceof Array ) ) {
+ jsonml.shift(); // get rid of the attributes
+ }
+
+ while ( jsonml.length ) {
+ content.push( render_tree( jsonml.shift() ) );
+ }
+ }
+
+ return content.join( "\n\n" );
+}
+
+function render_tree( jsonml ) {
+ // basic case
+ if ( typeof jsonml === "string" ) {
+ return jsonml.replace( /&/g, "&amp;" )
+ .replace( /</g, "&lt;" )
+ .replace( />/g, "&gt;" );
+ }
+
+ var tag = jsonml.shift(),
+ attributes = {},
+ content = [];
+
+ if ( jsonml.length && typeof jsonml[ 0 ] === "object" && !( jsonml[ 0 ] instanceof Array ) ) {
+ attributes = jsonml.shift();
+ }
+
+ while ( jsonml.length ) {
+ content.push( arguments.callee( jsonml.shift() ) );
+ }
+
+ var tag_attrs = "";
+ for ( var a in attributes ) {
+ tag_attrs += " " + a + '="' + attributes[ a ] + '"';
+ }
+
+ // be careful about adding whitespace here for inline elements
+ return "<"+ tag + tag_attrs + ">" + content.join( "" ) + "</" + tag + ">";
+}
+
+function convert_tree_to_html( tree, references ) {
+ // shallow clone
+ var jsonml = tree.slice( 0 );
+
+ // Clone attributes if the exist
+ var attrs = extract_attr( jsonml );
+ if ( attrs ) {
+ jsonml[ 1 ] = {};
+ for ( var i in attrs ) {
+ jsonml[ 1 ][ i ] = attrs[ i ];
+ }
+ attrs = jsonml[ 1 ];
+ }
+
+ // basic case
+ if ( typeof jsonml === "string" ) {
+ return jsonml;
+ }
+
+ // convert this node
+ switch ( jsonml[ 0 ] ) {
+ case "header":
+ jsonml[ 0 ] = "h" + jsonml[ 1 ].level;
+ delete jsonml[ 1 ].level;
+ break;
+ case "bulletlist":
+ jsonml[ 0 ] = "ul";
+ break;
+ case "numberlist":
+ jsonml[ 0 ] = "ol";
+ break;
+ case "listitem":
+ jsonml[ 0 ] = "li";
+ break;
+ case "para":
+ jsonml[ 0 ] = "p";
+ break;
+ case "markdown":
+ jsonml[ 0 ] = "html";
+ if ( attrs ) delete attrs.references;
+ break;
+ case "code_block":
+ jsonml[ 0 ] = "pre";
+ var i = attrs ? 2 : 1;
+ var code = [ "code" ];
+ code.push.apply( code, jsonml.splice( i ) );
+ jsonml[ i ] = code;
+ break;
+ case "inlinecode":
+ jsonml[ 0 ] = "code";
+ break;
+ case "img":
+ jsonml[ 1 ].src = jsonml[ 1 ].href;
+ delete jsonml[ 1 ].href;
+ break;
+ case "linebreak":
+ jsonml[0] = "br";
+ break;
+ case "link":
+ jsonml[ 0 ] = "a";
+ break;
+ case "link_ref":
+ jsonml[ 0 ] = "a";
+
+ // grab this ref and clean up the attribute node
+ var ref = references[ attrs.ref ];
+
+ // if the reference exists, make the link
+ if ( ref ) {
+ delete attrs.ref;
+
+ // add in the href and title, if present
+ attrs.href = ref.href;
+ if ( ref.title ) {
+ attrs.title = ref.title;
+ }
+
+ // get rid of the unneeded original text
+ delete attrs.original;
+ }
+ // the reference doesn't exist, so revert to plain text
+ else {
+ return attrs.original;
+ }
+ break;
+ }
+
+ // convert all the children
+ var i = 1;
+
+ // deal with the attribute node, if it exists
+ if ( attrs ) {
+ // if there are keys, skip over it
+ for ( var key in jsonml[ 1 ] ) {
+ i = 2;
+ }
+ // if there aren't, remove it
+ if ( i === 1 ) {
+ jsonml.splice( i, 1 );
+ }
+ }
+
+ for ( ; i < jsonml.length; ++i ) {
+ jsonml[ i ] = arguments.callee( jsonml[ i ], references );
+ }
+
+ return jsonml;
+}
+
+
+// merges adjacent text nodes into a single node
+function merge_text_nodes( jsonml ) {
+ // skip the tag name and attribute hash
+ var i = extract_attr( jsonml ) ? 2 : 1;
+
+ while ( i < jsonml.length ) {
+ // if it's a string check the next item too
+ if ( typeof jsonml[ i ] === "string" ) {
+ if ( i + 1 < jsonml.length && typeof jsonml[ i + 1 ] === "string" ) {
+ // merge the second string into the first and remove it
+ jsonml[ i ] += jsonml.splice( i + 1, 1 )[ 0 ];
+ }
+ else {
+ ++i;
+ }
+ }
+ // if it's not a string recurse
+ else {
+ arguments.callee( jsonml[ i ] );
+ ++i;
+ }
+ }
+}
+
+} )( (function() {
+ if ( typeof exports === "undefined" ) {
+ window.markdown = {};
+ return window.markdown;
+ }
+ else {
+ return exports;
+ }
+} )() ); \ No newline at end of file
diff --git a/lib/mustache/LICENSE b/lib/mustache/LICENSE
new file mode 100644
index 00000000..e06f8eea
--- /dev/null
+++ b/lib/mustache/LICENSE
@@ -0,0 +1,21 @@
+Copyright (c) 2009 Chris Wanstrath (Ruby)
+Copyright (c) 2010 Jan Lehnardt (JavaScript)
+
+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. \ No newline at end of file
diff --git a/lib/mustache/index.js b/lib/mustache/index.js
new file mode 100644
index 00000000..292db14d
--- /dev/null
+++ b/lib/mustache/index.js
@@ -0,0 +1,344 @@
+/*
+ * CommonJS-compatible mustache.js module
+ *
+ * See http://github.com/janl/mustache.js for more info.
+ */
+/*
+ mustache.js Logic-less templates in JavaScript
+
+ See http://mustache.github.com/ for more info.
+*/
+
+var Mustache = function() {
+ var Renderer = function() {};
+
+ Renderer.prototype = {
+ otag: "{{",
+ ctag: "}}",
+ pragmas: {},
+ buffer: [],
+ pragmas_implemented: {
+ "IMPLICIT-ITERATOR": true
+ },
+ context: {},
+
+ render: function(template, context, partials, in_recursion) {
+ // reset buffer & set context
+ if(!in_recursion) {
+ this.context = context;
+ this.buffer = []; // TODO: make this non-lazy
+ }
+
+ // fail fast
+ if(!this.includes("", template)) {
+ if(in_recursion) {
+ return template;
+ } else {
+ this.send(template);
+ return;
+ }
+ }
+
+ template = this.render_pragmas(template);
+ var html = this.render_section(template, context, partials);
+ if(in_recursion) {
+ return this.render_tags(html, context, partials, in_recursion);
+ }
+
+ this.render_tags(html, context, partials, in_recursion);
+ },
+
+ /*
+ Sends parsed lines
+ */
+ send: function(line) {
+ if(line != "") {
+ this.buffer.push(line);
+ }
+ },
+
+ /*
+ Looks for %PRAGMAS
+ */
+ render_pragmas: function(template) {
+ // no pragmas
+ if(!this.includes("%", template)) {
+ return template;
+ }
+
+ var that = this;
+ var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
+ this.ctag);
+ return template.replace(regex, function(match, pragma, options) {
+ if(!that.pragmas_implemented[pragma]) {
+ throw({message:
+ "This implementation of mustache doesn't understand the '" +
+ pragma + "' pragma"});
+ }
+ that.pragmas[pragma] = {};
+ if(options) {
+ var opts = options.split("=");
+ that.pragmas[pragma][opts[0]] = opts[1];
+ }
+ return "";
+ // ignore unknown pragmas silently
+ });
+ },
+
+ /*
+ Tries to find a partial in the curent scope and render it
+ */
+ render_partial: function(name, context, partials) {
+ name = this.trim(name);
+ if(!partials || partials[name] === undefined) {
+ throw({message: "unknown_partial '" + name + "'"});
+ }
+ if(typeof(context[name]) != "object") {
+ return this.render(partials[name], context, partials, true);
+ }
+ return this.render(partials[name], context[name], partials, true);
+ },
+
+ /*
+ Renders inverted (^) and normal (#) sections
+ */
+ render_section: function(template, context, partials) {
+ if(!this.includes("#", template) && !this.includes("^", template)) {
+ return template;
+ }
+
+ var that = this;
+ // CSW - Added "+?" so it finds the tighest bound, not the widest
+ var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag +
+ "\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag +
+ "\\s*", "mg");
+
+ // for each {{#foo}}{{/foo}} section do...
+ return template.replace(regex, function(match, type, name, content) {
+ var value = that.find(name, context);
+ if(type == "^") { // inverted section
+ if(!value || that.is_array(value) && value.length === 0) {
+ // false or empty list, render it
+ return that.render(content, context, partials, true);
+ } else {
+ return "";
+ }
+ } else if(type == "#") { // normal section
+ if(that.is_array(value)) { // Enumerable, Let's loop!
+ return that.map(value, function(row) {
+ return that.render(content, that.create_context(row),
+ partials, true);
+ }).join("");
+ } else if(that.is_object(value)) { // Object, Use it as subcontext!
+ return that.render(content, that.create_context(value),
+ partials, true);
+ } else if(typeof value === "function") {
+ // higher order section
+ return value.call(context, content, function(text) {
+ return that.render(text, context, partials, true);
+ });
+ } else if(value) { // boolean section
+ return that.render(content, context, partials, true);
+ } else {
+ return "";
+ }
+ }
+ });
+ },
+
+ /*
+ Replace {{foo}} and friends with values from our view
+ */
+ render_tags: function(template, context, partials, in_recursion) {
+ // tit for tat
+ var that = this;
+
+ var new_regex = function() {
+ return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" +
+ that.ctag + "+", "g");
+ };
+
+ var regex = new_regex();
+ var tag_replace_callback = function(match, operator, name) {
+ switch(operator) {
+ case "!": // ignore comments
+ return "";
+ case "=": // set new delimiters, rebuild the replace regexp
+ that.set_delimiters(name);
+ regex = new_regex();
+ return "";
+ case ">": // render partial
+ return that.render_partial(name, context, partials);
+ case "{": // the triple mustache is unescaped
+ return that.find(name, context);
+ default: // escape the value
+ return that.escape(that.find(name, context));
+ }
+ };
+ var lines = template.split("\n");
+ for(var i = 0; i < lines.length; i++) {
+ lines[i] = lines[i].replace(regex, tag_replace_callback, this);
+ if(!in_recursion) {
+ this.send(lines[i]);
+ }
+ }
+
+ if(in_recursion) {
+ return lines.join("\n");
+ }
+ },
+
+ set_delimiters: function(delimiters) {
+ var dels = delimiters.split(" ");
+ this.otag = this.escape_regex(dels[0]);
+ this.ctag = this.escape_regex(dels[1]);
+ },
+
+ escape_regex: function(text) {
+ // thank you Simon Willison
+ if(!arguments.callee.sRE) {
+ var specials = [
+ '/', '.', '*', '+', '?', '|',
+ '(', ')', '[', ']', '{', '}', '\\'
+ ];
+ arguments.callee.sRE = new RegExp(
+ '(\\' + specials.join('|\\') + ')', 'g'
+ );
+ }
+ return text.replace(arguments.callee.sRE, '\\$1');
+ },
+
+ /*
+ find `name` in current `context`. That is find me a value
+ from the view object
+ */
+ find: function(name, context) {
+ name = this.trim(name);
+
+ // Checks whether a value is thruthy or false or 0
+ function is_kinda_truthy(bool) {
+ return bool === false || bool === 0 || bool;
+ }
+
+ var value = context;
+ var path = name.split(/\./);
+ for(var i = 0; i < path.length; i++) {
+ name = path[i];
+ if(value && is_kinda_truthy(value[name])) {
+ value = value[name];
+ } else if(i == 0 && is_kinda_truthy(this.context[name])) {
+ value = this.context[name];
+ } else {
+ value = undefined;
+ }
+ }
+
+ if(typeof value === "function") {
+ return value.apply(context);
+ }
+ if(value !== undefined) {
+ return value;
+ }
+ // silently ignore unkown variables
+ return "";
+ },
+
+ // Utility methods
+
+ /* includes tag */
+ includes: function(needle, haystack) {
+ return haystack.indexOf(this.otag + needle) != -1;
+ },
+
+ /*
+ Does away with nasty characters
+ */
+ escape: function(s) {
+ s = String(s === null ? "" : s);
+ return s.replace(/&(?!\w+;)|["'<>\\]/g, function(s) {
+ switch(s) {
+ case "&": return "&amp;";
+ case "\\": return "\\\\";
+ case '"': return '&quot;';
+ case "'": return '&#39;';
+ case "<": return "&lt;";
+ case ">": return "&gt;";
+ default: return s;
+ }
+ });
+ },
+
+ // by @langalex, support for arrays of strings
+ create_context: function(_context) {
+ if(this.is_object(_context)) {
+ return _context;
+ } else {
+ var iterator = ".";
+ if(this.pragmas["IMPLICIT-ITERATOR"]) {
+ iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
+ }
+ var ctx = {};
+ ctx[iterator] = _context;
+ return ctx;
+ }
+ },
+
+ is_object: function(a) {
+ return a && typeof a == "object";
+ },
+
+ is_array: function(a) {
+ return Object.prototype.toString.call(a) === '[object Array]';
+ },
+
+ /*
+ Gets rid of leading and trailing whitespace
+ */
+ trim: function(s) {
+ return s.replace(/^\s*|\s*$/g, "");
+ },
+
+ /*
+ Why, why, why? Because IE. Cry, cry cry.
+ */
+ map: function(array, fn) {
+ if (typeof array.map == "function") {
+ return array.map(fn);
+ } else {
+ var r = [];
+ var l = array.length;
+ for(var i = 0; i < l; i++) {
+ r.push(fn(array[i]));
+ }
+ return r;
+ }
+ }
+ };
+
+ return({
+ name: "mustache.js",
+ version: "0.3.1-dev",
+
+ /*
+ Turns a template and view into HTML
+ */
+ to_html: function(template, view, partials, send_fun) {
+ var renderer = new Renderer();
+ if(send_fun) {
+ renderer.send = send_fun;
+ }
+ renderer.render(template, view, partials);
+ if(!send_fun) {
+ return renderer.buffer.join("\n");
+ }
+ }
+ });
+}();
+
+
+exports.name = Mustache.name;
+exports.version = Mustache.version;
+
+exports.to_html = function() {
+ return Mustache.to_html.apply(this, arguments);
+}; \ No newline at end of file
diff --git a/lib/nodeserver/server.js b/lib/nodeserver/server.js
index f91f6afa..bcaed299 100644
--- a/lib/nodeserver/server.js
+++ b/lib/nodeserver/server.js
@@ -84,12 +84,14 @@ StaticServlet.MimeMap = {
StaticServlet.prototype.handleRequest = function(req, res) {
var self = this;
- var path = ('./' + req.url.pathname).replace('//','/');
+ var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/, function(match, hex){
+ return String.fromCharCode(parseInt(hex, 16));
+ });
var parts = path.split('/');
if (parts[parts.length-1].charAt(0) === '.')
return self.sendForbidden_(req, res, path);
fs.stat(path, function(err, stat) {
- if (err)
+ if (err)
return self.sendMissing_(req, res, path);
if (stat.isDirectory())
return self.sendDirectory_(req, res, path);
@@ -118,8 +120,8 @@ StaticServlet.prototype.sendMissing_ = function(req, res, path) {
res.write('<title>404 Not Found</title>\n');
res.write('<h1>Not Found</h1>');
res.write(
- '<p>The requested URL ' +
- escapeHtml(path) +
+ '<p>The requested URL ' +
+ escapeHtml(path) +
' was not found on this server.</p>'
);
res.end();
@@ -135,7 +137,7 @@ StaticServlet.prototype.sendForbidden_ = function(req, res, path) {
res.write('<title>403 Forbidden</title>\n');
res.write('<h1>Forbidden</h1>');
res.write(
- '<p>You do not have permission to access ' +
+ '<p>You do not have permission to access ' +
escapeHtml(path) + ' on this server.</p>'
);
res.end();
@@ -151,8 +153,8 @@ StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) {
res.write('<title>301 Moved Permanently</title>\n');
res.write('<h1>Moved Permanently</h1>');
res.write(
- '<p>The document has moved <a href="' +
- redirectUrl +
+ '<p>The document has moved <a href="' +
+ redirectUrl +
'">here</a>.</p>'
);
res.end();
@@ -187,7 +189,7 @@ StaticServlet.prototype.sendDirectory_ = function(req, res, path) {
return self.sendRedirect_(req, res, redirectUrl);
}
fs.readdir(path, function(err, files) {
- if (err)
+ if (err)
return self.sendError_(req, res, error);
if (!files.length)
@@ -235,5 +237,5 @@ StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) {
res.end();
};
-// Must be last,
+// Must be last,
main(process.argv);
diff --git a/src/Angular.js b/src/Angular.js
index 62cfdef6..004ab48f 100644
--- a/src/Angular.js
+++ b/src/Angular.js
@@ -10,7 +10,7 @@ if (typeof document.getAttribute == $undefined)
*
* @description Converts string to lowercase
* @param {string} value
- * @return {string} Lowercased string.
+ * @returns {string} Lowercased string.
*/
var lowercase = function (value){ return isString(value) ? value.toLowerCase() : value; };
@@ -22,7 +22,7 @@ var lowercase = function (value){ return isString(value) ? value.toLowerCase() :
*
* @description Converts string to uppercase.
* @param {string} value
- * @return {string} Uppercased string.
+ * @returns {string} Uppercased string.
*/
var uppercase = function (value){ return isString(value) ? value.toUpperCase() : value; };
@@ -100,7 +100,7 @@ var _undefined = undefined,
* @ngdoc overview
* @name angular.filter
* @namespace Namespace for all filters.
- *
+ * @description
* # Overview
* Filters are a standard way to format your data for display to the user. For example, you
* might have the number 1234.5678 and would like to display it as US currency: $1,234.57.
@@ -364,7 +364,7 @@ function isLeafNode (node) {
* @param {*} source The source to be used during copy.
* Can be any type including primitives, null and undefined.
* @param {(Object|Array)=} destination Optional destination into which the source is copied
- * @return {*}
+ * @returns {*}
*/
function copy(source, destination){
if (!destination) {
@@ -507,7 +507,7 @@ function compile(element, existingScope) {
/**
* Parses an escaped url query string into key-value pairs.
- * @return Object.<(string|boolean)>
+ * @returns Object.<(string|boolean)>
*/
function parseKeyValue(/**string*/keyValue) {
var obj = {}, key_value, key;
diff --git a/src/filters.js b/src/filters.js
index ed824f93..767c1234 100644
--- a/src/filters.js
+++ b/src/filters.js
@@ -6,24 +6,25 @@
* @description
* Formats a number as a currency (ie $1,234.56).
*
- * @param {number} amout Input to filter.
+ * @param {number} amount Input to filter.
* @returns {string} Formated number.
*
* @css ng-format-negative
* When the value is negative, this css class is applied to the binding making it by default red.
- *
+ *
* @example
* <input type="text" name="amount" value="1234.56"/> <br/>
* {{amount | currency}}
*
* @scenario
* it('should init with 1234.56', function(){
- * expect(bind('amount')).toEqual('$1,234.56');
+ * expect(binding('amount')).toEqual('$1,234.56');
* });
* it('should update', function(){
- * element(':input[name=amount]').value('-1234');
- * expect(bind('amount')).toEqual('-$1,234.00');
- * expect(bind('amount')).toHaveColor('red');
+ * input('amount').enter('-1234');
+ * expect(binding('amount')).toEqual('$-1,234.00');
+ * // TODO: implement
+ * // expect(binding('amount')).toHaveColor('red');
* });
*/
angularFilter.currency = function(amount){
@@ -31,7 +32,6 @@ angularFilter.currency = function(amount){
return '$' + angularFilter['number'].apply(this, [amount, 2]);
};
-
/**
* @ngdoc filter
* @name angular.filter.number
@@ -43,7 +43,7 @@ angularFilter.currency = function(amount){
* If the input is not a number empty string is returned.
*
* @param {(number|string)} number Number to format.
- * @param {(number|string)=} fractionSize Number of decimal places to round the number to. Default 2.
+ * @param {(number|string)=2} fractionSize Number of decimal places to round the number to. Default 2.
* @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
*
* @example
@@ -54,10 +54,10 @@ angularFilter.currency = function(amount){
*
* @scenario
* it('should format numbers', function(){
- * expect(element('span[ng\\:bind="1234.56789|number"]').val()).toBe('1,234.57');
- * expect(element('span[ng\\:bind="1234.56789|number:0"]').val()).toBe('1,234');
- * expect(element('span[ng\\:bind="1234.56789|number:2"]').val()).toBe('1,234.56');
- * expect(element('span[ng\\:bind="-1234.56789|number:4"]').val()).toBe('-1,234.56789');
+ * expect(binding('1234.56789|number')).toEqual('1,234.57');
+ * expect(binding('1234.56789|number:0')).toEqual('1,235');
+ * expect(binding('1234.56789|number:2')).toEqual('1,234.57');
+ * expect(binding('-1234.56789|number:4')).toEqual('-1,234.5679');
* });
*/
angularFilter.number = function(number, fractionSize){
@@ -204,10 +204,10 @@ angularFilter.date = function(date, format) {
*
* @scenario
* it('should jsonify filtered objects', function() {
- * expect(element('pre[ng\\:bind-template="{{ {a:1, b:[]} | json }}"]').val()).toBe(
+ * expect(binding('{{ {a:1, b:[]} | json')).toEqual(
* '{\n "a":1,\n "b":[]}'
* );
- * }
+ * });
*
*/
angularFilter.json = function(object) {
@@ -257,7 +257,7 @@ angularFilter.uppercase = uppercase;
* filtered and you can't get the content through the sanitizer.
*
* @param {string} html Html input.
- * @param {string=} option If 'unsafe' then do not sanitize the HTML input.
+ * @param {string='safe'} option If 'unsafe' then do not sanitize the HTML input.
* @returns {string} Sanitized or raw html.
*/
angularFilter.html = function(html, option){
diff --git a/src/widgets.js b/src/widgets.js
index 4d8c71d5..5eda5345 100644
--- a/src/widgets.js
+++ b/src/widgets.js
@@ -1,3 +1,7 @@
+/**
+ *
+ */
+
function modelAccessor(scope, element) {
var expr = element.attr('name');
if (!expr) throw "Required field 'name' not found.";
@@ -240,6 +244,16 @@ angularWidget('option', function(){
});
+/*ng:doc
+ * @type widget
+ * @name ng:include
+ *
+ * @description
+ *
+ * @example
+ *
+ * @scenario
+ */
angularWidget('ng:include', function(element){
var compiler = this,
srcExp = element.attr("src"),