Compare commits
244 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f947e5b8b | |||
| 75de7395bb | |||
| 9f73b1f290 | |||
| bd33f60276 | |||
| 8682befc72 | |||
| 3f0a37f380 | |||
| f5d08963b0 | |||
| 7a48ee6aa9 | |||
| 6512a736ac | |||
| 9e0fa5b7c8 | |||
| 9368ea3814 | |||
| a6a4c18ecd | |||
| d8acfe7389 | |||
| b036c0bc58 | |||
| bf03eb007c | |||
| c2f2587a79 | |||
| 5d0d34ae72 | |||
| 70c74a9c4e | |||
| 22c5b7059b | |||
| 190498efd7 | |||
| a5990050d4 | |||
| 8d507df8c9 | |||
| 567f33823b | |||
| 5340d1e0b1 | |||
| 8d91ec4173 | |||
| 746e7a9768 | |||
| 94737cd017 | |||
| c8bb044be1 | |||
| 4a569560d8 | |||
| 17ee0f031a | |||
| 10646c9f6f | |||
| 964e394555 | |||
| 2c0f7ffe3a | |||
| a5eb3ed107 | |||
| 63690d1892 | |||
| 050e5d773f | |||
| da4b8a74c3 | |||
| 5be92f7b9c | |||
| 459a01e582 | |||
| 72e554ffeb | |||
| 3bd96609e9 | |||
| d73635d573 | |||
| ad3b811b8b | |||
| d3f7bd699e | |||
| 1c55123f9c | |||
| 1d7b9d5626 | |||
| 347be5ae9a | |||
| 934f44f69e | |||
| d3d2a3a374 | |||
| 527d0a1600 | |||
| 23875cb330 | |||
| b0be87f663 | |||
| 9ccd2f0412 | |||
| 99004b0aed | |||
| ab040254f0 | |||
| 4f5d5029c2 | |||
| f534def0c6 | |||
| c3e32f1a51 | |||
| 4f22d6866c | |||
| aab3df7aea | |||
| 0a6cf70deb | |||
| c79aba92f6 | |||
| 84dedb81e7 | |||
| e999740044 | |||
| 0ad39dde4f | |||
| 4c71824a69 | |||
| 47c454a315 | |||
| 16086aa37c | |||
| c0a26b1853 | |||
| 9db2170dcf | |||
| b28dee7fd5 | |||
| 142a985f33 | |||
| bd5ec7c32a | |||
| bdc251c5a5 | |||
| ad9537cdf6 | |||
| 807d8c92b3 | |||
| 454626ad39 | |||
| 247c99a8a4 | |||
| da1d50fbe9 | |||
| 67d064820c | |||
| b2631f6170 | |||
| 1430c6d6b1 | |||
| 3ea5941f0e | |||
| d0270d9256 | |||
| 5f080193cb | |||
| cf891428bf | |||
| 5b9967518e | |||
| 38f462d572 | |||
| 56eeba0f3c | |||
| 5a534235b6 | |||
| e7a0fb250f | |||
| e3ddc2bcc4 | |||
| d11088eb43 | |||
| a5df1fc41f | |||
| ec4d446f89 | |||
| b225083a21 | |||
| e84d3334b0 | |||
| 7989c7d24a | |||
| 5c36f466e1 | |||
| f8151afd90 | |||
| 74120eaa0f | |||
| b370fac4fc | |||
| 23fc73081f | |||
| e5e69d9b90 | |||
| fa722447f8 | |||
| 81d10e819e | |||
| 809ca94e1c | |||
| 824eab9029 | |||
| d503dfe99b | |||
| e4d33917e3 | |||
| 6326e2028b | |||
| 8aed2047f0 | |||
| f60b6b0938 | |||
| eea7113abe | |||
| c8f34e7f6b | |||
| 011fa39c2a | |||
| 58d0e8945d | |||
| 2bbced212e | |||
| 5a8ad8fe32 | |||
| 41d5938883 | |||
| 5480d2a80b | |||
| 95adf3a4d8 | |||
| cc315ef4cc | |||
| 41c233ada1 | |||
| 46091f811b | |||
| fde2ccb3f5 | |||
| 1cc2ad2443 | |||
| 1aa46fac62 | |||
| 5bde02a8ca | |||
| d07e9f77f1 | |||
| aa21c521eb | |||
| bd14a90610 | |||
| 9f1b9849d8 | |||
| 47f159cdf3 | |||
| 99eb123d79 | |||
| 6515adc118 | |||
| b7aff92354 | |||
| 6b3b00e095 | |||
| 921f7ce49e | |||
| 17eee57c52 | |||
| 4fc3ee8040 | |||
| 39d3ae80d9 | |||
| 480f2f33c1 | |||
| 9c9a89f7ff | |||
| 73194009a9 | |||
| 162f41a1ab | |||
| 7c82c4f837 | |||
| 97b1371199 | |||
| 95d1768c77 | |||
| c3d99d68da | |||
| 303a683081 | |||
| a0e8c45880 | |||
| 870547d185 | |||
| 0d1f8a0532 | |||
| b94600d71e | |||
| 3e5a4ef86c | |||
| efec0c358d | |||
| 1f59de35c9 | |||
| 9b53b25f15 | |||
| 3fbfa357ca | |||
| 50ef1f8e35 | |||
| 66c0bfaa8e | |||
| 1719b0aca5 | |||
| 7ee102eecf | |||
| fc7f11d03b | |||
| 3c7874b07b | |||
| 7f339a1782 | |||
| 72a5f007d8 | |||
| 63380bbbda | |||
| c635b69f5c | |||
| 522ec1a9ec | |||
| 9cb57772a4 | |||
| d54f09ef29 | |||
| 65989c6f0d | |||
| 4491bbdede | |||
| a6978b201b | |||
| 28e72cbe6b | |||
| 916dadd8ec | |||
| e509ec37f5 | |||
| ee0e9a4452 | |||
| 9d36368ff9 | |||
| d4bcee0799 | |||
| dd687e2bf5 | |||
| 4c69d694d7 | |||
| ff7c738c21 | |||
| 51a22cf435 | |||
| c2c60ab49a | |||
| 71c2f24fc6 | |||
| fc78738cc6 | |||
| c7052f098d | |||
| 7d6f5f986e | |||
| beeb5ff908 | |||
| b2d63ac48b | |||
| 4af32de84a | |||
| a130bb899d | |||
| cc749760fd | |||
| b467a50bc7 | |||
| a1652057a5 | |||
| 7e6f999221 | |||
| 625cc7609c | |||
| c51273b1fb | |||
| 0a8b3161b1 | |||
| ba554eeb1b | |||
| 5f0af2cd0e | |||
| 7411b24812 | |||
| ae5f6f48b4 | |||
| c5b2bf083c | |||
| 0499c47270 | |||
| 43a4ff4cdf | |||
| 6b8ed42670 | |||
| c57df3dc77 | |||
| 6d53808475 | |||
| a7e8a503fd | |||
| 324694a58b | |||
| effcd340e9 | |||
| 264f960800 | |||
| 257e97a65f | |||
| c048f0d8e8 | |||
| 96e37a0866 | |||
| 5062d32621 | |||
| d458f31711 | |||
| fc9ce9ec07 | |||
| da17c61444 | |||
| e5c135ac50 | |||
| 1a43f36e23 | |||
| 1c305dc67a | |||
| a397645537 | |||
| f077649f48 | |||
| f3ac2cd434 | |||
| 7779630989 | |||
| 00ca67e4be | |||
| 91b6c5f7ff | |||
| 5be325a0c1 | |||
| b7027b9d87 | |||
| fe8353bc5e | |||
| c780030c6e | |||
| d5e9f38f3d | |||
| dc66687149 | |||
| 3d6a099d6e | |||
| 8767e766d1 | |||
| 47066e70e1 | |||
| c0d30aedfc | |||
| b246d6e2ab | |||
| 3b04b48b7c |
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType">
|
||||
<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
|
||||
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_BUILDER_ENABLED" value="true"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_BUILD_SCOPE" value="${working_set:<?xml version="1.0" encoding="UTF-8"?> <launchConfigurationWorkingSet editPageId="org.eclipse.ui.resourceWorkingSetPage" factoryID="org.eclipse.ui.internal.WorkingSetFactory" id="1262905463390_2" label="workingSet" name="workingSet"> <item factoryID="org.eclipse.ui.internal.model.ResourceFactory" path="/angular.js/test" type="2"/> <item factoryID="org.eclipse.ui.internal.model.ResourceFactory" path="/angular.js/src" type="2"/> </launchConfigurationWorkingSet>}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/angular.js}/test.sh"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,auto,"/>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?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:<?xml version="1.0" encoding="UTF-8"?> <resources> <item path="/angular.js/docs" type="2"/> </resources>}"/>
|
||||
<stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE" value="${working_set:<?xml version="1.0" encoding="UTF-8"?> <resources> <item path="/angular.js/build" type="2"/> </resources>}"/>
|
||||
<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:<?xml version="1.0" encoding="UTF-8"?> <resources> <item path="/angular.js/docs/callback.js" type="1"/> <item path="/angular.js/docs/collect.js" type="1"/> <item path="/angular.js/docs/filter.template" type="1"/> </resources>}"/>
|
||||
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_BUILDER_ENABLED" value="true"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_BUILD_SCOPE" value="${working_set:<?xml version="1.0" encoding="UTF-8"?> <resources> <item path="/angular.js/docs" type="2"/> <item path="/angular.js/src" type="2"/> </resources>}"/>
|
||||
<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"/>
|
||||
|
||||
+3
-1
@@ -2,4 +2,6 @@ build/
|
||||
angularjs.netrc
|
||||
jstd.log
|
||||
.DS_Store
|
||||
regression/temp.html
|
||||
regression/temp*.html
|
||||
performance/temp*.html
|
||||
.idea/workspace.xml
|
||||
|
||||
Generated
+67
@@ -0,0 +1,67 @@
|
||||
IMPORTANT: THIS IS TO CERTIFY THE RIGHT TO USE THE JETBRAINS SOFTWARE PRODUCT, GRANTED BY JETBRAINS S.R.O. UNDER THE TERMS AND CONDITIONS OF THE LICENSE AGREEMENT INCLUDED WITH THE SOFTWARE. PLEASE SAVE A COPY OF THIS EMAIL FOR FUTURE REFERENCES.
|
||||
|
||||
========LICENSE DETAILS========
|
||||
|
||||
Type: Open Source License
|
||||
Reference No*: LC-93161-D352729080
|
||||
Date of Issue: 3 November 2010
|
||||
Expiration Date: 3 November 2011
|
||||
Number of Authorized Users: not limited
|
||||
|
||||
* Please quote this reference when contacting JetBrains
|
||||
|
||||
===========LICENSEE============
|
||||
|
||||
Name: angularjs
|
||||
Customer ID: 93161
|
||||
Address:
|
||||
|
||||
=======SOFTWARE PRODUCT========
|
||||
|
||||
Product Name: WebStorm
|
||||
Licensed Version: 1.0 and any new product release which is made generally available before 3 November 2011
|
||||
|
||||
The software is shipped electronically and is available for download from:
|
||||
http://www.jetbrains.com/webstorm/download/
|
||||
|
||||
Your WebStorm license includes a 1-year upgrade subscription qualifying you for free upgrades to any new versions of WebStorm released by JetBrains during your upgrade subscription period starting on your license issue date and ending on 3 November 2011. To check availability of the new versions of WebStorm, please visit http://www.jetbrains.com.
|
||||
|
||||
For running any new version of WebStorm released by JetBrains during your upgrade subscription period, please use the included below licensing details.
|
||||
|
||||
You can renew your upgrade subscription before or after its expiration. Your new subscription period will start on the date following the expiration date of your current upgrade subscription.
|
||||
|
||||
=========INSTALLATION==========
|
||||
|
||||
Run WebStorm and follow the Installation Wizard's instructions. To register for use of the software or change your existing registration details, go to Help/Register menu of the software and enter the included below the User Name and License Key(s) into the registration dialog:
|
||||
|
||||
User Name: angularjs
|
||||
|
||||
===== LICENSE BEGIN =====
|
||||
93161-03112010
|
||||
00000jBsEx59XVlc79fV"aAqWXQ09e
|
||||
jQsg5TNp5X4HGhc10LNBdu!!ejRcFG
|
||||
7h3S6T09YcRWs23TH0RgaM87!HqmQo
|
||||
===== LICENSE END =====
|
||||
|
||||
===DOCUMENTATION AND SUPPORT===
|
||||
|
||||
WebStorm documentation:
|
||||
http://www.jetbrains.com/webstorm/documentation/
|
||||
|
||||
Available support resources:
|
||||
http://www.jetbrains.com/support/
|
||||
|
||||
Technical support contact:
|
||||
support@jetbrains.com
|
||||
|
||||
Contact for the license renewal requests:
|
||||
opensource@jetbrains.com
|
||||
|
||||
For questions, please contact:
|
||||
sales@jetbrains.com
|
||||
|
||||
|
||||
JetBrains Sales Team
|
||||
http://www.jetbrains.com
|
||||
"Develop with pleasure!"
|
||||
|
||||
Generated
-500
@@ -1,500 +0,0 @@
|
||||
<?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="updated Rakefile to support packaging of the docs">
|
||||
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/filters.js" afterPath="$PROJECT_DIR$/src/filters.js" />
|
||||
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/docs/docs-scenario.html" afterPath="$PROJECT_DIR$/docs/docs-scenario.html" />
|
||||
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/workspace.xml" afterPath="$PROJECT_DIR$/.idea/workspace.xml" />
|
||||
</list>
|
||||
<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">
|
||||
<splitter split-orientation="horizontal" split-proportion="0.5">
|
||||
<split-first>
|
||||
<leaf>
|
||||
<file leaf-file-name="filters.js" pinned="false" current="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/src/filters.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state line="377" column="5" selection-start="12137" selection-end="12137" vertical-scroll-proportion="0.0">
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="docs-scenario.html" pinned="false" current="true" current-in-tab="true">
|
||||
<entry file="file://$PROJECT_DIR$/docs/docs-scenario.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state line="3" column="26" selection-start="119" selection-end="119" vertical-scroll-proportion="0.039173014">
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="validators.js" pinned="false" current="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/src/validators.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state line="54" column="69" selection-start="1564" selection-end="1611" vertical-scroll-proportion="0.0">
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="test.sh" pinned="false" current="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/test.sh">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state line="3" column="13" selection-start="59" selection-end="59" vertical-scroll-proportion="0.0">
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="docs-scenario.html" pinned="false" current="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/build/docs/docs-scenario.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state line="5" column="44" selection-start="259" selection-end="259" vertical-scroll-proportion="0.0">
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="index.html" pinned="false" current="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/build/docs/index.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state line="24" column="7" selection-start="1033" selection-end="1033" vertical-scroll-proportion="0.0">
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="angular.filter.html.html" pinned="false" current="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/build/docs/angular.filter.html.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0">
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
</leaf>
|
||||
</split-first>
|
||||
<split-second>
|
||||
<leaf>
|
||||
<file leaf-file-name="FiltersSpec.js" pinned="false" current="false" current-in-tab="true">
|
||||
<entry file="file://$PROJECT_DIR$/test/FiltersSpec.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state line="89" column="20" selection-start="2996" selection-end="2996" vertical-scroll-proportion="0.7635783">
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
</leaf>
|
||||
</split-second>
|
||||
</splitter>
|
||||
</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" />
|
||||
<option value="$PROJECT_DIR$/src/scenario/dsl.js" />
|
||||
<option value="$PROJECT_DIR$/test.sh" />
|
||||
<option value="$PROJECT_DIR$/test/FiltersSpec.js" />
|
||||
<option value="$PROJECT_DIR$/src/filters.js" />
|
||||
<option value="$PROJECT_DIR$/docs/docs-scenario.html" />
|
||||
</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="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="docs" />
|
||||
<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>
|
||||
<pane id="Favorites" />
|
||||
<pane id="Scope" />
|
||||
</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">
|
||||
<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="false" name="test.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$/test.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="4">
|
||||
<item index="0" class="java.lang.String" itemvalue="Bash.gen_docs.sh" />
|
||||
<item index="1" class="java.lang.String" itemvalue="Bash.rake compile" />
|
||||
<item index="2" class="java.lang.String" itemvalue="Bash.gen_docs" />
|
||||
<item index="3" class="java.lang.String" itemvalue="Bash.test.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="true" />
|
||||
<layout>
|
||||
<window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32943603" sideWeight="0.0" 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="true" 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="Messages" 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="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" />
|
||||
</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" />
|
||||
</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>
|
||||
<entry file="file://$PROJECT_DIR$/src/scenario/dsl.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state line="109" column="11" selection-start="3010" selection-end="3010" vertical-scroll-proportion="-0.021905806">
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/validators.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state line="54" column="69" selection-start="1564" selection-end="1611" vertical-scroll-proportion="0.0">
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/test.sh">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state line="3" column="13" selection-start="59" selection-end="59" vertical-scroll-proportion="0.0">
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/build/docs/docs-scenario.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state line="5" column="44" selection-start="259" selection-end="259" vertical-scroll-proportion="0.0">
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/build/docs/index.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state line="24" column="7" selection-start="1033" selection-end="1033" vertical-scroll-proportion="0.0">
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/build/docs/angular.filter.html.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0">
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/test/FiltersSpec.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state line="89" column="20" selection-start="2996" selection-end="2996" vertical-scroll-proportion="0.7635783">
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/src/filters.js">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state line="377" column="5" selection-start="12137" selection-end="12137" vertical-scroll-proportion="0.0">
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/docs/docs-scenario.html">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state line="3" column="26" selection-start="119" selection-end="119" vertical-scroll-proportion="0.039173014">
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</component>
|
||||
</project>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<?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="docs-data.js|docs-scenario.js" kind="src" path="docs"/>
|
||||
<classpathentry excluding="test/" kind="src" path="test"/>
|
||||
<classpathentry kind="src" path="test/test"/>
|
||||
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#Mon Jan 24 10:31:47 PST 2011
|
||||
activeContentFilterList=*.makefile,makefile,*.Makefile,Makefile,Makefile.*,*.mk,MANIFEST.MF
|
||||
addNewLine=true
|
||||
convertActionOnSaave=AnyEdit.CnvrtTabToSpaces
|
||||
eclipse.preferences.version=1
|
||||
inActiveContentFilterList=
|
||||
javaTabWidthForJava=true
|
||||
org.eclipse.jdt.ui.editor.tab.width=2
|
||||
projectPropsEnabled=false
|
||||
removeTrailingSpaces=true
|
||||
replaceAllSpaces=false
|
||||
replaceAllTabs=false
|
||||
saveAndAddLine=true
|
||||
saveAndConvert=true
|
||||
saveAndTrim=true
|
||||
useModulo4Tabs=false
|
||||
+207
@@ -1,3 +1,210 @@
|
||||
# <angular/> 0.9.10 flea-whisperer (2011-01-26) #
|
||||
|
||||
### Features
|
||||
- new [`ng:view`](http://docs.angularjs.org/#!angular.widget.ng:view) widget to simplify integration
|
||||
with the `$route` service
|
||||
- the content of all standard HTML widgets is now being processed
|
||||
(e.g. `<button>{{foo}}</button>` works now) (commit 1d7b9d56)
|
||||
- new [`$log`](http://docs.angularjs.org/#!angular.mock.service.$log) and
|
||||
[`$exceptionHandler`](http://docs.angularjs.org/#!angular.mock.service.$exceptionHandler) service
|
||||
mocks now part of `angular-mocks.js` (commit f5d08963)
|
||||
|
||||
### Bug Fixes
|
||||
- <select> (one/multiple) could not chose from a list of objects (commit 347be5ae)
|
||||
- null and other falsy values should not be rendered in the view (issue #242)
|
||||
|
||||
### Docs
|
||||
- rewrite of several major portions of angular.service.*, angular.Array.*, angular.Object.* docs
|
||||
- added support for [sitemap]((http://docs.angularjs.org/sitemap.xml) to make the docs indexable by
|
||||
search crawlers
|
||||
- transition of Developer Guide docs from the wiki into docs.angularjs.org
|
||||
- lots of improvements related to formatting of the content of docs.anguarjs.org
|
||||
|
||||
|
||||
# <angular/> 0.9.9 time-shift (2011-01-13) #
|
||||
|
||||
### Security
|
||||
- Added a just in case security check for JSON parsing. (commit 5f080193)
|
||||
- Completed security review with the Google Security Team.
|
||||
|
||||
### Performance
|
||||
- $location and $cookies services are now lazily initialized to avoid the polling overhead when
|
||||
not needed.
|
||||
- $location service now listens for `onhashchange` events (if supported by browser) instead of
|
||||
constant polling. (commit 16086aa3)
|
||||
- input widgets known listens on keydown events instead of keyup which improves perceived
|
||||
performance (commit 47c454a3)
|
||||
- angular boots significantly sooner by listening for DOMContentLoaded event instead of
|
||||
window.load when supported by browser (commit c79aba92)
|
||||
- new service $updateView which may be used in favor of $root.$eval() to run a complete eval on
|
||||
the entire document. This service bulks and throttles DOM updates to improve performance.
|
||||
(commit 47c454a3)
|
||||
|
||||
### Docs
|
||||
- Major improvements to the doc parser (commit 4f22d686)
|
||||
- Docs now offline enabled (all dependencies are bundled in the tarball) (commit 4f5d5029)
|
||||
- Added support for navigating the docs app with keyboard shortcuts (tab and ctrl+alt+s)
|
||||
|
||||
### Bugfixes
|
||||
- `angular.Object.equals` now properly handless comparing an object with a null (commit b0be87f6)
|
||||
- Several issues were addressed in the `$location` service (commit 23875cb3)
|
||||
- angular.filter.date now properly handles some corner-cases (issue #159 - fix contributed by Vojta)
|
||||
|
||||
### Breaking changes
|
||||
- API for accessing registered services — `scope.$inject` — was renamed to
|
||||
[`scope.$service`](http://docs.angularjs.org/#!angular.scope.$service). (commit b2631f61)
|
||||
|
||||
- Support for `eager-published` services was removed. This change was done to make explicit
|
||||
dependency declaration always required in order to allow making relatively expensive services
|
||||
lazily initialized (e.g. $cookie, $location), as well as remove 'magic' and reduce unnecessary
|
||||
scope namespace pollution. (commit 3ea5941f)
|
||||
|
||||
Complete list of affected services:
|
||||
|
||||
- $location
|
||||
- $route
|
||||
- $cookies
|
||||
- $window
|
||||
- $document
|
||||
- $exceptionHandler
|
||||
- $invalidWidgets
|
||||
|
||||
To temporarily preserve the 'eager-published' status for these services, you may use `ng:init`
|
||||
(e.g. `ng:init="$location = $service('$location'), ...`) in the view or more correctly create
|
||||
a service like this:
|
||||
|
||||
angular.service('published-svc-shim', function() {
|
||||
this.$location = this.$service('$location');
|
||||
this.$route = this.$service('$route');
|
||||
this.$cookies = this.$service('$cookies');
|
||||
this.$window = this.$service('$window');
|
||||
this.$document = this.$service('$document');
|
||||
this.$exceptionHandler = this.$service('$exceptionHandler');
|
||||
this.$invalidWidgets = this.$service('$invalidWidgets');
|
||||
}, {$eager: true});
|
||||
|
||||
- In the light of the `eager-published` change, to complete the cleanup we renamed `$creation`
|
||||
property of services to `eager` with its value being a boolean.
|
||||
To transition, please rename all `$creation: 'eager'` declarations to `$eager: true`.
|
||||
(commit 1430c6d6)
|
||||
|
||||
- `angular.foreach` was renamed to `angular.forEach` to make the api consistent. (commit 0a6cf70d)
|
||||
|
||||
- The `toString` method of the `angular.service.$location` service was removed. (commit 23875cb3)
|
||||
|
||||
|
||||
# <angular/> 0.9.8 astral-projection (2010-12-23) #
|
||||
|
||||
### Docs/Getting started
|
||||
- angular-seed project to get you hacking on an angular apps quickly
|
||||
https://github.com/angular/angular-seed
|
||||
|
||||
### Performance
|
||||
- Delegate JSON parsing to native parser (JSON.parse) if available
|
||||
|
||||
### Bug Fixes
|
||||
- Ignore input widgets which have no name (issue #153)
|
||||
|
||||
|
||||
# <angular/> 0.9.7 sonic-scream (2010-12-10) #
|
||||
|
||||
### Bug Fixes
|
||||
- $defer service should always call $eval on the root scope after a callback runs (issue #189)
|
||||
- fix for failed assignments of form obj[0].name=value (issue #169)
|
||||
- significant parser improvements that resulted in lower memory usage
|
||||
(commit 23fc73081feb640164615930b36ef185c23a3526)
|
||||
|
||||
### Docs
|
||||
- small docs improvements (mainly docs for the $resource service)
|
||||
|
||||
### Breaking changes
|
||||
- Angular expressions in the view used to support regular expressions. This feature was rarely
|
||||
used and added unnecessary complexity. It not a good idea to have regexps in the view anyway,
|
||||
so we removed this support. If you had any regexp in your views, you will have to move them to
|
||||
your controllers. (commit e5e69d9b90850eb653883f52c76e28dd870ee067)
|
||||
|
||||
|
||||
# <angular/> 0.9.6 night-vision (2010-12-06) #
|
||||
|
||||
### Security
|
||||
- several improvements in the HTML sanitizer code to prevent code execution via `href`s and other
|
||||
attributes.
|
||||
Commits:
|
||||
- 41d5938883a3d06ffe8a88a51efd8d1896f7d747
|
||||
- 2bbced212e2ee93948c45360fee00b2e3f960392
|
||||
|
||||
### Docs
|
||||
- set up http://docs.angularjs.org domain, the docs for the latest release will from now on be
|
||||
deployed here.
|
||||
- docs app UI polishing with dual scrolling and other improvements
|
||||
|
||||
### Bug Fixes
|
||||
- `select` widget now behaves correctly when it's `option` items are created via `ng:repeat`
|
||||
(issue #170)
|
||||
- fix for async xhr cache issue #152 by adding `$browser.defer` and `$defer` service
|
||||
|
||||
### Breaking Changes
|
||||
- Fix for issue #152 might break some tests that were relying on the incorrect behavior. The
|
||||
breakage will usually affect code that tests resources, xhr or services/widgets build on top of
|
||||
these. All that is typically needed to resolve the issue is adding a call to
|
||||
`$browser.defer.flush()` in your test just before the point where you expect all cached
|
||||
resource/xhr requests to return any results. Please see 011fa39c2a0b5da843395b538fc4e52e5ade8287
|
||||
for more info.
|
||||
- The HTML sanitizer is slightly more strinct now. Please see info in the "Security" section above.
|
||||
|
||||
|
||||
# <angular/> 0.9.5 turkey-blast (2010-11-25) #
|
||||
|
||||
### Docs
|
||||
- 99% of the content from the angular wiki is now in the docs
|
||||
|
||||
### Api
|
||||
- added `angular.Array.limitTo` to make it easy to select first or last few items of an array
|
||||
|
||||
|
||||
# <angular/> 0.9.4 total-recall (2010-11-18) #
|
||||
|
||||
### Docs
|
||||
- searchable docs
|
||||
- UI improvements
|
||||
- we now have ~85% of the wiki docs migrated to ng docs
|
||||
- some but not all docs were updated along the way
|
||||
|
||||
|
||||
### Api
|
||||
- ng:include now supports `onload` attribute (commit cc749760)
|
||||
|
||||
### Misc
|
||||
- Better error handling - compilation exception now contain stack trace (commit b2d63ac4)
|
||||
|
||||
|
||||
# <angular/> 0.9.3 cold-resistance (2010-11-10) #
|
||||
|
||||
### Docs
|
||||
- prettier docs app with syntax highlighting for examples, etc
|
||||
- added documentation, examples and scenario tests for many more apis including:
|
||||
- all directives
|
||||
- all formatters
|
||||
- all validators
|
||||
- some widgets
|
||||
|
||||
### Api
|
||||
- date filter now accepts strings that angular.String.toDate can convert to Date objects
|
||||
- angular.String.toDate supports ISO8061 formated strings with all time fractions being optional
|
||||
- ng:repeat now exposes $position with values set to 'first', 'middle' or 'last'
|
||||
- ng:switch now supports ng:switch-default as fallback switch option
|
||||
|
||||
### Breaking changes
|
||||
- we now support ISO 8601 extended format datetime strings (YYYY-MM-DDTHH:mm:ss.SSSZ) as defined
|
||||
in EcmaScript 5 throughout angular. This means that the following apis switched from
|
||||
YYYY-MM-DDTHH:mm:ssZ to YYYY-MM-DDTHH:mm:ss.SSSZ (note the added millis) when representing dates:
|
||||
- angular.Date.toString
|
||||
- angular.String.fromDate
|
||||
- JSON serialization and deserialization (used by json filter, $xhr and $resource)
|
||||
- removed SSN validator. It's unlikely that most people will need it and if they do, it can be added
|
||||
simple RegExp validator.
|
||||
|
||||
|
||||
# <angular/> 0.9.2 faunal-mimicry (2010-11-03) #
|
||||
|
||||
### Docs
|
||||
|
||||
@@ -158,7 +158,7 @@ end
|
||||
|
||||
desc 'Generate docs'
|
||||
task :docs do
|
||||
`node docs/collect.js`
|
||||
`node docs/src/gen-docs.js`
|
||||
end
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ task :package => [:clean, :compile, :docs] do
|
||||
FileUtils.rm_r(path_to('pkg'), :force => true)
|
||||
FileUtils.mkdir_p(pkg_dir)
|
||||
|
||||
['test/angular-mocks.js',
|
||||
['src/angular-mocks.js',
|
||||
path_to('angular.js'),
|
||||
path_to('angular.min.js'),
|
||||
path_to('angular-ie-compat.js'),
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
border: 2px solid #FF0000;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
font-size: smaller;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.ng-validation-error {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular.directive
|
||||
@namespace Namespace for all directives.
|
||||
|
||||
@description
|
||||
A directive is an HTML attribute that you can use in an existing HTML element type or in a
|
||||
DOM element type that you create as {@link angular.widget}, to modify that element's
|
||||
properties. You can use any number of directives per element.
|
||||
|
||||
For example, you can add the ng:bind directive as an attribute of an HTML span element, as in
|
||||
`<span ng:bind="1+2"></span>`. How does this work? The compiler passes the attribute value
|
||||
`1+2` to the ng:bind extension, which in turn tells the {@link angular.scope} to watch that
|
||||
expression and report changes. On any change it sets the span text to the expression value.
|
||||
|
||||
Here's how to define {@link angular.directive.ng:bind ng:bind}:
|
||||
<pre>
|
||||
angular.directive('ng:bind', function(expression, compiledElement) {
|
||||
var compiler = this;
|
||||
return function(linkElement) {
|
||||
var currentScope = this;
|
||||
currentScope.$watch(expression, function(value) {
|
||||
linkElement.text(value);
|
||||
});
|
||||
};
|
||||
});
|
||||
</pre>
|
||||
|
||||
# Directive vs. Attribute Widget
|
||||
Both [attribute widgets](#!angular.widget) and directives can compile a DOM element
|
||||
attribute. So why have two different ways to do the same thing? The answer is that order
|
||||
matters, but we have no control over the order in which attributes are read. To solve this
|
||||
we apply attribute widget before the directive.
|
||||
|
||||
For example, consider this piece of HTML, which uses the directives `ng:repeat`, `ng:init`,
|
||||
and `ng:bind`:
|
||||
<pre>
|
||||
<ul ng:init="people=['mike', 'mary']">
|
||||
<li ng:repeat="person in people" ng:init="a=a+1" ng:bind="person"></li>
|
||||
</ul>
|
||||
</pre>
|
||||
|
||||
Notice that the order of execution matters here. We need to execute
|
||||
{@link angular.directive.ng:repeat ng:repeat} before we run the
|
||||
{@link angular.directive.ng:init ng:init} and `ng:bind` on the `<li/>;`. This is because we
|
||||
want to run the `ng:init="a=a+1` and `ng:bind="person"` once for each person in people. We
|
||||
could not have used directive to create this template because attributes are read in an
|
||||
unspecified order and there is no way of guaranteeing that the repeater attribute would
|
||||
execute first. Using the `ng:repeat` attribute directive ensures that we can transform the
|
||||
DOM element into a template.
|
||||
|
||||
Widgets run before directives. Widgets may manipulate the DOM whereas directives are not
|
||||
expected to do so, and so they run last.
|
||||
@@ -0,0 +1,43 @@
|
||||
@workInProgress
|
||||
@ngdoc function
|
||||
@name angular.element
|
||||
@function
|
||||
|
||||
@description
|
||||
Wraps a raw DOM element or HTML string as [jQuery](http://jquery.com) element.
|
||||
`angular.element` is either an alias for [jQuery](http://api.jquery.com/jQuery/) function if
|
||||
jQuery is loaded or a function that wraps the element or string in angular's jQuery lite
|
||||
implementation.
|
||||
|
||||
Real jQuery always takes precedence if it was loaded before angular.
|
||||
|
||||
Angular's jQuery lite implementation is a tiny API-compatible subset of jQuery which allows
|
||||
angular to manipulate DOM. The functions implemented are usually just the basic versions of
|
||||
them and might not support arguments and invocation styles.
|
||||
|
||||
NOTE: All element references in angular are always wrapped with jQuery (lite) and are never
|
||||
raw DOM references.
|
||||
|
||||
Angular's jQuery lite implements these functions:
|
||||
|
||||
- [addClass()](http://api.jquery.com/addClass/)
|
||||
- [after()](http://api.jquery.com/after/)
|
||||
- [append()](http://api.jquery.com/append/)
|
||||
- [attr()](http://api.jquery.com/attr/)
|
||||
- [bind()](http://api.jquery.com/bind/)
|
||||
- [children()](http://api.jquery.com/children/)
|
||||
- [clone()](http://api.jquery.com/clone/)
|
||||
- [css()](http://api.jquery.com/css/)
|
||||
- [data()](http://api.jquery.com/data/)
|
||||
- [hasClass()](http://api.jquery.com/hasClass/)
|
||||
- [parent()](http://api.jquery.com/parent/)
|
||||
- [remove()](http://api.jquery.com/remove/)
|
||||
- [removeAttr()](http://api.jquery.com/removeAttr/)
|
||||
- [removeClass()](http://api.jquery.com/removeClass/)
|
||||
- [removeData()](http://api.jquery.com/removeData/)
|
||||
- [replaceWith()](http://api.jquery.com/replaceWith/)
|
||||
- [text()](http://api.jquery.com/text/)
|
||||
- [trigger()](http://api.jquery.com/trigger/)
|
||||
|
||||
@param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
|
||||
@returns {Object} jQuery object.
|
||||
@@ -0,0 +1,76 @@
|
||||
@workInProgress
|
||||
@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.
|
||||
Filters allow you to do just that. In addition to transforming the data, filters also modify
|
||||
the DOM. This allows the filters to for example apply css styles to the filtered output if
|
||||
certain conditions were met.
|
||||
|
||||
|
||||
# Standard Filters
|
||||
|
||||
The Angular framework provides a standard set of filters for common operations, including:
|
||||
{@link angular.filter.currency currency}, {@link angular.filter.json json},
|
||||
{@link angular.filter.number number}, and {@link angular.filter.html html}. You can also add
|
||||
your own filters.
|
||||
|
||||
|
||||
# Syntax
|
||||
|
||||
Filters can be part of any {@link angular.scope} evaluation but are typically used with
|
||||
{{bindings}}. Filters typically transform the data to a new data type, formating the data in
|
||||
the process. Filters can be chained and take optional arguments. Here are few examples:
|
||||
|
||||
* No filter: {{1234.5678}} => 1234.5678
|
||||
* Number filter: {{1234.5678|number}} => 1,234.57. Notice the “,” and rounding to two
|
||||
significant digits.
|
||||
* Filter with arguments: {{1234.5678|number:5}} => 1,234.56780. Filters can take optional
|
||||
arguments, separated by colons in a binding. To number, the argument “5” requests 5 digits
|
||||
to the right of the decimal point.
|
||||
|
||||
|
||||
# Writing your own Filters
|
||||
|
||||
Writing your own filter is very easy: just define a JavaScript function on `angular.filter`.
|
||||
The framework passes in the input value as the first argument to your function. Any filter
|
||||
arguments are passed in as additional function arguments.
|
||||
|
||||
You can use these variables in the function:
|
||||
|
||||
* `this` — The current scope.
|
||||
* `this.$element` — The DOM element containing the binding. This allows the filter to manipulate
|
||||
the DOM in addition to transforming the input.
|
||||
|
||||
|
||||
@exampleDescription
|
||||
The following example filter reverses a text string. In addition, it conditionally makes the
|
||||
text upper-case (to demonstrate optional arguments) and assigns color (to demonstrate DOM
|
||||
modification).
|
||||
|
||||
@example
|
||||
<script type="text/javascript">
|
||||
angular.filter('reverse', function(input, uppercase, color) {
|
||||
var out = "";
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
out = input.charAt(i) + out;
|
||||
}
|
||||
if (uppercase) {
|
||||
out = out.toUpperCase();
|
||||
}
|
||||
if (color) {
|
||||
this.$element.css('color', color);
|
||||
}
|
||||
return out;
|
||||
});
|
||||
</script>
|
||||
|
||||
<input name="text" type="text" value="hello" /><br>
|
||||
No filter: {{text}}<br>
|
||||
Reverse: {{text|reverse}}<br>
|
||||
Reverse + uppercase: {{text|reverse:true}}<br>
|
||||
Reverse + uppercase + blue: {{text|reverse:true:"blue"}}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular.formatter
|
||||
@namespace Namespace for all formats.
|
||||
@description
|
||||
# Overview
|
||||
The formatters are responsible for translating user readable text in an input widget to a
|
||||
data model stored in an application.
|
||||
|
||||
# Writting your own Formatter
|
||||
Writing your own formatter is easy. Just register a pair of JavaScript functions with
|
||||
`angular.formatter`. One function for parsing user input text to the stored form,
|
||||
and one for formatting the stored data to user-visible text.
|
||||
|
||||
Here is an example of a "reverse" formatter: The data is stored in uppercase and in
|
||||
reverse, while it is displayed in lower case and non-reversed. User edits are
|
||||
automatically parsed into the internal form and data changes are automatically
|
||||
formatted to the viewed form.
|
||||
|
||||
<pre>
|
||||
function reverse(text) {
|
||||
var reversed = [];
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
reversed.unshift(text.charAt(i));
|
||||
}
|
||||
return reversed.join('');
|
||||
}
|
||||
|
||||
angular.formatter('reverse', {
|
||||
parse: function(value){
|
||||
return reverse(value||'').toUpperCase();
|
||||
},
|
||||
format: function(value){
|
||||
return reverse(value||'').toLowerCase();
|
||||
}
|
||||
});
|
||||
</pre>
|
||||
|
||||
@example
|
||||
<script type="text/javascript">
|
||||
function reverse(text) {
|
||||
var reversed = [];
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
reversed.unshift(text.charAt(i));
|
||||
}
|
||||
return reversed.join('');
|
||||
}
|
||||
|
||||
angular.formatter('reverse', {
|
||||
parse: function(value){
|
||||
return reverse(value||'').toUpperCase();
|
||||
},
|
||||
format: function(value){
|
||||
return reverse(value||'').toLowerCase();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
Formatted:
|
||||
<input type="text" name="data" value="angular" ng:format="reverse"/>
|
||||
<br/>
|
||||
|
||||
Stored:
|
||||
<input type="text" name="data"/><br/>
|
||||
<pre>{{data}}</pre>
|
||||
|
||||
|
||||
@scenario
|
||||
it('should store reverse', function(){
|
||||
expect(element('.doc-example input:first').val()).toEqual('angular');
|
||||
expect(element('.doc-example input:last').val()).toEqual('RALUGNA');
|
||||
|
||||
this.addFutureAction('change to XYZ', function($window, $document, done){
|
||||
$document.elements('.doc-example input:last').val('XYZ').trigger('change');
|
||||
done();
|
||||
});
|
||||
expect(element('.doc-example input:first').val()).toEqual('zyx');
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular
|
||||
@namespace The exported angular namespace.
|
||||
@@ -0,0 +1,166 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular.service
|
||||
|
||||
@description
|
||||
# Overview
|
||||
Services are substituable objects, which are wired together using dependency injection (DI).
|
||||
Each service could have dependencies (other services), which are passed in constructor.
|
||||
Because JS is dynamicaly typed language, dependency injection can not use static types
|
||||
to identify these dependencies, so each service must explicitely define its dependencies.
|
||||
This is done by `$inject` property.
|
||||
|
||||
|
||||
# Built-in services
|
||||
angular provides a set of services for common operations. These services can be overriden by custom
|
||||
services if needed.
|
||||
|
||||
Like other core angular variables and identifiers, the built-in services always start with `$`.
|
||||
|
||||
* `{@link angular.service.$browser $browser}`
|
||||
* `{@link angular.service.$window $window}`
|
||||
* `{@link angular.service.$document $document}`
|
||||
* `{@link angular.service.$location $location}`
|
||||
* `{@link angular.service.$log $log}`
|
||||
* `{@link angular.service.$exceptionHandler $exceptionHandler}`
|
||||
* `{@link angular.service.$hover $hover}`
|
||||
* `{@link angular.service.$invalidWidgets $invalidWidgets}`
|
||||
* `{@link angular.service.$route $route}`
|
||||
* `{@link angular.service.$xhr $xhr}`
|
||||
* `{@link angular.service.$xhr.error $xhr.error}`
|
||||
* `{@link angular.service.$xhr.bulk $xhr.bulk}`
|
||||
* `{@link angular.service.$xhr.cache $xhr.cache}`
|
||||
* `{@link angular.service.$resource $resource}`
|
||||
* `{@link angular.service.$cookies $cookies}`
|
||||
* `{@link angular.service.$cookieStore $cookieStore}`
|
||||
|
||||
# Writing your own custom services
|
||||
angular provides only set of basic services, so for any nontrivial application it will be necessary
|
||||
to write one or more custom services. To do so, a factory function that creates a services needs to
|
||||
be registered with angular's dependency injector. This factory function must return an object - the
|
||||
service (it is not called with the `new` operator).
|
||||
|
||||
**angular.service** accepts three parameters:
|
||||
|
||||
- `{string} name` - Name of the service.
|
||||
- `{function()} factory` - Factory function (called just once by DI).
|
||||
- `{Object} config` - Configuration object with following properties:
|
||||
- `$inject` - {Array.<string>} - Array of service ids that this service depends on. These
|
||||
services will be passed as arguments into the factory function in the same order as specified
|
||||
in the `$inject` array. Defaults to `[]`.
|
||||
- `$eager` - {boolean} - If true, the service factory will be called and thus, the service will
|
||||
be instantiated when angular boots. If false, service will be lazily instantiated when it is
|
||||
first requested during instantiation of a dependant. Defaults to `false`.
|
||||
|
||||
The `this` of the factory function is bound to the root scope of the angular application.
|
||||
|
||||
angular enables services to participate in dependency injection (DI) by registering themselves with
|
||||
angular's DI system (injector) under a `name` (id) as well as by declaring dependencies which need
|
||||
to be provided for the factory function of the registered service. The ability to swap dependencies
|
||||
for mocks/stubs/dummies in tests allows for services to be highly testable.
|
||||
|
||||
Here is an example of very simple service. This service requires $window service (it's
|
||||
passed as a parameter to factory function) and it's just a function.
|
||||
|
||||
This service simple stores all notifications and after third one, it displays all of them by
|
||||
window alert.
|
||||
<pre>
|
||||
angular.service('notify', function(win) {
|
||||
var msgs = [];
|
||||
return function(msg) {
|
||||
msgs.push(msg);
|
||||
if (msgs.length == 3) {
|
||||
win.alert(msgs.join("\n"));
|
||||
msgs = [];
|
||||
}
|
||||
};
|
||||
}, {$inject: ['$window']});
|
||||
</pre>
|
||||
|
||||
And here is a unit test for this service. We use Jasmine spy (mock) instead of real browser's alert.
|
||||
<pre>
|
||||
var mock, notify;
|
||||
|
||||
beforeEach(function() {
|
||||
mock = {alert: jasmine.createSpy()};
|
||||
notify = angular.service('notify')(mock);
|
||||
});
|
||||
|
||||
it('should not alert first two notifications', function() {
|
||||
notify('one');
|
||||
notify('two');
|
||||
expect(mock.alert).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should alert all after third notification', function() {
|
||||
notify('one');
|
||||
notify('two');
|
||||
notify('three');
|
||||
expect(mock.alert).toHaveBeenCalledWith("one\ntwo\nthree");
|
||||
});
|
||||
|
||||
it('should clear messages after alert', function() {
|
||||
notify('one');
|
||||
notify('two');
|
||||
notify('third');
|
||||
notify('more');
|
||||
notify('two');
|
||||
notify('third');
|
||||
expect(mock.alert.callCount).toEqual(2);
|
||||
expect(mock.alert.mostRecentCall.args).toEqual(["more\ntwo\nthird"]);
|
||||
});
|
||||
</pre>
|
||||
|
||||
# Injecting services into controllers
|
||||
Using services as dependencies for controllers is very similar to using them as dependencies for
|
||||
another service.
|
||||
|
||||
JavaScript is dynamic language, so DI is not able to figure out which services to inject by
|
||||
static types (like in static typed languages). Therefore you must specify the service name
|
||||
by the `$inject` property - it's an array that contains strings with names of services to be
|
||||
injected. The name must match the id that service has been registered as with angular.
|
||||
The order of the services in the array matters, because this order will be used when calling
|
||||
the factory function with injected parameters. The names of parameters in factory function
|
||||
don't matter, but by convention they match the service ids.
|
||||
<pre>
|
||||
function myController($loc, $log) {
|
||||
this.firstMethod = function() {
|
||||
// use $location service
|
||||
$loc.setHash();
|
||||
};
|
||||
this.secondMethod = function() {
|
||||
// use $log service
|
||||
$log.info('...');
|
||||
};
|
||||
}
|
||||
// which services to inject ?
|
||||
myController.$inject = ['$location', '$log'];
|
||||
</pre>
|
||||
|
||||
@example
|
||||
<script type="text/javascript">
|
||||
angular.service('notify', function(win) {
|
||||
var msgs = [];
|
||||
return function(msg) {
|
||||
msgs.push(msg);
|
||||
if (msgs.length == 3) {
|
||||
win.alert(msgs.join("\n"));
|
||||
msgs = [];
|
||||
}
|
||||
};
|
||||
}, {$inject: ['$window']});
|
||||
|
||||
function myController(notifyService) {
|
||||
this.callNotify = function(msg) {
|
||||
notifyService(msg);
|
||||
};
|
||||
}
|
||||
|
||||
myController.$inject = ['notify'];
|
||||
</script>
|
||||
|
||||
<div ng:controller="myController">
|
||||
<p>Let's try this simple notify service, injected into the controller...</p>
|
||||
<input ng:init="message='test'" type="text" name="message" />
|
||||
<button ng:click="callNotify(message);">NOTIFY</button>
|
||||
</div>
|
||||
@@ -0,0 +1,73 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular.validator
|
||||
@namespace Namespace for all filters.
|
||||
@description
|
||||
# Overview
|
||||
Validators are a standard way to check the user input against a specific criteria. For
|
||||
example, you might need to check that an input field contains a well-formed phone number.
|
||||
|
||||
# Syntax
|
||||
Attach a validator on user input widgets using the `ng:validate` attribute.
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Change me: <input type="text" name="number" ng:validate="integer" value="123">
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should validate the default number string', function() {
|
||||
expect(element('input[name=number]').attr('class')).
|
||||
not().toMatch(/ng-validation-error/);
|
||||
});
|
||||
it('should not validate "foo"', function() {
|
||||
input('number').enter('foo');
|
||||
expect(element('input[name=number]').attr('class')).
|
||||
toMatch(/ng-validation-error/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
|
||||
# Writing your own Validators
|
||||
Writing your own validator is easy. To make a function available as a
|
||||
validator, just define the JavaScript function on the `angular.validator`
|
||||
object. <angular/> passes in the input to validate as the first argument
|
||||
to your function. Any additional validator arguments are passed in as
|
||||
additional arguments to your function.
|
||||
|
||||
You can use these variables in the function:
|
||||
|
||||
* `this` — The current scope.
|
||||
* `this.$element` — The DOM element containing the binding. This allows the filter to manipulate
|
||||
the DOM in addition to transforming the input.
|
||||
|
||||
In this example we have written a upsTrackingNo validator.
|
||||
It marks the input text "valid" only when the user enters a well-formed
|
||||
UPS tracking number.
|
||||
|
||||
@css ng-validation-error
|
||||
When validation fails, this css class is applied to the binding, making its borders red by
|
||||
default.
|
||||
|
||||
@example
|
||||
<script>
|
||||
angular.validator('upsTrackingNo', function(input, format) {
|
||||
var regexp = new RegExp("^" + format.replace(/9/g, '\\d') + "$");
|
||||
return input.match(regexp)?"":"The format must match " + format;
|
||||
});
|
||||
</script>
|
||||
<input type="text" name="trackNo" size="40"
|
||||
ng:validate="upsTrackingNo:'1Z 999 999 99 9999 999 9'"
|
||||
value="1Z 123 456 78 9012 345 6"/>
|
||||
|
||||
@scenario
|
||||
it('should validate correct UPS tracking number', function() {
|
||||
expect(element('input[name=trackNo]').attr('class')).
|
||||
not().toMatch(/ng-validation-error/);
|
||||
});
|
||||
|
||||
it('should not validate in correct UPS tracking number', function() {
|
||||
input('trackNo').enter('foo');
|
||||
expect(element('input[name=trackNo]').attr('class')).
|
||||
toMatch(/ng-validation-error/);
|
||||
});
|
||||
@@ -0,0 +1,73 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular.widget
|
||||
@namespace Namespace for all widgets.
|
||||
@description
|
||||
# Overview
|
||||
Widgets allow you to create DOM elements that the browser doesn't
|
||||
already understand. You create the widget in your namespace and
|
||||
assign it behavior. You can only bind one widget per DOM element
|
||||
(unlike directives, in which you can use any number per DOM
|
||||
element). Widgets are expected to manipulate the DOM tree by
|
||||
adding new elements whereas directives are expected to only modify
|
||||
element properties.
|
||||
|
||||
Widgets come in two flavors: element and attribute.
|
||||
|
||||
# Element Widget
|
||||
Let's say we would like to create a new element type in the
|
||||
namespace `my` that can watch an expression and alert() the user
|
||||
with each new value.
|
||||
|
||||
<pre>
|
||||
<my:watch exp="name"/>
|
||||
</pre>
|
||||
|
||||
You can implement `my:watch` like this:
|
||||
<pre>
|
||||
angular.widget('my:watch', function(compileElement) {
|
||||
var compiler = this;
|
||||
var exp = compileElement.attr('exp');
|
||||
return function(linkElement) {
|
||||
var currentScope = this;
|
||||
currentScope.$watch(exp, function(value){
|
||||
alert(value);
|
||||
}};
|
||||
};
|
||||
});
|
||||
</pre>
|
||||
|
||||
# Attribute Widget
|
||||
Let's implement the same widget, but this time as an attribute
|
||||
that can be added to any existing DOM element.
|
||||
<pre>
|
||||
<div my-watch="name">text</div>
|
||||
</pre>
|
||||
You can implement `my:watch` attribute like this:
|
||||
<pre>
|
||||
angular.widget('@my:watch', function(expression, compileElement) {
|
||||
var compiler = this;
|
||||
return function(linkElement) {
|
||||
var currentScope = this;
|
||||
currentScope.$watch(expression, function(value){
|
||||
alert(value);
|
||||
});
|
||||
};
|
||||
});
|
||||
</pre>
|
||||
|
||||
@example
|
||||
<script>
|
||||
angular.widget('my:time', function(compileElement){
|
||||
compileElement.css('display', 'block');
|
||||
return function(linkElement){
|
||||
function update(){
|
||||
linkElement.text('Current time is: ' + new Date());
|
||||
setTimeout(update, 1000);
|
||||
}
|
||||
update();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
<my:time></my:time>
|
||||
|
||||
-210
@@ -1,210 +0,0 @@
|
||||
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 escapedHtmlTag(doc, name, value) {
|
||||
doc[name] = value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
|
||||
function markdownTag(doc, name, value) {
|
||||
doc[name] = markdown.toHTML(value);
|
||||
}
|
||||
|
||||
var TAG = {
|
||||
ngdoc: valueTag,
|
||||
example: escapedHtmlTag,
|
||||
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],
|
||||
name: match[4] || match[5],
|
||||
'default':match[6],
|
||||
description:match[7]};
|
||||
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();
|
||||
}));
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
NG_DOC={{{JSON}}};
|
||||
@@ -1,9 +0,0 @@
|
||||
{{#all}}
|
||||
describe('{{name}}', function(){
|
||||
beforeEach(function(){
|
||||
browser().navigateTo('index.html#{{name}}');
|
||||
});
|
||||
// {{raw.file}}:{{raw.line}}
|
||||
{{{scenario}}}
|
||||
});
|
||||
{{/all}}
|
||||
@@ -1,7 +0,0 @@
|
||||
function DocController($resource, $location){
|
||||
this.docs = $resource('documentation.json').get();
|
||||
this.getPartialDoc = function(){
|
||||
return encodeURIComponent($location.hashPath) + '.html';
|
||||
};
|
||||
}
|
||||
DocController.$inject=['$resource', '$location'];
|
||||
@@ -1,33 +0,0 @@
|
||||
<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>
|
||||
@@ -0,0 +1,97 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Bootstrap
|
||||
@description
|
||||
|
||||
# Bootstrap
|
||||
This section explains how to bootstrap your application to the angular environment using either
|
||||
the `angular.js` or `angular.min.js` script.
|
||||
|
||||
## The bootstrap code
|
||||
|
||||
Note that there are two versions of the bootstrap code that you can use:
|
||||
|
||||
* `angular-0.0.0.js` - this file is unobfuscated, uncompressed, and thus human-readable.
|
||||
* `angular-0.0.0.min.js` - this is a compressed and obfuscated version of angular-debug.js.
|
||||
|
||||
In this section and throughout the Developer Guide, feel free to use `angular.min.js` instead of
|
||||
`angular.js` when working through code examples.
|
||||
|
||||
## ng:autobind
|
||||
|
||||
The simplest way to get an angular application up and running is by inserting a script tag in your
|
||||
HTML file that bootstraps the `angular.js` code and uses the special `ng:autobind` attribute,
|
||||
like in this snippet of HTML:
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Hello {{'World'}}!
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
The `ng:autobind` attribute tells angular to compile and manage the whole HTML document. The
|
||||
compilation occurs in the page's onLoad handler. Note that you don't need to explicitly add an
|
||||
onLoad event; auto bind mode takes care of all the magic for you.
|
||||
|
||||
## Manual bind
|
||||
|
||||
Using autobind mode is a handy way to start using angular, but advanced users who want more
|
||||
control over the initialization process might prefer to use manual bind mode instead.
|
||||
|
||||
The best way to get started with manual bind mode is to look at the magic behind `ng:autobind`
|
||||
by writing out each step of the autobind process explicitly. Note that the following code is
|
||||
equivalent to the code in the previous section.
|
||||
|
||||
<pre>
|
||||
<!DOCTYPE HTML>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<script type="text/javascript" src="http://code.angularjs.org/angular-0.0.0.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
(function(window, previousOnLoad){
|
||||
window.onload = function(){
|
||||
try { (previousOnLoad||angular.noop)(); } catch(e) {}
|
||||
angular.compile(window.document).$init();
|
||||
};
|
||||
})(window, window.onload);
|
||||
</script>
|
||||
<body>
|
||||
Hello {{'World'}}!
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
This is the sequence that your code should follow if you're writing your own manual binding code:
|
||||
|
||||
* After the page is loaded, find the root of the HTML template, which is typically the root of
|
||||
the document.
|
||||
* Run the HTML compiler, which converts the templates into an executable, bi-directionally
|
||||
bound application.
|
||||
|
||||
|
||||
# XML Namespace
|
||||
|
||||
**IMPORTANT:** When using angular you must declare the `ng` namespace using the `xmlns` tag.
|
||||
If you don't declare the namespace, Internet Explorer does not render widgets properly.
|
||||
|
||||
<pre>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
</pre>
|
||||
|
||||
|
||||
# Create your own namespace
|
||||
|
||||
If you want to define your own widgets, you must create your own namespace and use that namespace
|
||||
to form the fully qualified widget name. For example, you could map the alias my to your domain
|
||||
and create a widget called my:widget. To create your own namespace, simply add another xmlsn tag
|
||||
to your page, create an alias, and set it to your unique domain:
|
||||
|
||||
<pre>
|
||||
<html xmlns:my="http://mydomain.com">
|
||||
</pre>
|
||||
|
||||
|
||||
# Global Object
|
||||
|
||||
The angular script creates a single global variable `angular` in the global namespace. All APIs are
|
||||
bound to fields of this global object.
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Compiler
|
||||
@description
|
||||
|
||||
#Compiler
|
||||
|
||||
While angular might look like just a cool way to build web applications, the core of angular is
|
||||
actually an HTML compiler. The default HTML transformations that this compiler provides are useful
|
||||
for building generic apps, but you can also use them to create a domain-specific language for
|
||||
building specific types of web applications.
|
||||
|
||||
The compiler allows you to add behavior to existing HTML through widgets, directives, and text
|
||||
markup.
|
||||
|
||||
All of this compilation happens in the web browser, meaning no server is involved.
|
||||
|
||||
# The compilation process
|
||||
This section describes the steps that angular's HTML compiler goes through. If you use
|
||||
`ng:autobind` in your application, this compilation process happens automatically when the
|
||||
application is initialized (e.g. when the user loads the app in a browser). If you're an advanced
|
||||
user using manual bind mode, you can decide when and how often the compilation happens.
|
||||
|
||||
First, a bit of background of what the compilation step is for. Every type of
|
||||
{@link angular.widget widget}, {@link angular.markup markup}, and
|
||||
{@link angular.directive directive} in angular is defined with a compile function, and that
|
||||
compile function returns an optional link function. Here is the relationship between the two:
|
||||
|
||||
* **compile function** - registers a listener for the widget, markup, or directive's expression.
|
||||
This function is called exactly once.
|
||||
* **link function** - sets up the listener. This function can be called multiple times, once per
|
||||
cloned DOM element (e.g. repeating element).
|
||||
|
||||
Note that angular's built-in widgets, markup, and directives have predefined compile and link
|
||||
functions that you don't need to modify. However, if you're writing your own widgets, markup, or
|
||||
directives, you write compile and link functions. Refer to the Compiler API for more information.
|
||||
|
||||
When the HTML compiler compiles a page, it goes through 3 phases: Compile, Create Root Scope, and
|
||||
Link.
|
||||
|
||||
## 1. Compile Phase
|
||||
|
||||
* Recursively traverse the DOM, depth-first.
|
||||
* Look for a matching compile function of type widget, then markup, then directive.
|
||||
* If a compile function is found then execute it.
|
||||
* When the compile function completes, it should return a link function. Aggregate this link
|
||||
function with all link functions returned previously by step 1c.
|
||||
* Repeat steps 1c and 1d for all compile functions found. The result of the compilation step is
|
||||
the aggregate link function, which comprises all of the individual link functions.
|
||||
|
||||
## 2. Create Root Scope
|
||||
|
||||
* Inject all of the services into the root scope.
|
||||
|
||||
## 3. Link Phase
|
||||
|
||||
* Execute the aggregate link function with the root scope. The aggregate link function calls all
|
||||
the individual link functions that were generated in the compile phase.
|
||||
* If there are any clones of the DOM caused by repeating elements, call the link function multiple
|
||||
times, one for each repeating item.
|
||||
|
||||
Note that while the compile function is executed exactly once, the link function can be executed
|
||||
multiple times: once for each iteration in a repeater.
|
||||
|
||||
# Example
|
||||
|
||||
The compilation process is best understood through example. Let's say that in your namespace my,
|
||||
you want to create a new DOM element <my:greeter/>, which should display a greeting.
|
||||
|
||||
If we want this HTML source:
|
||||
|
||||
<pre>
|
||||
<div ng:init="salutation='Hello'; name='World'">
|
||||
<my:greeter salutation="salutation" name="name"/>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
To produce this DOM:
|
||||
|
||||
<pre>
|
||||
<div ng:init="salutation='Hello'; name='World'">
|
||||
<my:greeter salutation="salutation" name="name"/>
|
||||
<span class="salutation">Hello</span>
|
||||
<span class="name">World</span>!
|
||||
</my:greeter>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
Write this widget definition (assuming you've already declared the my namespace in the page):
|
||||
|
||||
|
||||
<pre>
|
||||
angular.widget('my:greeter', function(compileElement){
|
||||
var compiler = this;
|
||||
compileElement.css('display', 'block');
|
||||
var salutationExp = compileElement.attr('salutation');
|
||||
var nameExp = compileElement.attr('name');
|
||||
return function(linkElement){
|
||||
var salutationSpan = angular.element('<span class="salutation"></span');
|
||||
var nameSpan = angular.element('<span class="name"></span>');
|
||||
linkElement.append(salutationSpan);
|
||||
linkElement.append(compiler.text(' '));
|
||||
linkElement.append(nameSpan);
|
||||
linkElement.append(compiler.text('!'));
|
||||
this.$watch(salutationExp, function(value){
|
||||
salutationSpan.text(value);
|
||||
});
|
||||
this.$watch(nameExp, function(value){
|
||||
nameSpan.text(value);
|
||||
});
|
||||
};
|
||||
});
|
||||
</pre>
|
||||
|
||||
Note: For more about widgets, see {@link angular.widget Widget}.
|
||||
|
||||
## Compilation process for this example
|
||||
|
||||
Here are the steps that the compiler goes through for the page that contains this widget definition:
|
||||
|
||||
### Compile Phase
|
||||
|
||||
* Recursively traverse the DOM depth-first.
|
||||
* Find the angular.widget definition.
|
||||
* Find and execute the widget's compileElement function, which includes the following steps:
|
||||
* Add a style element with attribute display: block; to the template DOM so that the browser
|
||||
knows to treat the element as block element for rendering. (Note: because this style element
|
||||
was added on the template compileElement, this style is automatically applied to any clones
|
||||
of the template (i.e. any repeating elements)).
|
||||
* Extract the salutation and name HTML attributes as angular expressions.
|
||||
* Return the aggregate link function, which includes just one link function in this example.
|
||||
|
||||
### Link Phase
|
||||
|
||||
* Execute the aggregate link function, which includes the following steps:
|
||||
* Create a <span> element set to the salutation class
|
||||
* Create a <span> element set to the name class.
|
||||
* Add the span elements to the linkElement. (Note: be careful not to add them to the
|
||||
compileElement, because that's the template.)
|
||||
* Set up watches on the expressions. When an expression changes, copy the data to the
|
||||
corresponding spans.
|
||||
|
||||
|
||||
## Compiler API
|
||||
|
||||
If you define your own widgets, markup, or directives, you need to access the compiler API.
|
||||
This section describes the methods on the compiler that you can call.
|
||||
|
||||
Note: As of 12 August 2010, these methods are subject to change.
|
||||
|
||||
Recall that the compile function's this is a reference to the compiler.
|
||||
|
||||
* `compile(element)` - returns `linker` - Invoke new instance of compiler to compile a DOM element
|
||||
and return a linker function. You can apply the linker function to the original element or a
|
||||
clone of the original element. The linker function returns a scope.
|
||||
* `comment(commentText)` - returns `element` - Create a comment element.
|
||||
* `element(elementName)` - returns `element` - Create an element by name.
|
||||
* `text(text)` - returns `element` - Create a text element.
|
||||
* `descend([set])` - returns `descend` - State Get or set the current descend state. If true the
|
||||
compiler will descend to children elements.
|
||||
* `directives([set])` - returns `directive` - State Get or set the current directives processing
|
||||
state. The compiler will process directives only when directives set to true.
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Data Binding
|
||||
@description
|
||||
|
||||
# Data Binding
|
||||
|
||||
Data-binding allows you to treat the model as the single-source-of-truth of your application, and
|
||||
consider the view as only a projection of the model, at all times. The process of copying the model
|
||||
values to the view, and any changes to the view by the user to the model, is known as data-binding.
|
||||
|
||||
## Classical Template Systems
|
||||
|
||||
<img class="right" src="img/One_Way_Data_Binding.png"/>
|
||||
At the highest level, angular looks like a just another templating system. But there is one
|
||||
important reason why angular templating system is different and makes it very good fit for
|
||||
application development: two-way data binding.
|
||||
|
||||
Most templating systems bind data in only one direction: they merge a template and model together
|
||||
into a view, as illustrated in the diagram to the right. After the merge occurs, any changes to
|
||||
the model or in related sections of the view are NOT automatically reflected in the view. Worse,
|
||||
any changes that the user makes to the view are not reflected in the model. This means that the
|
||||
developer has to write code that constantly syncs the view with the model and the model with the
|
||||
view.
|
||||
|
||||
|
||||
# angular Template Systems
|
||||
<img class="right" src="img/Two_Way_Data_Binding.png"/>
|
||||
The way angular templates works is different, as illustrated in the diagram on the right. They are
|
||||
different because first the template (which is the uncompiled HTML along with any additional markup
|
||||
or directives) is compiled on the browser, and second, the compilation step produces a live view.
|
||||
We say live because any changes to the view are immediately reflected in the model, and any changes
|
||||
in the model are propagated to the view. This makes the model always the single-source-of-truth for
|
||||
the application state, greatly simplifying the programing model for the developer. You can think of
|
||||
the view as simply an instant projection of your model.
|
||||
|
||||
Because the view is just a projection of the model, the controller is completely separated from the
|
||||
view and unaware of it. This makes testing a snap because it is easy to test your controller in
|
||||
isolation without the view and the related DOM/browser dependency.
|
||||
|
||||
For details about how data binding works in angular, see {@link angular.scope Scope}.
|
||||
@@ -0,0 +1,36 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide
|
||||
@description
|
||||
|
||||
* {@link guide.overview Overview} - An overview of angular, including its philosophy and how it
|
||||
works.
|
||||
* {@link guide.bootstrap Bootstrap} - How to bootstrap your application to the angular environment.
|
||||
* {@link guide.template Template} - How to define your application's view using HTML, CSS, and
|
||||
other built-in angular constructs.
|
||||
* {@link guide.compiler Compiler} - All about the HTML compiler that's at the core of angular.
|
||||
* {@link angular.directive Directive} - How to use XML attributes to augment an existing DOM
|
||||
element.
|
||||
* {@link angular.markup Markup} - How to use markup to create shorthand for a widget or a
|
||||
directive. For example, markup is what allows you to use the double curly brace notation
|
||||
`{{}}` to bind expressions to elements.
|
||||
* {@link guide.data-binding Data Binding} - About the mechanism that keeps the model the single
|
||||
source of truth of your application at all times, with the view as a live projection of the
|
||||
model.
|
||||
* {@link angular.filter Filter} - How to format your data for display to the user.
|
||||
* {@link angular.widget Widget} - How to create new DOM elements that the browser doesn't already
|
||||
understand.
|
||||
* {@link angular.validator Validator} - How to validate user input.
|
||||
* {@link angular.formatter Formatter} - How to format stored data to user-readable text and
|
||||
parse the text back to the stored form.
|
||||
* {@link guide.css CSS} - Built-in CSS classes, when angular assigns them, and how to override
|
||||
their styles.
|
||||
* {@link angular.scope Scope} - The model in the model-view-controller design pattern. You can
|
||||
think about scopes as the JavaScript objects that have extra APIs for registering watchers.
|
||||
* {@link guide.expression Expression} - The bindings that are embedded in an angular View.
|
||||
* {@link angular.service Service} - Objects that are wired through dependency injection and then
|
||||
injected into the root scope.
|
||||
* {@link guide.testing Testing}
|
||||
* service:$browser(mock)
|
||||
* {@link guide.downloading Downloading} - How to download, compile, and host the angular
|
||||
environment on your own server.
|
||||
@@ -0,0 +1,166 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Overview
|
||||
@description
|
||||
|
||||
# What is angular?
|
||||
Angular teaches your old browser new tricks. It is what HTML would have been if it had been
|
||||
designed for building web applications.
|
||||
|
||||
Take a simple example of user input as shown below. If you were using just HTML and JavaScript to
|
||||
implement this form, you would need to define listeners, DOM updates, and complex input validators
|
||||
in order to update and format the result. In angular you can achieve the effect with zero lines
|
||||
of JavaScript code using a declarative approach. Click on the source tab of the example below to
|
||||
view the angular implementation of this form.
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
QTY: <input name="qty" value="1" ng:validate="integer:0" ng:required/>
|
||||
*
|
||||
Cost: <input name="cost" value="19.95" ng:validate="number" ng:required/>
|
||||
=
|
||||
{{qty * cost | currency}}
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should show of angular binding', function(){
|
||||
expect(binding('qty * cost')).toEqual('$19.95');
|
||||
input('qty').enter('2');
|
||||
input('cost').enter('5.00');
|
||||
expect(binding('qty * cost')).toEqual('$10.00');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
Angular is to AJAX apps as Ruby on Rails is to round trip apps.
|
||||
|
||||
# Angular frees you from:
|
||||
* **Registering callbacks:** Registering callbacks clutters your code, and it makes it hard to see
|
||||
the forest from the trees. Removing common boilerplate code such as callbacks is advantageous
|
||||
because it leaves the JavaScript with a more succinct version of your code, better describing
|
||||
what your application does.
|
||||
* **Manipulating HTML DOM programatically:** Manipulating HTML DOM is a cornerstone of AJAX
|
||||
applications, but it is very cumbersome and error-prone. By declaratively describing how the UI
|
||||
should change as your application state changes, you are freed from low level DOM manipulation
|
||||
activities. Most applications written with angular never have to programatically manipulate
|
||||
the DOM.
|
||||
* **Marshaling data to and from the UI:** CRUD operations make up the majority of most AJAX
|
||||
applications. The flow of marshaling data from the server to an internal object to a HTML form,
|
||||
validating the form, displaying validation errors, returning to an internal model and then back
|
||||
to the server creates a lot of boilerplate code. angular eliminates almost all of this
|
||||
boilerplate. leaving code that is richer and describes the overall flow of the application
|
||||
rather than implementation details.
|
||||
* **Writing tons of initialization code just to get started:** Typically you need to write a lot
|
||||
of plumbing and initialization code just to get a basic "Hello World" AJAX app working. With
|
||||
angular you can bootstrap your app easily using services, which are auto-injected into your
|
||||
application in a GUICE-like dependency-injection style. This allows you to get started
|
||||
developing features quickly. As a bonus, you get full control over the initialization process
|
||||
in automated tests.
|
||||
|
||||
|
||||
# angular is/has:
|
||||
* **An HTML Compiler:** angular is an HTML compiler in the browser. It allows you to give meaning
|
||||
to any HTML element, attribute, or text and create new primitives as building blocks for your
|
||||
application.
|
||||
* **Declarative:** Declarative means that you describe what the page looks like rather than
|
||||
instructing how to draw the page. HTML is great at declaring static documents. Angular extends
|
||||
the declarative nature of HTML beyond static documents to define dynamic applications.
|
||||
* **Declarative Templates:** In angular, you write HTML to declare the view and UI templates for
|
||||
your application. You can express many common application constructs without using any
|
||||
JavaScript at all.
|
||||
* **Bidirectional Data Binding:** Allows your application to have a single source of truth
|
||||
(your model), where the HTML is a projection of the internal state of your application, which
|
||||
you can provide to the user in a declarative way.
|
||||
* **Built-in Services:** angular provides many standard AJAX operations to get you going quickly,
|
||||
and dependency-injection allows you to swap them out as needed. Common services include:
|
||||
Dependency Inject, History Management, URL Router, AJAX/XHR requests, and data caching,
|
||||
to name a few.
|
||||
* **Very testable:** Testing difficulty is dramatically affected by the way you structure your
|
||||
code. With angular, testability is baked in.
|
||||
|
||||
# angular is NOT a:
|
||||
* **Library:** You don't call its functions.
|
||||
* **Framework:** It does not call your functions.
|
||||
* **DOM Manipulation Library:** It does not provide a way to manipulate DOM, but does provide
|
||||
primitives to create UI projections of your data.
|
||||
* **Widget Library:** There are lots of existing widget libraries that you can integrate with
|
||||
angular.
|
||||
|
||||
|
||||
|
||||
# Not just another templating system
|
||||
|
||||
At the highest level, angular looks like a just another templating system, but there are few
|
||||
important reasons why angular is different and makes it a very good fit for application
|
||||
development.
|
||||
|
||||
Angular:
|
||||
* **Uses HTML/CSS syntax:** This makes it easy to read and can be edited with existing HTML/CSS
|
||||
authoring tools.
|
||||
* **Extends HTML vocabulary:** Angular allows you to create new HTML tags, which expand into
|
||||
dynamic UI components.
|
||||
* **Executes in the browser:** Removes the round trip to the server for many operations and
|
||||
creates instant feedback for users.
|
||||
* **Bidirectional data binding:** The model is the single source of truth. Programmatic changes
|
||||
to the model are automatically reflected in the view. Any changes by the user to the view are
|
||||
automatically reflected in the model.
|
||||
* **Services:** These allow for a reusable way of injecting dependencies into an application.
|
||||
* **MVC:** Clean separation between model-view-controller, which aids in understanding,
|
||||
maintenance, and testing of large systems.
|
||||
|
||||
|
||||
# The angular philosophy
|
||||
|
||||
Angular is built around the belief that declarative code is preferred over imperative when it
|
||||
comes to building UIs and connecting the pieces together.
|
||||
|
||||
As an example, if you wanted to add a new label to your application, you could do so by simply
|
||||
adding text to the HTML template, saving the code, and refreshing your browser:
|
||||
|
||||
<pre>
|
||||
<span class="label">Hello</span>
|
||||
</pre>
|
||||
|
||||
In programmatic systems you would have to write and run code like this:
|
||||
|
||||
<pre>
|
||||
var label = new Label();
|
||||
label.setText('Hello');
|
||||
label.setClass('label');
|
||||
parent.addChild(label);
|
||||
</pre>
|
||||
|
||||
## Benefits:
|
||||
|
||||
* Compile-free: Change your template and logic code and reload the browser to see it run
|
||||
immediately. In contrast, programmatic serverside views often need to be compiled and executed
|
||||
to be viewed, which takes time. This dramatically increases the speed of your development cycle.
|
||||
* Declarative templates are easier to understand and change than programmatic instructions.
|
||||
* Declarative templates can be edited in existing HTML editors such as DreamWeaver, Eclipse,
|
||||
TextMate, Vim, etc.
|
||||
* Declarative templates can be edited by web designers without the need to work with web
|
||||
developers.
|
||||
|
||||
HTML is missing certain features, which angular adds via its compiler, thereby "teaching" the
|
||||
browser these new tricks:
|
||||
|
||||
* Dynamic behavior
|
||||
* Componentizing HTML snippets into reusable components
|
||||
* Dynamically include other HTML templates
|
||||
* Two-way data binding
|
||||
* Rich validation in forms
|
||||
* Model-View-Controller modularization
|
||||
|
||||
# Watch a presentation about angular
|
||||
|
||||
<object width="480" height="385">
|
||||
<param name="movie" value="http://www.youtube.com/v/elvcgVSynRg&hl=en_US&fs=1"></param>
|
||||
<param name="allowFullScreen" value="true"></param>
|
||||
<param name="allowscriptaccess" value="always"></param>
|
||||
<embed src="http://www.youtube.com/v/elvcgVSynRg&hl=en_US&fs=1"
|
||||
type="application/x-shockwave-flash" allowscriptaccess="always"
|
||||
allowfullscreen="true" width="480" height="385"></embed>
|
||||
</object>
|
||||
|
||||
{@link https://docs.google.com/present/edit?id=0Abz6S2TvsDWSZDQ0OWdjaF8yNTRnODczazdmZg&hl=en&authkey=CO-b7oID Presentation}
|
||||
|
|
||||
{@link https://docs.google.com/document/edit?id=1ZHVhqC0apbzPRQcgnb1Ye-bAUbNJ-IlFMyPBPCZ2cYU&hl=en&authkey=CInnwLYO Source}
|
||||
@@ -0,0 +1,22 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Template
|
||||
@description
|
||||
#Template
|
||||
|
||||
You can think of a template in angular as a domain-specific language that you can use to easily
|
||||
build the view of your web application. You create a template by writing HTML and CSS, and you can
|
||||
add any constructs that you want to the HTML. This means that you can attach rendering and behavior
|
||||
to any HTML element, attribute or markup text.
|
||||
|
||||
In addition to writing HTML and CSS, you can also use the following angular constructs to create
|
||||
your template:
|
||||
|
||||
* **Directive** - XML attributes that augment an existing DOM element.
|
||||
* **Markup** - Lets you create shorthand for a widget or a directive. For example, markup is what
|
||||
allows you to use the double curly brace notation {{}} to bind expressions to
|
||||
elements.
|
||||
* **Filter** - Lets you format your data for display to the user.
|
||||
* **Widget** - Lets you create new DOM elements that the browser doesn't already understand.
|
||||
* **Validator** - Lets you validate user input.
|
||||
* **Formatter** - Lets you format the input object into a user readable view.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
@@ -1,25 +0,0 @@
|
||||
<!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>
|
||||
@@ -1 +0,0 @@
|
||||
{{{description}}}
|
||||
@@ -0,0 +1,57 @@
|
||||
<h1><tt>{{name}}</tt></h1>
|
||||
|
||||
{{#workInProgress}}
|
||||
<fieldset class="workInProgress">
|
||||
<legend>Work In Progress</legend>
|
||||
This page is currently being revised. It might be incomplete or contain inaccuracies.
|
||||
{{{workInProgress.description}}}
|
||||
</fieldset>
|
||||
{{/workInProgress}}
|
||||
|
||||
{{#deprecated}}
|
||||
<fieldset class="deprecated">
|
||||
<legend>Deprecated API</legend>
|
||||
{{deprecated}}
|
||||
</fieldset>
|
||||
{{/deprecated}}
|
||||
|
||||
<h2>Description</h2>
|
||||
{{{description}}}
|
||||
|
||||
<h2>Dependencies</h2>
|
||||
<ul>
|
||||
{{#requires}}
|
||||
<li><tt>{{name}}</tt></li>
|
||||
{{/requires}}
|
||||
</ul>
|
||||
|
||||
{{#method.length}}
|
||||
<h2>Methods</h2>
|
||||
<ul>
|
||||
{{#method}}
|
||||
<li><tt>{{shortName}}()</tt>: {{{description}}}</li>
|
||||
{{/method}}
|
||||
</ul>
|
||||
{{/method.length}}
|
||||
|
||||
{{#property.length}}
|
||||
<h2>Properties</h2>
|
||||
<ul>
|
||||
{{#property}}
|
||||
<li><tt>{{name}}:{{#type}}{{type}}{{/type}}</tt>{{#description}}: {{{description}}}{{/description}}</li>
|
||||
{{/property}}
|
||||
</ul>
|
||||
{{/property.length}}
|
||||
|
||||
{{#example}}
|
||||
<h2>Example</h2>
|
||||
{{{exampleDescription}}}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
{{/example}}
|
||||
{{{example}}}
|
||||
{{#example}}
|
||||
</doc:source>
|
||||
{{#scenario}}<doc:scenario>{{{scenario}}}</doc:scenario>{{/scenario}}
|
||||
</doc:example>
|
||||
{{/example}}
|
||||
@@ -0,0 +1,61 @@
|
||||
var DOM = require('dom.js').DOM;
|
||||
|
||||
describe('dom', function(){
|
||||
var dom;
|
||||
|
||||
beforeEach(function(){
|
||||
dom = new DOM();
|
||||
});
|
||||
|
||||
describe('example', function(){
|
||||
it('should render code, live, test', function(){
|
||||
dom.example('desc', 'src', 'scenario');
|
||||
expect(dom.toString()).toEqual(
|
||||
'<h1>Example</h1>\n' +
|
||||
'<div class="example">' +
|
||||
'desc<doc:example><doc:source>src</doc:source>\n' +
|
||||
'<doc:scenario>scenario</doc:scenario>\n'+
|
||||
'</doc:example>\n' +
|
||||
'</div>\n');
|
||||
});
|
||||
|
||||
it('should render non-live, test with description', function(){
|
||||
dom.example('desc', 'src', false);
|
||||
expect(dom.toString()).toEqual('<h1>Example</h1>\n' +
|
||||
'<div class="example">' +
|
||||
'desc<div ng:non-bindable="">' +
|
||||
'<pre class="brush: js; html-script: true;">src</pre>\n' +
|
||||
'</div>\n' +
|
||||
'</div>\n');
|
||||
});
|
||||
|
||||
it('should render non-live, test', function(){
|
||||
dom.example('desc', 'src', false);
|
||||
expect(dom.toString()).toContain('<pre class="brush: js; html-script: true;">src</pre>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('h', function(){
|
||||
|
||||
it('should render using function', function(){
|
||||
var cbThis;
|
||||
var cdValue;
|
||||
dom.h('heading', 'content', function(value){
|
||||
cbThis = this;
|
||||
cbValue = value;
|
||||
});
|
||||
expect(cbThis).toEqual(dom);
|
||||
expect(cbValue).toEqual('content');
|
||||
});
|
||||
|
||||
it('should update heading numbers', function(){
|
||||
dom.h('heading', function(){
|
||||
this.html('<h1>sub-heading</h1>');
|
||||
});
|
||||
expect(dom.toString()).toContain('<h1>heading</h1>');
|
||||
expect(dom.toString()).toContain('<h2>sub-heading</h2>');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,454 @@
|
||||
var ngdoc = require('ngdoc.js');
|
||||
var DOM = require('dom.js').DOM;
|
||||
|
||||
describe('ngdoc', function(){
|
||||
var Doc = ngdoc.Doc;
|
||||
var dom;
|
||||
|
||||
beforeEach(function(){
|
||||
dom = new DOM();
|
||||
this.addMatchers({
|
||||
toContain: function(text) {
|
||||
this.actual = this.actual.toString();
|
||||
return this.actual.indexOf(text) > -1;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Doc', function(){
|
||||
describe('metadata', function(){
|
||||
|
||||
it('should find keywords', function(){
|
||||
expect(new Doc('\nHello: World! @ignore. $abc').keywords()).toEqual('$abc hello world');
|
||||
expect(new Doc('The `ng:class-odd` and').keywords()).toEqual('and ng:class-odd the');
|
||||
});
|
||||
|
||||
it('should have shortName', function(){
|
||||
var d1 = new Doc('@name a.b.c').parse();
|
||||
var d2 = new Doc('@name a.b.ng:c').parse();
|
||||
var d3 = new Doc('@name some text: more text').parse();
|
||||
expect(ngdoc.metadata([d1])[0].shortName).toEqual('c');
|
||||
expect(ngdoc.metadata([d2])[0].shortName).toEqual('ng:c');
|
||||
expect(ngdoc.metadata([d3])[0].shortName).toEqual('more text');
|
||||
});
|
||||
|
||||
it('should have depth information', function(){
|
||||
var d1 = new Doc('@name a.b.c').parse();
|
||||
var d2 = new Doc('@name a.b.ng:c').parse();
|
||||
var d3 = new Doc('@name some text: more text').parse();
|
||||
expect(ngdoc.metadata([d1])[0].depth).toEqual(2);
|
||||
expect(ngdoc.metadata([d2])[0].depth).toEqual(2);
|
||||
expect(ngdoc.metadata([d3])[0].depth).toEqual(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('parse', function(){
|
||||
it('should convert @names into properties', function(){
|
||||
var doc = new Doc('\n@name name\n@desc\ndesc\ndesc2\n@dep\n');
|
||||
doc.parse();
|
||||
expect(doc.name).toEqual('name');
|
||||
expect(doc.desc).toEqual('desc\ndesc2');
|
||||
expect(doc.dep).toEqual('');
|
||||
});
|
||||
|
||||
it('should parse parameters', function(){
|
||||
var doc = new Doc(
|
||||
'@param {*} a short\n' +
|
||||
'@param {Type} b med\n' +
|
||||
'@param {Class=} [c=2] long\nline');
|
||||
doc.parse();
|
||||
expect(doc.param).toEqual([
|
||||
{name:'a', description:'<p>short</p>', type:'*', optional:false, 'default':undefined},
|
||||
{name:'b', description:'<p>med</p>', type:'Type', optional:false, 'default':undefined},
|
||||
{name:'c', description:'<p>long\nline</p>', type:'Class', optional:true, 'default':'2'}
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse return', function(){
|
||||
var doc = new Doc('@returns {Type} text *bold*.');
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({
|
||||
type: 'Type',
|
||||
description: '<p>text <em>bold</em>.</p>'
|
||||
});
|
||||
});
|
||||
|
||||
it('should not remove extra line breaks', function(){
|
||||
var doc = new Doc('@example\nA\n\nB');
|
||||
doc.parse();
|
||||
expect(doc.example).toEqual('A\n\nB');
|
||||
});
|
||||
|
||||
it('should parse filename', function(){
|
||||
var doc = new Doc('@name friendly name', 'docs/a.b.ngdoc', 1);
|
||||
doc.parse(0);
|
||||
expect(doc.id).toEqual('a.b');
|
||||
expect(doc.name).toEqual('friendly name');
|
||||
});
|
||||
|
||||
it('should escape <doc:source> element', function(){
|
||||
var doc = new Doc('@description before <doc:example>' +
|
||||
'<doc:source>\n<>\n</doc:source></doc:example> after');
|
||||
doc.parse();
|
||||
expect(doc.description).toContain('<p>before </p><doc:example>' +
|
||||
'<doc:source>\n<>\n</doc:source></doc:example><p>after</p>');
|
||||
});
|
||||
|
||||
it('should escape <doc:scenario> element', function(){
|
||||
var doc = new Doc('@description before <doc:example>' +
|
||||
'<doc:scenario>\n<>\n</doc:scenario></doc:example> after');
|
||||
doc.parse();
|
||||
expect(doc.description).toContain('<p>before </p><doc:example>' +
|
||||
'<doc:scenario>\n<>\n</doc:scenario></doc:example><p>after</p>');
|
||||
});
|
||||
|
||||
describe('sorting', function(){
|
||||
function property(name) {
|
||||
return function(obj) {return obj[name];};
|
||||
}
|
||||
function noop(){}
|
||||
function doc(type, name){
|
||||
return {
|
||||
id: name,
|
||||
ngdoc: type,
|
||||
keywords: noop
|
||||
};
|
||||
}
|
||||
|
||||
var angular_widget = doc('overview', 'angular.widget');
|
||||
var angular_x = doc('function', 'angular.x');
|
||||
var angular_y = doc('property', 'angular.y');
|
||||
|
||||
it('should put angular.fn() in front of angular.widget, etc', function(){
|
||||
expect(ngdoc.metadata([angular_widget, angular_y, angular_x]).map(property('id')))
|
||||
.toEqual(['angular.x', 'angular.y', 'angular.widget' ]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('scenario', function(){
|
||||
it('should render from @example/@scenario and <doc:example>', function(){
|
||||
var doc = new Doc(
|
||||
'@id id\n' +
|
||||
'@description <doc:example><doc:scenario>scenario0</doc:scenario></doc:example>' +
|
||||
'@example exempleText\n' +
|
||||
'@scenario scenario1\n' +
|
||||
'@scenario scenario2').parse();
|
||||
expect(ngdoc.scenarios([doc])).toContain('describe("id"');
|
||||
expect(ngdoc.scenarios([doc])).toContain('navigateTo("index.html#!id")');
|
||||
expect(ngdoc.scenarios([doc])).toContain('\n scenario0\n');
|
||||
expect(ngdoc.scenarios([doc])).toContain('\n scenario1\n');
|
||||
expect(ngdoc.scenarios([doc])).toContain('\n scenario2\n');
|
||||
});
|
||||
});
|
||||
|
||||
describe('markdown', function(){
|
||||
var markdown = ngdoc.markdown;
|
||||
|
||||
it('should replace angular in markdown', function(){
|
||||
expect(markdown('<angular/>')).
|
||||
toEqual('<p><tt><angular/></tt></p>');
|
||||
});
|
||||
|
||||
it('should not replace anything in <pre>, but escape the html escape the content', function(){
|
||||
expect(markdown('bah x\n<pre>\n<b>angular</b>.k\n</pre>\n asdf x')).
|
||||
toEqual(
|
||||
'<p>bah x</p>' +
|
||||
'<div ng:non-bindable><pre class="brush: js; html-script: true;">\n' +
|
||||
'<b>angular</b>.k\n' +
|
||||
'</pre></div>' +
|
||||
'<p>asdf x</p>');
|
||||
});
|
||||
|
||||
it('should replace text between two <pre></pre> tags', function() {
|
||||
expect(markdown('<pre>x</pre># One<pre>b</pre>')).
|
||||
toMatch('</div><h1>One</h1><div');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('trim', function(){
|
||||
var trim = ngdoc.trim;
|
||||
it('should remove leading/trailing space', function(){
|
||||
expect(trim(' \nabc\n ')).toEqual('abc');
|
||||
});
|
||||
|
||||
it('should remove leading space on every line', function(){
|
||||
expect(trim('\n 1\n 2\n 3\n')).toEqual('1\n 2\n 3');
|
||||
});
|
||||
});
|
||||
|
||||
describe('merge', function(){
|
||||
it('should merge child with parent', function(){
|
||||
var parent = new Doc({name:'angular.service.abc'});
|
||||
var methodA = new Doc({name:'methodA', methodOf:'angular.service.abc'});
|
||||
var methodB = new Doc({name:'methodB', methodOf:'angular.service.abc'});
|
||||
var propA = new Doc({name:'propA', propertyOf:'angular.service.abc'});
|
||||
var propB = new Doc({name:'propB', propertyOf:'angular.service.abc'});
|
||||
;var docs = [methodB, methodA, propB, propA, parent]; // keep wrong order;
|
||||
ngdoc.merge(docs);
|
||||
expect(docs.length).toEqual(1);
|
||||
expect(docs[0].name).toEqual('angular.service.abc');
|
||||
expect(docs[0].methods).toEqual([methodA, methodB]);
|
||||
expect(docs[0].properties).toEqual([propA, propB]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
describe('TAG', function(){
|
||||
describe('@param', function(){
|
||||
it('should parse with no default', function(){
|
||||
var doc = new Doc('@param {(number|string)} number Number \n to format.');
|
||||
doc.parse();
|
||||
expect(doc.param).toEqual([{
|
||||
type : '(number|string)',
|
||||
name : 'number',
|
||||
optional: false,
|
||||
'default' : undefined,
|
||||
description : '<p>Number \n to format.</p>' }]);
|
||||
});
|
||||
|
||||
it('should parse with default and optional', function(){
|
||||
var doc = new Doc('@param {(number|string)=} [fractionSize=2] desc');
|
||||
doc.parse();
|
||||
expect(doc.param).toEqual([{
|
||||
type : '(number|string)',
|
||||
name : 'fractionSize',
|
||||
optional: true,
|
||||
'default' : '2',
|
||||
description : '<p>desc</p>' }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('@requires', function() {
|
||||
it('should parse more @requires tag into array', function() {
|
||||
var doc = new Doc('@requires $service\n@requires $another');
|
||||
doc.parse();
|
||||
expect(doc.requires).toEqual(['$service', '$another']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('@property', function() {
|
||||
it('should parse @property tags into array', function() {
|
||||
var doc = new Doc("@property {type} name1 desc\n@property {type} name2 desc");
|
||||
doc.parse();
|
||||
expect(doc.properties.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should parse @property with only name', function() {
|
||||
var doc = new Doc("@property fake");
|
||||
doc.parse();
|
||||
expect(doc.properties[0].name).toEqual('fake');
|
||||
});
|
||||
|
||||
it('should parse @property with optional type', function() {
|
||||
var doc = new Doc("@property {string} name");
|
||||
doc.parse();
|
||||
expect(doc.properties[0].name).toEqual('name');
|
||||
expect(doc.properties[0].type).toEqual('string');
|
||||
});
|
||||
|
||||
it('should parse @property with optional description', function() {
|
||||
var doc = new Doc("@property name desc rip tion");
|
||||
doc.parse();
|
||||
expect(doc.properties[0].name).toEqual('name');
|
||||
expect(doc.properties[0].description).toEqual('desc rip tion');
|
||||
});
|
||||
|
||||
it('should parse @property with type and description both', function() {
|
||||
var doc = new Doc("@property {bool} name desc rip tion");
|
||||
doc.parse();
|
||||
expect(doc.properties[0].name).toEqual('name');
|
||||
expect(doc.properties[0].type).toEqual('bool');
|
||||
expect(doc.properties[0].description).toEqual('desc rip tion');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('@returns', function() {
|
||||
it('should not parse @returns without type', function() {
|
||||
var doc = new Doc("@returns lala");
|
||||
expect(doc.parse).toThrow();
|
||||
});
|
||||
|
||||
it('should parse @returns with type and description', function() {
|
||||
var doc = new Doc("@returns {string} descrip tion");
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({type: 'string', description: '<p>descrip tion</p>'});
|
||||
});
|
||||
|
||||
it('should transform description of @returns with markdown', function() {
|
||||
var doc = new Doc("@returns {string} descrip *tion*");
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({type: 'string', description: '<p>descrip <em>tion</em></p>'});
|
||||
});
|
||||
|
||||
it('should support multiline content', function() {
|
||||
var doc = new Doc("@returns {string} description\n new line\n another line");
|
||||
doc.parse();
|
||||
expect(doc.returns).
|
||||
toEqual({type: 'string', description: '<p>description\n new line\n another line</p>'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('@description', function(){
|
||||
it('should support pre blocks', function(){
|
||||
var doc = new Doc("@description <pre>abc</pre>");
|
||||
doc.parse();
|
||||
expect(doc.description).
|
||||
toBe('<div ng:non-bindable><pre class="brush: js; html-script: true;">abc</pre></div>');
|
||||
});
|
||||
|
||||
it('should support multiple pre blocks', function() {
|
||||
var doc = new Doc("@description foo \n<pre>abc</pre>\n#bah\nfoo \n<pre>cba</pre>");
|
||||
doc.parse();
|
||||
expect(doc.description).
|
||||
toBe('<p>foo </p>' +
|
||||
'<div ng:non-bindable><pre class="brush: js; html-script: true;">abc</pre></div>' +
|
||||
'<h1>bah</h1>\n\n' +
|
||||
'<p>foo </p>' +
|
||||
'<div ng:non-bindable><pre class="brush: js; html-script: true;">cba</pre></div>');
|
||||
|
||||
});
|
||||
|
||||
it('should support nested @link annotations with or without description', function() {
|
||||
var doc = new Doc("@description " +
|
||||
'foo {@link angular.foo}\n\n da {@link angular.foo bar foo bar } \n\n' +
|
||||
'dad{@link angular.foo}\n\n' +
|
||||
'{@link angular.directive.ng:foo ng:foo}');
|
||||
doc.parse();
|
||||
expect(doc.description).
|
||||
toContain('foo <a href="#!angular.foo"><code>angular.foo</code></a>');
|
||||
expect(doc.description).
|
||||
toContain('da <a href="#!angular.foo"><code>bar foo bar</code></a>');
|
||||
expect(doc.description).
|
||||
toContain('dad<a href="#!angular.foo"><code>angular.foo</code></a>');
|
||||
expect(doc.description).
|
||||
toContain('<a href="#!angular.directive.ng:foo"><code>ng:foo</code></a>');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('@example', function(){
|
||||
it('should not remove {{}}', function(){
|
||||
var doc = new Doc('@example text {{ abc }}');
|
||||
doc.parse();
|
||||
expect(doc.example).toEqual('text {{ abc }}');
|
||||
});
|
||||
});
|
||||
|
||||
describe('@exampleDescription', function(){
|
||||
it('should render example description', function(){
|
||||
var doc = new Doc('@exampleDescription some\n text');
|
||||
doc.ngdoc = "filter";
|
||||
doc.parse();
|
||||
expect(doc.html()).toContain('<p>some\n text');
|
||||
});
|
||||
|
||||
it('should alias @exampleDescription to @exampleDesc', function(){
|
||||
var doc = new Doc('@exampleDesc some\n text');
|
||||
doc.ngdoc = "filter";
|
||||
doc.parse();
|
||||
expect(doc.html()).toContain('<p>some\n text');
|
||||
});
|
||||
|
||||
it('should render description in related method', function(){
|
||||
var doc = new Doc();
|
||||
doc.ngdoc = 'service';
|
||||
doc.methods = [new Doc('@ngdoc method\n@exampleDescription MDesc\n@example MExmp').parse()];
|
||||
doc.properties = [new Doc('@ngdoc property\n@exampleDescription PDesc\n@example PExmp').parse()];
|
||||
expect(doc.html()).toContain('<p>MDesc</p><div ng:non-bindable=""><pre class="brush: js; html-script: true;">MExmp</pre>');
|
||||
expect(doc.html()).toContain('<p>PDesc</p><div ng:non-bindable=""><pre class="brush: js; html-script: true;">PExmp</pre>');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('@depricated', function() {
|
||||
it('should parse @depricated', function() {
|
||||
var doc = new Doc('@deprecated Replaced with foo.');
|
||||
doc.parse();
|
||||
expect(doc.deprecated).toBe('Replaced with foo.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('@this', function(){
|
||||
it('should render @this', function() {
|
||||
var doc = new Doc('@this I am self.');
|
||||
doc.ngdoc = 'filter';
|
||||
doc.parse();
|
||||
expect(doc.html()).toContain('<h3>Method\'s <code>this</code></h3>\n' +
|
||||
'<div>' +
|
||||
'<p>I am self.</p>' +
|
||||
'</div>\n');
|
||||
expect(doc.html()).toContain('<h3>Method\'s <code>this</code></h3>\n' +
|
||||
'<div><p>I am self.</p></div>');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('usage', function(){
|
||||
describe('overview', function(){
|
||||
it('should supress description heading', function(){
|
||||
var doc = new Doc('@ngdoc overview\n@name angular\n@description\n#heading\ntext');
|
||||
doc.parse();
|
||||
expect(doc.html()).toContain('text');
|
||||
expect(doc.html()).toContain('<h2>heading</h2>');
|
||||
expect(doc.html()).not.toContain('Description');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('filter', function(){
|
||||
it('should format', function(){
|
||||
var doc = new Doc({
|
||||
ngdoc:'formatter',
|
||||
shortName:'myFilter',
|
||||
param: [
|
||||
{name:'a'},
|
||||
{name:'b'}
|
||||
]
|
||||
});
|
||||
doc.html_usage_filter(dom);
|
||||
expect(dom).toContain('myFilter_expression | myFilter:b');
|
||||
expect(dom).toContain('angular.filter.myFilter(a, b)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('validator', function(){
|
||||
it('should format', function(){
|
||||
var doc = new Doc({
|
||||
ngdoc:'validator',
|
||||
shortName:'myValidator',
|
||||
param: [
|
||||
{name:'a'},
|
||||
{name:'b'}
|
||||
]
|
||||
});
|
||||
doc.html_usage_validator(dom);
|
||||
expect(dom).toContain('ng:validate="myValidator:b"');
|
||||
expect(dom).toContain('angular.validator.myValidator(a, b)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatter', function(){
|
||||
it('should format', function(){
|
||||
var doc = new Doc({
|
||||
ngdoc:'formatter',
|
||||
shortName:'myFormatter',
|
||||
param: [
|
||||
{name:'a'},
|
||||
]
|
||||
});
|
||||
doc.html_usage_formatter(dom);
|
||||
expect(dom).toContain('ng:format="myFormatter:a"');
|
||||
expect(dom).toContain('var userInputString = angular.formatter.myFormatter.format(modelValue, a);');
|
||||
expect(dom).toContain('var modelValue = angular.formatter.myFormatter.parse(userInputString, a);');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
var SiteMap = require('sitemap.js').SiteMap;
|
||||
var Doc = require('ngdoc.js').Doc;
|
||||
|
||||
|
||||
describe('sitemap', function(){
|
||||
it('should render empty sitemap', function(){
|
||||
var map = new SiteMap([]);
|
||||
expect(map.render()).toEqual([
|
||||
'<?xml version="1.0" encoding="UTF-8"?>',
|
||||
'<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
|
||||
'</sitemapindex>', ''].join('\n'));
|
||||
});
|
||||
|
||||
it('should render ngdoc url', function(){
|
||||
var map = new SiteMap([new Doc({name: 'a.b.c<>\'"&'})]);
|
||||
expect(map.render()).toContain([
|
||||
' <url>',
|
||||
'<loc>http://docs.angularjs.org/#!a.b.c<>'"&</loc>',
|
||||
'<changefreq>weekly</changefreq>',
|
||||
'</url>'].join(''));
|
||||
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
if (global.jasmine) return;
|
||||
|
||||
require.paths.push(__dirname + "/../../lib");
|
||||
require.paths.push(__dirname + '/../src');
|
||||
var jasmine = require('jasmine-1.0.1');
|
||||
var sys = require('util');
|
||||
|
||||
for(var key in jasmine) {
|
||||
global[key] = jasmine[key];
|
||||
}
|
||||
|
||||
//Patch Jasmine for proper stack traces
|
||||
jasmine.Spec.prototype.fail = function (e) {
|
||||
var expectationResult = new jasmine.ExpectationResult({
|
||||
passed: false,
|
||||
message: e ? jasmine.util.formatException(e) : 'Exception'
|
||||
});
|
||||
// PATCH
|
||||
if (e) {
|
||||
expectationResult.trace = e;
|
||||
}
|
||||
this.results_.addResult(expectationResult);
|
||||
};
|
||||
|
||||
|
||||
|
||||
var isVerbose = false;
|
||||
var showColors = true;
|
||||
process.argv.forEach(function(arg){
|
||||
switch(arg) {
|
||||
case '--color': showColors = true; break;
|
||||
case '--noColor': showColors = false; break;
|
||||
case '--verbose': isVerbose = true; break;
|
||||
}
|
||||
});
|
||||
|
||||
jasmine.executeSpecsInFolder(__dirname, function(runner, log){
|
||||
process.exit(runner.results().failedCount);
|
||||
}, isVerbose, showColors);
|
||||
@@ -0,0 +1,18 @@
|
||||
var writer = require('writer.js');
|
||||
describe('writer', function(){
|
||||
describe('toString', function(){
|
||||
var toString = writer.toString;
|
||||
|
||||
it('should merge string', function(){
|
||||
expect(toString('abc')).toEqual('abc');
|
||||
});
|
||||
|
||||
it('should merge obj', function(){
|
||||
expect(toString({a:1})).toEqual('{"a":1}');
|
||||
});
|
||||
|
||||
it('should merge array', function(){
|
||||
expect(toString(['abc',{}])).toEqual('abc{}');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
exports.SiteMap = SiteMap;
|
||||
|
||||
/**
|
||||
* @see http://www.sitemaps.org/protocol.php
|
||||
*
|
||||
* @param docs
|
||||
* @returns {SiteMap}
|
||||
*/
|
||||
function SiteMap(docs){
|
||||
this.render = function(){
|
||||
var map = [];
|
||||
map.push('<?xml version="1.0" encoding="UTF-8"?>');
|
||||
map.push('<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">');
|
||||
docs.forEach(function(doc){
|
||||
map.push(' <url><loc>http://docs.angularjs.org/#!' +
|
||||
encode(doc.name) + '</loc><changefreq>weekly</changefreq></url>');
|
||||
});
|
||||
map.push('</sitemapindex>');
|
||||
map.push('');
|
||||
return map.join('\n');
|
||||
};
|
||||
|
||||
function encode(text){
|
||||
return text
|
||||
.replace(/&/mg, '&')
|
||||
.replace(/</mg, '<')
|
||||
.replace(/>/mg, '>')
|
||||
.replace(/'/mg, ''')
|
||||
.replace(/"/mg, '"');
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,10 @@ function noop(){}
|
||||
|
||||
function chain(delegateFn, explicitDone){
|
||||
var onDoneFn = noop;
|
||||
var onErrorFn = noop;
|
||||
var onErrorFn = function(e){
|
||||
console.error(e.stack || e);
|
||||
process.exit(-1);
|
||||
};
|
||||
var waitForCount = 1;
|
||||
delegateFn = delegateFn || noop;
|
||||
var stackError = new Error('capture stack');
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* DOM generation class
|
||||
*/
|
||||
|
||||
exports.DOM = DOM;
|
||||
exports.htmlEscape = htmlEscape;
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
function htmlEscape(text){
|
||||
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
|
||||
|
||||
function DOM(){
|
||||
this.out = [];
|
||||
this.headingDepth = 0;
|
||||
}
|
||||
|
||||
var INLINE_TAGS = {
|
||||
i: true,
|
||||
b: true
|
||||
};
|
||||
|
||||
DOM.prototype = {
|
||||
toString: function() {
|
||||
return this.out.join('');
|
||||
},
|
||||
|
||||
text: function(content) {
|
||||
if (typeof content == "string") {
|
||||
this.out.push(htmlEscape(content));
|
||||
} else if (typeof content == 'function') {
|
||||
content.call(this, this);
|
||||
} else if (content instanceof Array) {
|
||||
this.ul(content);
|
||||
}
|
||||
},
|
||||
|
||||
html: function(html) {
|
||||
if (html) {
|
||||
var headingDepth = this.headingDepth;
|
||||
for ( var i = 10; i > 0; --i) {
|
||||
html = html
|
||||
.replace(new RegExp('(<\/?h)' + i + '(>)', 'gm'), function(all, start, end){
|
||||
return start + (i + headingDepth) + end;
|
||||
});
|
||||
}
|
||||
this.out.push(html);
|
||||
}
|
||||
},
|
||||
|
||||
tag: function(name, attr, text) {
|
||||
if (!text) {
|
||||
text = attr;
|
||||
attr = {};
|
||||
if (name == 'code')
|
||||
attr['ng:non-bindable'] = '';
|
||||
}
|
||||
this.out.push('<' + name);
|
||||
for(var key in attr) {
|
||||
this.out.push(" " + key + '="' + attr[key] + '"');
|
||||
}
|
||||
this.out.push('>');
|
||||
this.text(text);
|
||||
this.out.push('</' + name + '>');
|
||||
if (!INLINE_TAGS[name])
|
||||
this.out.push('\n');
|
||||
},
|
||||
|
||||
code: function(text) {
|
||||
this.tag('div', {'ng:non-bindable':''}, function(){
|
||||
this.tag('pre', {'class':"brush: js; html-script: true;"}, text);
|
||||
});
|
||||
},
|
||||
|
||||
example: function(description, source, scenario) {
|
||||
if (description || source || scenario) {
|
||||
this.h('Example', function(){
|
||||
if (description)
|
||||
this.html(description);
|
||||
if (scenario === false) {
|
||||
this.code(source);
|
||||
} else {
|
||||
this.tag('doc:example', function(){
|
||||
if (source) this.tag('doc:source', source);
|
||||
if (scenario) this.tag('doc:scenario', scenario);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
h: function(heading, content, fn){
|
||||
if (content==undefined || content && content.legth == 0) return;
|
||||
this.headingDepth++;
|
||||
this.tag('h' + this.headingDepth, heading);
|
||||
var className = typeof heading == 'string'
|
||||
? {'class': heading.toLowerCase().replace(/[^\d\w_]/mg, '-').replace(/-+/gm, '-')}
|
||||
: null;
|
||||
if (content instanceof Array) {
|
||||
this.ul(content, className, fn);
|
||||
} else if (fn) {
|
||||
this.tag('div', className, function(){
|
||||
fn.call(this, content);
|
||||
});
|
||||
} else {
|
||||
this.tag('div', className, content);
|
||||
}
|
||||
this.headingDepth--;
|
||||
},
|
||||
|
||||
h1: function(attr, text) {
|
||||
this.tag('h1', attr, text);
|
||||
},
|
||||
|
||||
h2: function(attr, text) {
|
||||
this.tag('h2', attr, text);
|
||||
},
|
||||
|
||||
h3: function(attr, text) {
|
||||
this.tag('h3', attr, text);
|
||||
},
|
||||
|
||||
p: function(attr, text) {
|
||||
this.tag('p', attr, text);
|
||||
},
|
||||
|
||||
ul: function(list, attr, fn) {
|
||||
if (typeof attr == 'function') {
|
||||
fn = attr;
|
||||
attr = {};
|
||||
}
|
||||
this.tag('ul', attr, function(dom){
|
||||
list.forEach(function(item){
|
||||
dom.out.push('<li>');
|
||||
dom.text(fn ? fn(item) : item);
|
||||
dom.out.push('</li>\n');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
require.paths.push(__dirname);
|
||||
require.paths.push('lib');
|
||||
var reader = require('reader.js'),
|
||||
ngdoc = require('ngdoc.js'),
|
||||
writer = require('writer.js'),
|
||||
callback = require('callback.js'),
|
||||
SiteMap = require('SiteMap.js').SiteMap;
|
||||
|
||||
var docs = [];
|
||||
var start;
|
||||
var work = callback.chain(function(){
|
||||
start = now();
|
||||
console.log('Generating Angular Reference Documentation...');
|
||||
reader.collect(work.waitMany(function(text, file, line){
|
||||
var doc = new ngdoc.Doc(text, file, line);
|
||||
docs.push(doc);
|
||||
doc.parse();
|
||||
}));
|
||||
});
|
||||
var writes = callback.chain(function(){
|
||||
ngdoc.merge(docs);
|
||||
docs.forEach(function(doc){
|
||||
writer.output(doc.id + '.html', doc.html(), writes.waitFor());
|
||||
});
|
||||
var metadata = ngdoc.metadata(docs);
|
||||
writer.output('docs-keywords.js', ['NG_PAGES=', JSON.stringify(metadata).replace(/{/g, '\n{'), ';'], writes.waitFor());
|
||||
writer.copyImages(writes.waitFor());
|
||||
writer.copy('index.html', writes.waitFor());
|
||||
writer.copy('docs.js', writes.waitFor());
|
||||
writer.copy('docs.css', writes.waitFor());
|
||||
writer.copy('doc_widgets.js', writes.waitFor());
|
||||
writer.copy('doc_widgets.css', writes.waitFor());
|
||||
writer.copy('docs-scenario.html', writes.waitFor());
|
||||
writer.output('docs-scenario.js', ngdoc.scenarios(docs), writes.waitFor());
|
||||
writer.output('sitemap.xml', new SiteMap(docs).render(), writes.waitFor());
|
||||
writer.output('robots.txt', 'Sitemap: http://docs.angularjs.org/sitemap.xml\n', writes.waitFor());
|
||||
writer.copy('syntaxhighlighter/shBrushJScript.js', writes.waitFor());
|
||||
writer.copy('syntaxhighlighter/shBrushXml.js', writes.waitFor());
|
||||
writer.copy('syntaxhighlighter/shCore.css', writes.waitFor());
|
||||
writer.copy('syntaxhighlighter/shCore.js', writes.waitFor());
|
||||
writer.copy('syntaxhighlighter/shThemeDefault.css', writes.waitFor());
|
||||
writer.copy('jquery.min.js', writes.waitFor());
|
||||
});
|
||||
writes.onDone(function(){
|
||||
console.log('DONE. Generated ' + docs.length + ' pages in ' +
|
||||
(now()-start) + 'ms.' );
|
||||
});
|
||||
work.onDone(writes);
|
||||
writer.makeDir('build/docs/syntaxhighlighter', work);
|
||||
|
||||
///////////////////////////////////
|
||||
function now(){ return new Date().getTime(); }
|
||||
@@ -0,0 +1,629 @@
|
||||
/**
|
||||
* All parsing/transformation code goes here. All code here should be sync to ease testing.
|
||||
*/
|
||||
|
||||
var Showdown = require('showdown').Showdown;
|
||||
var DOM = require('dom.js').DOM;
|
||||
var htmlEscape = require('dom.js').htmlEscape;
|
||||
var NEW_LINE = /\n\r?/;
|
||||
|
||||
exports.markdown = markdown;
|
||||
exports.trim = trim;
|
||||
exports.metadata = metadata;
|
||||
exports.scenarios = scenarios;
|
||||
exports.merge = merge;
|
||||
exports.Doc = Doc;
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function Doc(text, file, line) {
|
||||
if (typeof text == 'object') {
|
||||
for ( var key in text) {
|
||||
this[key] = text[key];
|
||||
}
|
||||
} else {
|
||||
this.text = text;
|
||||
this.file = file;
|
||||
this.line = line;
|
||||
}
|
||||
}
|
||||
Doc.METADATA_IGNORE = (function(){
|
||||
var words = require('fs').readFileSync(__dirname + '/ignore.words', 'utf8');
|
||||
return words.toString().split(/[,\s\n\r]+/gm);
|
||||
})();
|
||||
|
||||
|
||||
|
||||
Doc.prototype = {
|
||||
keywords: function keywords(){
|
||||
var keywords = {};
|
||||
Doc.METADATA_IGNORE.forEach(function(ignore){ keywords[ignore] = true; });
|
||||
var words = [];
|
||||
var tokens = this.text.toLowerCase().split(/[,\.\`\'\"\s]+/mg);
|
||||
tokens.forEach(function(key){
|
||||
var match = key.match(/^(([\$\_a-z]|ng\:)[\w\_\-]{2,})/);
|
||||
if (match){
|
||||
key = match[1];
|
||||
if (!keywords[key]) {
|
||||
keywords[key] = true;
|
||||
words.push(key);
|
||||
}
|
||||
}
|
||||
});
|
||||
words.sort();
|
||||
return words.join(' ');
|
||||
},
|
||||
|
||||
parse: function(){
|
||||
var atName;
|
||||
var atText;
|
||||
var match;
|
||||
var self = this;
|
||||
this.scenarios = [];
|
||||
this.requires = [];
|
||||
this.param = [];
|
||||
this.properties = [];
|
||||
this.methods = [];
|
||||
self.text.split(NEW_LINE).forEach(function(line){
|
||||
if (match = line.match(/^\s*@(\w+)(\s+(.*))?/)) {
|
||||
// we found @name ...
|
||||
// if we have existing name
|
||||
flush();
|
||||
atName = match[1];
|
||||
atText = [];
|
||||
if(match[3]) atText.push(match[3]);
|
||||
} else {
|
||||
if (atName) {
|
||||
atText.push(line);
|
||||
}
|
||||
}
|
||||
});
|
||||
flush();
|
||||
this.shortName = (this.name || '').split(/[\.#]/).pop();
|
||||
this.id = this.id // if we have an id just use it
|
||||
|| (((this.file||'').match(/.*\/([^\/]*)\.ngdoc/)||{})[1]) // try to extract it from file name
|
||||
|| this.name; // default to name
|
||||
this.description = markdown(this.description);
|
||||
this['this'] = markdown(this['this']);
|
||||
this.exampleDescription = markdown(this.exampleDescription || this.exampleDesc);
|
||||
return this;
|
||||
|
||||
function flush(){
|
||||
if (atName) {
|
||||
var text = trim(atText.join('\n'));
|
||||
if (atName == 'param') {
|
||||
var match = text.match(/^{([^}=]+)(=)?}\s+(([^\s=]+)|\[(\S+)=([^\]]+)\])\s+(.*)/);
|
||||
// 1 12 2 34 4 5 5 6 6 3 7 7
|
||||
if (!match) {
|
||||
throw new Error("Not a valid 'param' format: " + text);
|
||||
}
|
||||
var param = {
|
||||
name: match[5] || match[4],
|
||||
description:markdown(text.replace(match[0], match[7])),
|
||||
type: match[1],
|
||||
optional: !!match[2],
|
||||
'default':match[6]
|
||||
};
|
||||
self.param.push(param);
|
||||
} else if (atName == 'returns') {
|
||||
var match = text.match(/^{([^}=]+)}\s+(.*)/);
|
||||
if (!match) {
|
||||
throw new Error("Not a valid 'returns' format: " + text);
|
||||
}
|
||||
self.returns = {
|
||||
type: match[1],
|
||||
description: markdown(text.replace(match[0], match[2]))
|
||||
};
|
||||
} else if(atName == 'description') {
|
||||
text.replace(/<doc:scenario>([\s\S]*)<\/doc:scenario>/mi,
|
||||
function(_, scenario){
|
||||
self.scenarios.push(scenario);
|
||||
});
|
||||
self.description = text;
|
||||
} else if(atName == 'requires') {
|
||||
self.requires.push(text);
|
||||
} else if(atName == 'scenario') {
|
||||
self.scenarios.push(text);
|
||||
} else if(atName == 'property') {
|
||||
var match = text.match(/^({(\S+)}\s*)?(\S+)(\s+(.*))?/);
|
||||
if (!match) {
|
||||
throw new Error("Not a valid 'property' format: " + text);
|
||||
}
|
||||
var property = {
|
||||
type: match[2],
|
||||
name: match[3],
|
||||
description: match[5] || ''
|
||||
};
|
||||
self.properties.push(property);
|
||||
} else {
|
||||
self[atName] = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
html: function(){
|
||||
var dom = new DOM(),
|
||||
self = this;
|
||||
|
||||
dom.h(this.name, function(){
|
||||
notice('workInProgress', 'Work in Progress',
|
||||
'This page is currently being revised. It might be incomplete or contain inaccuracies.');
|
||||
notice('deprecated', 'Deprecated API', self.deprecated);
|
||||
|
||||
(self['html_usage_' + self.ngdoc] || function(){
|
||||
throw new Error("Don't know how to format @ngdoc: " + self.ngdoc);
|
||||
}).call(self, dom);
|
||||
|
||||
dom.example(self.exampleDescription, self.example, self.scenario);
|
||||
});
|
||||
|
||||
return dom.toString();
|
||||
|
||||
//////////////////////////
|
||||
|
||||
function notice(name, legend, msg){
|
||||
if (self[name] == undefined) return;
|
||||
dom.tag('fieldset', {'class':name}, function(dom){
|
||||
dom.tag('legend', legend);
|
||||
dom.text(msg);
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
html_usage_parameters: function(dom) {
|
||||
dom.h('Parameters', this.param, function(param){
|
||||
dom.tag('code', function(){
|
||||
dom.text(param.name);
|
||||
if (param.optional) {
|
||||
dom.tag('i', function(){
|
||||
dom.text('(optional');
|
||||
if(param['default']) {
|
||||
dom.text('=' + param['default']);
|
||||
}
|
||||
dom.text(')');
|
||||
});
|
||||
}
|
||||
dom.text(' – {');
|
||||
dom.text(param.type);
|
||||
dom.text('} – ');
|
||||
});
|
||||
dom.html(param.description);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_returns: function(dom) {
|
||||
var self = this;
|
||||
if (self.returns) {
|
||||
dom.h('Returns', function(){
|
||||
dom.tag('code', '{' + self.returns.type + '}');
|
||||
dom.text('– ');
|
||||
dom.html(self.returns.description);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
html_usage_this: function(dom) {
|
||||
var self = this;
|
||||
if (self['this']) {
|
||||
dom.h(function(dom){
|
||||
dom.html("Method's <code>this</code>");
|
||||
}, function(dom){
|
||||
dom.html(self['this']);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
html_usage_function: function(dom){
|
||||
var self = this;
|
||||
dom.h('Description', self.description, dom.html);
|
||||
dom.h('Dependencies', self.requires);
|
||||
dom.h('Usage', function(){
|
||||
dom.code(function(){
|
||||
dom.text(self.name);
|
||||
dom.text('(');
|
||||
self.parameters(dom, ', ');
|
||||
dom.text(');');
|
||||
});
|
||||
|
||||
self.html_usage_parameters(dom);
|
||||
self.html_usage_this(dom);
|
||||
self.html_usage_returns(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_directive: function(dom){
|
||||
var self = this;
|
||||
dom.h('Description', self.description, dom.html);
|
||||
dom.h('Dependencies', self.requires);
|
||||
dom.h('Usage', function(){
|
||||
dom.tag('pre', {'class':"brush: js; html-script: true;"}, function(){
|
||||
dom.text('<' + self.element + ' ');
|
||||
dom.text(self.shortName);
|
||||
if (self.param.length) {
|
||||
dom.text('="' + self.param[0].name + '"');
|
||||
}
|
||||
dom.text('>\n ...\n');
|
||||
dom.text('</' + self.element + '>');
|
||||
});
|
||||
self.html_usage_parameters(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_filter: function(dom){
|
||||
var self = this;
|
||||
dom.h('Description', self.description, dom.html);
|
||||
dom.h('Dependencies', self.requires);
|
||||
dom.h('Usage', function(){
|
||||
dom.h('In HTML Template Binding', function(){
|
||||
dom.tag('code', function(){
|
||||
dom.text('{{ ');
|
||||
dom.text(self.shortName);
|
||||
dom.text('_expression | ');
|
||||
dom.text(self.shortName);
|
||||
self.parameters(dom, ':', true);
|
||||
dom.text(' }}');
|
||||
});
|
||||
});
|
||||
|
||||
dom.h('In JavaScript', function(){
|
||||
dom.tag('code', function(){
|
||||
dom.text('angular.filter.');
|
||||
dom.text(self.shortName);
|
||||
dom.text('(');
|
||||
self.parameters(dom, ', ');
|
||||
dom.text(')');
|
||||
});
|
||||
});
|
||||
|
||||
self.html_usage_parameters(dom);
|
||||
self.html_usage_this(dom);
|
||||
self.html_usage_returns(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_formatter: function(dom){
|
||||
var self = this;
|
||||
dom.h('Description', self.description, dom.html);
|
||||
dom.h('Dependencies', self.requires);
|
||||
dom.h('Usage', function(){
|
||||
dom.h('In HTML Template Binding', function(){
|
||||
dom.code(function(){
|
||||
if (self.inputType=='select')
|
||||
dom.text('<select name="bindExpression"');
|
||||
else
|
||||
dom.text('<input type="text" name="bindExpression"');
|
||||
dom.text(' ng:format="');
|
||||
dom.text(self.shortName);
|
||||
self.parameters(dom, ':', false, true);
|
||||
dom.text('">');
|
||||
});
|
||||
});
|
||||
|
||||
dom.h('In JavaScript', function(){
|
||||
dom.code(function(){
|
||||
dom.text('var userInputString = angular.formatter.');
|
||||
dom.text(self.shortName);
|
||||
dom.text('.format(modelValue');
|
||||
self.parameters(dom, ', ', false, true);
|
||||
dom.text(');');
|
||||
dom.text('\n');
|
||||
dom.text('var modelValue = angular.formatter.');
|
||||
dom.text(self.shortName);
|
||||
dom.text('.parse(userInputString');
|
||||
self.parameters(dom, ', ', false, true);
|
||||
dom.text(');');
|
||||
});
|
||||
});
|
||||
|
||||
self.html_usage_parameters(dom);
|
||||
self.html_usage_this(dom);
|
||||
self.html_usage_returns(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_validator: function(dom){
|
||||
var self = this;
|
||||
dom.h('Description', self.description, dom.html);
|
||||
dom.h('Dependencies', self.requires);
|
||||
dom.h('Usage', function(){
|
||||
dom.h('In HTML Template Binding', function(){
|
||||
dom.code(function(){
|
||||
dom.text('<input type="text" ng:validate="');
|
||||
dom.text(self.shortName);
|
||||
self.parameters(dom, ':', true);
|
||||
dom.text('"/>');
|
||||
});
|
||||
});
|
||||
|
||||
dom.h('In JavaScript', function(){
|
||||
dom.code(function(){
|
||||
dom.text('angular.validator.');
|
||||
dom.text(self.shortName);
|
||||
dom.text('(');
|
||||
self.parameters(dom, ', ');
|
||||
dom.text(')');
|
||||
});
|
||||
});
|
||||
|
||||
self.html_usage_parameters(dom);
|
||||
self.html_usage_this(dom);
|
||||
self.html_usage_returns(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_widget: function(dom){
|
||||
var self = this;
|
||||
dom.h('Description', self.description, dom.html);
|
||||
dom.h('Dependencies', self.requires);
|
||||
dom.h('Usage', function(){
|
||||
dom.h('In HTML Template Binding', function(){
|
||||
dom.code(function(){
|
||||
if (self.shortName.match(/^@/)) {
|
||||
dom.text('<');
|
||||
dom.text(self.element);
|
||||
dom.text(' ');
|
||||
dom.text(self.shortName.substring(1));
|
||||
if (self.param.length) {
|
||||
dom.text('="');
|
||||
dom.text(self.param[0].name);
|
||||
dom.text('"');
|
||||
}
|
||||
dom.text('>\n ...\n</');
|
||||
dom.text(self.element);
|
||||
dom.text('>');
|
||||
} else {
|
||||
dom.text('<');
|
||||
dom.text(self.shortName);
|
||||
(self.param||[]).forEach(function(param){
|
||||
if (param.optional) {
|
||||
dom.text(' [' + param.name + '="..."]');
|
||||
} else {
|
||||
dom.text(' ' + param.name + '="..."');
|
||||
}
|
||||
});
|
||||
dom.text('></');
|
||||
dom.text(self.shortName);
|
||||
dom.text('>');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
self.html_usage_parameters(dom);
|
||||
self.html_usage_returns(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_overview: function(dom){
|
||||
dom.html(this.description);
|
||||
},
|
||||
|
||||
html_usage_service: function(dom){
|
||||
dom.h('Description', this.description, dom.html);
|
||||
dom.h('Dependencies', this.requires);
|
||||
|
||||
dom.h('Methods', this.methods, function(method){
|
||||
var signature = (method.param || []).map(property('name'));
|
||||
dom.h(method.shortName + '(' + signature.join(', ') + ')', method, function(){
|
||||
dom.html(method.description);
|
||||
method.html_usage_parameters(dom);
|
||||
dom.example(method.exampleDescription, method.example, false);
|
||||
});
|
||||
});
|
||||
dom.h('Properties', this.properties, function(property){
|
||||
dom.h(property.name, function(){
|
||||
dom.text(property.description);
|
||||
dom.example(property.exampleDescription, property.example, false);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
parameters: function(dom, separator, skipFirst, prefix) {
|
||||
var sep = prefix ? separator : '';
|
||||
(this.param||[]).forEach(function(param, i){
|
||||
if (!(skipFirst && i==0)) {
|
||||
if (param.optional) {
|
||||
dom.text('[' + sep + param.name + ']');
|
||||
} else {
|
||||
dom.text(sep + param.name);
|
||||
}
|
||||
}
|
||||
sep = separator;
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function markdown (text) {
|
||||
if (!text) return text;
|
||||
var parts = text.split(/(<pre>[\s\S]*?<\/pre>|<doc:example>[\s\S]*?<\/doc:example>)/),
|
||||
match;
|
||||
|
||||
parts.forEach(function(text, i){
|
||||
if (text.match(/^<pre>/)) {
|
||||
text = text.replace(/^<pre>([\s\S]*)<\/pre>/mi, function(_, content){
|
||||
return '<div ng:non-bindable><pre class="brush: js; html-script: true;">' +
|
||||
content.replace(/</g, '<').replace(/>/g, '>') +
|
||||
'</pre></div>';
|
||||
});
|
||||
} else if (text.match(/^<doc:example>/)) {
|
||||
text = text.replace(/(<doc:source>)([\s\S]*)(<\/doc:source>)/mi,
|
||||
function(_, before, content, after){
|
||||
return before + htmlEscape(content) + after;
|
||||
});
|
||||
text = text.replace(/(<doc:scenario>)([\s\S]*)(<\/doc:scenario>)/mi,
|
||||
function(_, before, content, after){
|
||||
return before + htmlEscape(content) + after;
|
||||
});
|
||||
} else {
|
||||
text = text.replace(/<angular\/>/gm, '<tt><angular/></tt>');
|
||||
text = text.replace(/{@link ([^\s}]+)((\s|\n)+(.+?))?\s*}/gm,
|
||||
function(_all, url, _2, _3, title){
|
||||
return '<a href="#!' + url + '">'
|
||||
+ (url.match(/^angular\./) ? '<code>' : '')
|
||||
+ (title || url)
|
||||
+ (url.match(/^angular\./) ? '</code>' : '')
|
||||
+ '</a>';
|
||||
});
|
||||
text = new Showdown.converter().makeHtml(text);
|
||||
}
|
||||
parts[i] = text;
|
||||
});
|
||||
return parts.join('');
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function scenarios(docs){
|
||||
var specs = [];
|
||||
docs.forEach(function(doc){
|
||||
specs.push('describe("' + doc.id + '", function(){');
|
||||
specs.push(' beforeEach(function(){');
|
||||
specs.push(' browser().navigateTo("index.html#!' + doc.id + '");');
|
||||
specs.push(' });');
|
||||
specs.push('');
|
||||
doc.scenarios.forEach(function(scenario){
|
||||
specs.push(trim(scenario, ' '));
|
||||
specs.push('');
|
||||
});
|
||||
specs.push('});');
|
||||
specs.push('');
|
||||
if (doc.scenario) {
|
||||
}
|
||||
});
|
||||
return specs.join('\n');
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function metadata(docs){
|
||||
var words = [];
|
||||
docs.forEach(function(doc){
|
||||
var path = (doc.name || '').split(/(\.|\:\s+)/);
|
||||
for ( var i = 1; i < path.length; i++) {
|
||||
path.splice(i, 1);
|
||||
}
|
||||
var depth = path.length - 1;
|
||||
var shortName = path.pop();
|
||||
words.push({
|
||||
id: doc.id,
|
||||
name: doc.name,
|
||||
depth: depth,
|
||||
shortName: shortName,
|
||||
type: doc.ngdoc,
|
||||
keywords:doc.keywords()
|
||||
});
|
||||
});
|
||||
words.sort(keywordSort);
|
||||
return words;
|
||||
}
|
||||
|
||||
var KEYWORD_PRIORITY = {
|
||||
'.guide': 1,
|
||||
'.guide.overview': 1,
|
||||
'.angular': 7,
|
||||
'.angular.Array': 7,
|
||||
'.angular.Object': 7,
|
||||
'.angular.directive': 7,
|
||||
'.angular.filter': 7,
|
||||
'.angular.formatter': 7,
|
||||
'.angular.scope': 7,
|
||||
'.angular.service': 7,
|
||||
'.angular.validator': 7,
|
||||
'.angular.widget': 7
|
||||
};
|
||||
function keywordSort(a, b){
|
||||
function mangleName(doc) {
|
||||
var path = doc.id.split(/\./);
|
||||
var mangled = [];
|
||||
var partialName = '';
|
||||
path.forEach(function(name){
|
||||
partialName += '.' + name;
|
||||
mangled.push(KEYWORD_PRIORITY[partialName] || 5);
|
||||
mangled.push(name);
|
||||
});
|
||||
return mangled.join('.');
|
||||
}
|
||||
var nameA = mangleName(a);
|
||||
var nameB = mangleName(b);
|
||||
return nameA < nameB ? -1 : (nameA > nameB ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function trim(text, prefix) {
|
||||
var MAX = 9999;
|
||||
var empty = RegExp.prototype.test.bind(/^\s*$/);
|
||||
var lines = text.split('\n');
|
||||
var minIndent = MAX;
|
||||
prefix = prefix || '';
|
||||
lines.forEach(function(line){
|
||||
minIndent = Math.min(minIndent, indent(line));
|
||||
});
|
||||
for ( var i = 0; i < lines.length; i++) {
|
||||
lines[i] = prefix + lines[i].substring(minIndent);
|
||||
}
|
||||
// remove leading lines
|
||||
while (empty(lines[0])) {
|
||||
lines.shift();
|
||||
}
|
||||
// remove trailing
|
||||
while (empty(lines[lines.length - 1])) {
|
||||
lines.pop();
|
||||
}
|
||||
return lines.join('\n');
|
||||
|
||||
function indent(line) {
|
||||
for(var i = 0; i < line.length; i++) {
|
||||
if (line.charAt(i) != ' ') {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return MAX;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function merge(docs){
|
||||
var byName = {};
|
||||
docs.forEach(function(doc){
|
||||
byName[doc.name] = doc;
|
||||
});
|
||||
for(var i=0; i<docs.length;) {
|
||||
if (findParent(docs[i], 'method') ||
|
||||
findParent(docs[i], 'property')) {
|
||||
docs.splice(i, 1);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
function findParent(doc, name){
|
||||
var parentName = doc[name+'Of'];
|
||||
if (!parentName) return false;
|
||||
|
||||
var parent = byName[parentName];
|
||||
if (!parent)
|
||||
throw new Error("No parent named '" + parentName + "' for '" +
|
||||
doc.name + "' in @" + name + "Of.");
|
||||
|
||||
var listName = (name + 's').replace(/ys$/, 'ies');
|
||||
var list = parent[listName] = (parent[listName] || []);
|
||||
list.push(doc);
|
||||
list.sort(orderByName);
|
||||
return true;
|
||||
}
|
||||
|
||||
function orderByName(a, b){
|
||||
return a.name < b.name ? -1 : (a.name > b.name ? 1 : 0);
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
function property(name) {
|
||||
return function(value){
|
||||
return value[name];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* All reading related code here. This is so that we can separate the async code from sync code
|
||||
* for testability
|
||||
*/
|
||||
require.paths.push(__dirname);
|
||||
var fs = require('fs'),
|
||||
callback = require('callback');
|
||||
|
||||
var NEW_LINE = /\n\r?/;
|
||||
|
||||
function collect(callback){
|
||||
findJsFiles('src', callback.waitMany(function(file) {
|
||||
//console.log('reading', file, '...');
|
||||
findNgDocInJsFile(file, callback.waitMany(function(doc, line) {
|
||||
callback(doc, file, line);
|
||||
}));
|
||||
}));
|
||||
findNgDocInDir('docs/', callback.waitMany(callback));
|
||||
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();
|
||||
}));
|
||||
}
|
||||
|
||||
function findNgDocInDir(directory, docNotify) {
|
||||
fs.readdir(directory, docNotify.waitFor(function(err, files){
|
||||
if (err) return this.error(err);
|
||||
files.forEach(function(file){
|
||||
//console.log('reading', directory + file, '...');
|
||||
if (!file.match(/\.ngdoc$/)) return;
|
||||
fs.readFile(directory + file, docNotify.waitFor(function(err, content){
|
||||
if (err) return this.error(err);
|
||||
docNotify(content.toString(), directory + file, 1);
|
||||
}));
|
||||
});
|
||||
docNotify.done();
|
||||
}));
|
||||
}
|
||||
|
||||
function findNgDocInJsFile(file, callback) {
|
||||
fs.readFile(file, callback.waitFor(function(err, content){
|
||||
var lines = content.toString().split(NEW_LINE);
|
||||
var text;
|
||||
var startingLine ;
|
||||
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;
|
||||
text = [];
|
||||
startingLine = lineNumber;
|
||||
}
|
||||
// are we done?
|
||||
if (inDoc && line.match(/\*\//)) {
|
||||
text = text.join('\n');
|
||||
text = text.replace(/^\n/, '');
|
||||
if (text.match(/@ngdoc/)){
|
||||
callback(text, startingLine);
|
||||
}
|
||||
doc = null;
|
||||
inDoc = false;
|
||||
}
|
||||
// is the comment add text
|
||||
if (inDoc){
|
||||
text.push(line.replace(/^\s*\*\s?/, ''));
|
||||
}
|
||||
});
|
||||
callback.done();
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
|
||||
exports.collect = collect;
|
||||
@@ -0,0 +1,35 @@
|
||||
@namespace doc url("http://docs.angularjs.org/");
|
||||
|
||||
doc\:example {
|
||||
display: none;
|
||||
}
|
||||
|
||||
ul.doc-example {
|
||||
list-style-type: none;
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
ul.doc-example > li {
|
||||
border: 2px solid gray;
|
||||
border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
background-color: white;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
ul.doc-example > li.doc-example-heading {
|
||||
border: none;
|
||||
border-radius: none;
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
|
||||
li.doc-example-live {
|
||||
padding: 10px;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
div.syntaxhighlighter {
|
||||
padding-bottom: 1px !important; /* fix to remove unnecessary scrollbars http://is.gd/gSMgC */
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
(function(){
|
||||
|
||||
var angularJsUrl;
|
||||
var scripts = document.getElementsByTagName("script");
|
||||
var filename = /(.*\/)angular([^\/]*)/;
|
||||
for(var j = 0; j < scripts.length; j++) {
|
||||
var src = scripts[j].src;
|
||||
if (src && src.match(filename)) {
|
||||
angularJsUrl = src.replace('docs.angularjs.org', 'code.angularjs.org');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var HTML_TEMPLATE =
|
||||
'<!doctype html>\n' +
|
||||
'<html xmlns:ng="http://angularjs.org">\n' +
|
||||
' <script src="' + angularJsUrl + '" ng:autobind></script>\n' +
|
||||
' <body>\n' +
|
||||
'_HTML_SOURCE_\n' +
|
||||
' </body>\n' +
|
||||
'</html>';
|
||||
|
||||
angular.widget('doc:example', function(element){
|
||||
this.descend(true); //compile the example code
|
||||
element.hide();
|
||||
|
||||
var example = element.find('doc\\:source').eq(0),
|
||||
exampleSrc = example.text(),
|
||||
scenario = element.find('doc\\:scenario').eq(0);
|
||||
|
||||
var code = indent(exampleSrc);
|
||||
var tabs = angular.element(
|
||||
'<ul class="doc-example">' +
|
||||
'<li class="doc-example-heading"><h3>Source</h3></li>' +
|
||||
'<li class="doc-example-source" ng:non-bindable>' +
|
||||
'<pre class="brush: js; html-script: true; highlight: [' +
|
||||
code.hilite + ']; toolbar: false;"></pre></li>' +
|
||||
'<li class="doc-example-heading"><h3>Live Preview</h3></li>' +
|
||||
'<li class="doc-example-live">' + exampleSrc +'</li>' +
|
||||
'<li class="doc-example-heading"><h3>Scenario Test</h3></li>' +
|
||||
'<li class="doc-example-scenario"><pre class="brush: js">' + scenario.text() + '</pre></li>' +
|
||||
'</ul>');
|
||||
|
||||
tabs.find('li.doc-example-source > pre').text(HTML_TEMPLATE.replace('_HTML_SOURCE_', code.html));
|
||||
|
||||
element.html('');
|
||||
element.append(tabs);
|
||||
element.show();
|
||||
|
||||
var script = (exampleSrc.match(/<script[^\>]*>([\s\S]*)<\/script>/) || [])[1] || '';
|
||||
try {
|
||||
eval(script);
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
}
|
||||
});
|
||||
|
||||
function indent(text) {
|
||||
var lines = text.split(/\n/);
|
||||
var lineNo = [];
|
||||
// remove any leading blank lines
|
||||
while (lines[0].match(/^\s*$/)) lines.shift();
|
||||
// remove any trailing blank lines
|
||||
while (lines[lines.length - 1].match(/^\s*$/)) lines.pop();
|
||||
var minIndent = 999;
|
||||
for ( var i = 0; i < lines.length; i++) {
|
||||
var line = lines[0];
|
||||
var indent = line.match(/^\s*/)[0];
|
||||
if (indent !== line && indent.length < minIndent) {
|
||||
minIndent = indent.length;
|
||||
}
|
||||
}
|
||||
|
||||
for ( var i = 0; i < lines.length; i++) {
|
||||
lines[i] = ' ' + lines[i].substring(minIndent);
|
||||
lineNo.push(5 + i);
|
||||
}
|
||||
return {html: lines.join('\n'), hilite: lineNo.join(',') };
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -7,4 +7,4 @@
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -0,0 +1,302 @@
|
||||
/* Common Style */
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
|
||||
/* Main Layout */
|
||||
|
||||
#header {
|
||||
height: 3.5em;
|
||||
}
|
||||
|
||||
#sidebar,
|
||||
#main {
|
||||
position: absolute;
|
||||
top: 3.5em;
|
||||
bottom: 0;
|
||||
margin-top: 1px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
width: 13.8em;
|
||||
padding: 0.8em 0em 1.5em 0.8em;
|
||||
}
|
||||
|
||||
#main {
|
||||
left: 14.6em;
|
||||
right: 0;
|
||||
padding: 1em;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
#api-list {
|
||||
position: absolute;
|
||||
top: 3em;
|
||||
bottom: 1em;
|
||||
overflow-y: scroll;
|
||||
padding-right: 0.8em;
|
||||
}
|
||||
|
||||
|
||||
/* App Header */
|
||||
|
||||
#header {
|
||||
background-color: #F2C200;
|
||||
border-bottom: 1px solid #957800;
|
||||
}
|
||||
|
||||
#header h1 {
|
||||
font-weight: normal;
|
||||
font-size: 30px;
|
||||
line-height: 30px;
|
||||
margin: 0;
|
||||
padding: 10px 10px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
#header .angular {
|
||||
font-family: "Courier New", monospace;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#header h1 a {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#header h1 a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
||||
/* Main main Style */
|
||||
|
||||
#main h1 {
|
||||
font-family: monospace;
|
||||
margin-top: 0;
|
||||
padding-bottom: 5px;
|
||||
border-bottom: 1px solid #CCC;
|
||||
}
|
||||
|
||||
#main h2 {
|
||||
margin-top: 1.8em;
|
||||
}
|
||||
|
||||
#main h1 + h2 {
|
||||
margin-top: 1.3em;
|
||||
}
|
||||
|
||||
#main h3 {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
#main ul.methods,
|
||||
#main ul.properties {
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
#main ul.methods > li,
|
||||
#main ul.properties > li {
|
||||
list-style: none;
|
||||
padding: 0 10px 10px 10px;
|
||||
margin-top: 1.5em;
|
||||
border: 1px solid #000;
|
||||
}
|
||||
|
||||
#main ul.methods > li > h3,
|
||||
#main ul.properties > li > h3 {
|
||||
font-family: "Courier New", monospace;
|
||||
background-color: #E0E0E0;
|
||||
margin: 0 -10px 5px -10px;
|
||||
padding: .5em .5em .25em .5em;
|
||||
border-bottom: 1px solid #888;
|
||||
}
|
||||
|
||||
#main li > p,
|
||||
#main div.returns > p {
|
||||
display: inline; /* the top most paragraph should not cause new lines inside lists. */
|
||||
}
|
||||
|
||||
.main-title {
|
||||
float: right;
|
||||
}
|
||||
|
||||
|
||||
/* Searchbox & Sidebar Style */
|
||||
|
||||
#search-box, #sidebar {
|
||||
border-right: 1px solid #DDD;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
background-color: #EEE;
|
||||
}
|
||||
|
||||
#search-box {
|
||||
width: 16em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
#sidebar a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#sidebar a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#sidebar ul {
|
||||
list-style-type: none;
|
||||
/*TODO(esprehn): Can we just reset globally and not break examples?*/
|
||||
margin: 0;
|
||||
padding: 0 0.8em 0 0;
|
||||
width: 13em;
|
||||
}
|
||||
|
||||
#sidebar ul li {
|
||||
}
|
||||
|
||||
#sidebar ul li a {
|
||||
display: block;
|
||||
padding: 2px 2px 2px 4px;
|
||||
}
|
||||
|
||||
#sidebar ul li.selected a {
|
||||
background-color: #DDD;
|
||||
border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border: 1px solid #CCC;
|
||||
padding: 1px 1px 1px 3px;
|
||||
}
|
||||
|
||||
#sidebar ul li.level-0 {
|
||||
margin-top: 0.5em;
|
||||
margin-left: 0em;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
#sidebar ul li.level-0:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#sidebar ul li.level-1.level-angular {
|
||||
font-family: monospace;
|
||||
font-weight: normal;
|
||||
font-size: 1em;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#sidebar ul li.level-1 {
|
||||
margin-left: 1em;
|
||||
margin-top: 5px;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#sidebar ul li.level-2 {
|
||||
margin-left: 2em;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
#sidebar ul li.level-3 {
|
||||
margin-left: 3em;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
#sidebar ul li.level-4 {
|
||||
margin-left: 4em;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
|
||||
/* Warning and Info Banners */
|
||||
|
||||
.deprecated {
|
||||
border: 2px solid red;
|
||||
}
|
||||
|
||||
.deprecated legend {
|
||||
font-weight: bold;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.workInProgress {
|
||||
border: 2px solid orange;
|
||||
}
|
||||
|
||||
.workInProgress legend {
|
||||
font-weight: bold;
|
||||
color: orange;
|
||||
}
|
||||
|
||||
|
||||
/* Feedback Link */
|
||||
|
||||
#feedback {
|
||||
float: right;
|
||||
width: 10em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
/* Live Example Style */
|
||||
|
||||
.doc-example-live table td {
|
||||
padding: 0 1.5em;
|
||||
}
|
||||
|
||||
|
||||
/* Scrollbars */
|
||||
|
||||
::-webkit-scrollbar{
|
||||
width:0.8em;
|
||||
margin: 0.2em 0em;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar:hover{
|
||||
background-color:#eee;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb{
|
||||
min-height:0.8em;
|
||||
min-width:0.8em;
|
||||
-webkit-border-radius:0.5em;
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover{
|
||||
background-color: #bbb;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:active{
|
||||
background-color:#888;
|
||||
}
|
||||
|
||||
#sidebar::-webkit-scrollbar {
|
||||
background-color:#eee;
|
||||
}
|
||||
|
||||
#main::-webkit-scrollbar {
|
||||
background-color:#fff;
|
||||
}
|
||||
|
||||
/* Content */
|
||||
img.right {
|
||||
float: right;
|
||||
}
|
||||
h1, h2, h3, h4, h5 {
|
||||
clear: both;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
DocsController.$inject = ['$location', '$browser', '$window'];
|
||||
function DocsController($location, $browser, $window) {
|
||||
this.pages = NG_PAGES;
|
||||
window.$root = this.$root;
|
||||
|
||||
this.getUrl = function(page){
|
||||
return '#!' + page.id;
|
||||
};
|
||||
|
||||
this.getCurrentPartial = function(){
|
||||
return './' + this.getTitle() + '.html';
|
||||
};
|
||||
|
||||
this.getTitle = function(){
|
||||
var hashPath = $location.hashPath || '!angular';
|
||||
if (hashPath.match(/^!/)) {
|
||||
this.partialTitle = hashPath.substring(1);
|
||||
}
|
||||
return this.partialTitle;
|
||||
};
|
||||
|
||||
this.getClass = function(page) {
|
||||
var depth = page.depth,
|
||||
cssClass = 'level-' + depth + (page.name == this.getTitle() ? ' selected' : '');
|
||||
|
||||
if (depth == 1 && page.type !== 'overview') cssClass += ' level-angular';
|
||||
|
||||
return cssClass;
|
||||
};
|
||||
|
||||
this.afterPartialLoaded = function() {
|
||||
SyntaxHighlighter.highlight();
|
||||
};
|
||||
|
||||
this.getFeedbackUrl = function() {
|
||||
return "mailto:angular@googlegroups.com?" +
|
||||
"subject=" + escape("Feedback on " + $location.href) + "&" +
|
||||
"body=" + escape("Hi there,\n\nI read " + $location.href + " and wanted to ask ....");
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
SyntaxHighlighter['defaults'].toolbar = false;
|
||||
@@ -0,0 +1,46 @@
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org/"
|
||||
xmlns:doc="http://docs.angularjs.org/"
|
||||
ng:controller="DocsController">
|
||||
<head>
|
||||
<title ng:bind-template="<angular/>: {{getTitle()}}"><angular/></title>
|
||||
|
||||
<meta name="fragment" content="!">
|
||||
|
||||
<link rel="stylesheet" href="doc_widgets.css" type="text/css" />
|
||||
<link rel="stylesheet" href="docs.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="syntaxhighlighter/shCore.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="syntaxhighlighter/shThemeDefault.css" type="text/css"/>
|
||||
|
||||
<script src="jquery.min.js"></script>
|
||||
<script src="syntaxhighlighter/shCore.js"></script>
|
||||
<script src="syntaxhighlighter/shBrushJScript.js"></script>
|
||||
<script src="syntaxhighlighter/shBrushXml.js"></script>
|
||||
|
||||
<script src="../angular.min.js" ng:autobind></script>
|
||||
<script src="docs.js"></script>
|
||||
<script src="doc_widgets.js"></script>
|
||||
<script src="docs-keywords.js"></script>
|
||||
</head>
|
||||
<body style="display:none;" ng:show="true">
|
||||
<div id="header">
|
||||
<h1>
|
||||
<span class="main-title">{{getTitle()}}</span>
|
||||
<a href="#" tabindex="0"><span class="angular"><angular/></span> Docs</a>
|
||||
</h1>
|
||||
</div>
|
||||
<div id="sidebar">
|
||||
<input type="text" name="search" id="search-box" placeholder="search the docs"
|
||||
tabindex="1" accesskey="s"/>
|
||||
<ul id="api-list">
|
||||
<li ng:repeat="page in pages.$filter(search)" ng:class="getClass(page)">
|
||||
<a href="{{getUrl(page)}}" ng:click="" tabindex="2">{{page.shortName}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<a id="feedback" ng:href="{{getFeedbackUrl()}}">Report an Issue or Ask a Question</a>
|
||||
<ng:include src="getCurrentPartial()" onload="afterPartialLoaded()"></ng:include>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../../lib/jquery/jquery-1.4.2.min.js
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* SyntaxHighlighter
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter
|
||||
*
|
||||
* SyntaxHighlighter is donationware. If you are using it, please donate.
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
|
||||
*
|
||||
* @version
|
||||
* 3.0.83 (July 02 2010)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (C) 2004-2010 Alex Gorbatchev.
|
||||
*
|
||||
* @license
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*/
|
||||
;(function()
|
||||
{
|
||||
// CommonJS
|
||||
typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
|
||||
|
||||
function Brush()
|
||||
{
|
||||
var keywords = 'break case catch continue ' +
|
||||
'default delete do else false ' +
|
||||
'for function if in instanceof ' +
|
||||
'new null return super switch ' +
|
||||
'this throw true try typeof var while with'
|
||||
;
|
||||
|
||||
var r = SyntaxHighlighter.regexLib;
|
||||
|
||||
this.regexList = [
|
||||
{ regex: r.multiLineDoubleQuotedString, css: 'string' }, // double quoted strings
|
||||
{ regex: r.multiLineSingleQuotedString, css: 'string' }, // single quoted strings
|
||||
{ regex: r.singleLineCComments, css: 'comments' }, // one line comments
|
||||
{ regex: r.multiLineCComments, css: 'comments' }, // multiline comments
|
||||
{ regex: /\s*#.*/gm, css: 'preprocessor' }, // preprocessor tags like #region and #endregion
|
||||
{ regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' } // keywords
|
||||
];
|
||||
|
||||
this.forHtmlScript(r.scriptScriptTags);
|
||||
};
|
||||
|
||||
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
||||
Brush.aliases = ['js', 'jscript', 'javascript'];
|
||||
|
||||
SyntaxHighlighter.brushes.JScript = Brush;
|
||||
|
||||
// CommonJS
|
||||
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
|
||||
})();
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* SyntaxHighlighter
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter
|
||||
*
|
||||
* SyntaxHighlighter is donationware. If you are using it, please donate.
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
|
||||
*
|
||||
* @version
|
||||
* 3.0.83 (July 02 2010)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (C) 2004-2010 Alex Gorbatchev.
|
||||
*
|
||||
* @license
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*/
|
||||
;(function()
|
||||
{
|
||||
// CommonJS
|
||||
typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
|
||||
|
||||
function Brush()
|
||||
{
|
||||
function process(match, regexInfo)
|
||||
{
|
||||
var constructor = SyntaxHighlighter.Match,
|
||||
code = match[0],
|
||||
tag = new XRegExp('(<|<)[\\s\\/\\?]*(?<name>[:\\w-\\.]+)', 'xg').exec(code),
|
||||
result = []
|
||||
;
|
||||
|
||||
if (match.attributes != null)
|
||||
{
|
||||
var attributes,
|
||||
regex = new XRegExp('(?<name> [\\w:\\-\\.]+)' +
|
||||
'\\s*=\\s*' +
|
||||
'(?<value> ".*?"|\'.*?\'|\\w+)',
|
||||
'xg');
|
||||
|
||||
while ((attributes = regex.exec(code)) != null)
|
||||
{
|
||||
result.push(new constructor(attributes.name, match.index + attributes.index, 'color1'));
|
||||
result.push(new constructor(attributes.value, match.index + attributes.index + attributes[0].indexOf(attributes.value), 'string'));
|
||||
}
|
||||
}
|
||||
|
||||
if (tag != null)
|
||||
result.push(
|
||||
new constructor(tag.name, match.index + tag[0].indexOf(tag.name), 'keyword')
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
this.regexList = [
|
||||
{ regex: new XRegExp('(\\<|<)\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\](\\>|>)', 'gm'), css: 'color2' }, // <![ ... [ ... ]]>
|
||||
{ regex: SyntaxHighlighter.regexLib.xmlComments, css: 'comments' }, // <!-- ... -->
|
||||
{ regex: new XRegExp('(<|<)[\\s\\/\\?]*(\\w+)(?<attributes>.*?)[\\s\\/\\?]*(>|>)', 'sg'), func: process }
|
||||
];
|
||||
};
|
||||
|
||||
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
||||
Brush.aliases = ['xml', 'xhtml', 'xslt', 'html'];
|
||||
|
||||
SyntaxHighlighter.brushes.Xml = Brush;
|
||||
|
||||
// CommonJS
|
||||
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
|
||||
})();
|
||||
@@ -0,0 +1,226 @@
|
||||
/**
|
||||
* SyntaxHighlighter
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter
|
||||
*
|
||||
* SyntaxHighlighter is donationware. If you are using it, please donate.
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
|
||||
*
|
||||
* @version
|
||||
* 3.0.83 (July 02 2010)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (C) 2004-2010 Alex Gorbatchev.
|
||||
*
|
||||
* @license
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*/
|
||||
.syntaxhighlighter a,
|
||||
.syntaxhighlighter div,
|
||||
.syntaxhighlighter code,
|
||||
.syntaxhighlighter table,
|
||||
.syntaxhighlighter table td,
|
||||
.syntaxhighlighter table tr,
|
||||
.syntaxhighlighter table tbody,
|
||||
.syntaxhighlighter table thead,
|
||||
.syntaxhighlighter table caption,
|
||||
.syntaxhighlighter textarea {
|
||||
-moz-border-radius: 0 0 0 0 !important;
|
||||
-webkit-border-radius: 0 0 0 0 !important;
|
||||
background: none !important;
|
||||
border: 0 !important;
|
||||
bottom: auto !important;
|
||||
float: none !important;
|
||||
height: auto !important;
|
||||
left: auto !important;
|
||||
line-height: 1.1em !important;
|
||||
margin: 0 !important;
|
||||
outline: 0 !important;
|
||||
overflow: visible !important;
|
||||
padding: 0 !important;
|
||||
position: static !important;
|
||||
right: auto !important;
|
||||
text-align: left !important;
|
||||
top: auto !important;
|
||||
vertical-align: baseline !important;
|
||||
width: auto !important;
|
||||
box-sizing: content-box !important;
|
||||
font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
|
||||
font-weight: normal !important;
|
||||
font-style: normal !important;
|
||||
font-size: 1em !important;
|
||||
min-height: inherit !important;
|
||||
min-height: auto !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter {
|
||||
width: 100% !important;
|
||||
margin: 1em 0 1em 0 !important;
|
||||
position: relative !important;
|
||||
overflow: auto !important;
|
||||
font-size: 1em !important;
|
||||
}
|
||||
.syntaxhighlighter.source {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
.syntaxhighlighter .bold {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
.syntaxhighlighter .italic {
|
||||
font-style: italic !important;
|
||||
}
|
||||
.syntaxhighlighter .line {
|
||||
white-space: pre !important;
|
||||
}
|
||||
.syntaxhighlighter table {
|
||||
width: 100% !important;
|
||||
}
|
||||
.syntaxhighlighter table caption {
|
||||
text-align: left !important;
|
||||
padding: .5em 0 0.5em 1em !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code {
|
||||
width: 100% !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code .container {
|
||||
position: relative !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code .container textarea {
|
||||
box-sizing: border-box !important;
|
||||
position: absolute !important;
|
||||
left: 0 !important;
|
||||
top: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
border: none !important;
|
||||
background: white !important;
|
||||
padding-left: 1em !important;
|
||||
overflow: hidden !important;
|
||||
white-space: pre !important;
|
||||
}
|
||||
.syntaxhighlighter table td.gutter .line {
|
||||
text-align: right !important;
|
||||
padding: 0 0.5em 0 1em !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code .line {
|
||||
padding: 0 1em !important;
|
||||
}
|
||||
.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
|
||||
padding-left: 0em !important;
|
||||
}
|
||||
.syntaxhighlighter.show {
|
||||
display: block !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed table {
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar {
|
||||
padding: 0.1em 0.8em 0em 0.8em !important;
|
||||
font-size: 1em !important;
|
||||
position: static !important;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar span {
|
||||
display: inline !important;
|
||||
margin-right: 1em !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar span a {
|
||||
padding: 0 !important;
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar span a.expandSource {
|
||||
display: inline !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar {
|
||||
position: absolute !important;
|
||||
right: 1px !important;
|
||||
top: 1px !important;
|
||||
width: 11px !important;
|
||||
height: 11px !important;
|
||||
font-size: 10px !important;
|
||||
z-index: 10 !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar span.title {
|
||||
display: inline !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a {
|
||||
display: block !important;
|
||||
text-align: center !important;
|
||||
text-decoration: none !important;
|
||||
padding-top: 1px !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a.expandSource {
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.ie {
|
||||
font-size: .9em !important;
|
||||
padding: 1px 0 1px 0 !important;
|
||||
}
|
||||
.syntaxhighlighter.ie .toolbar {
|
||||
line-height: 8px !important;
|
||||
}
|
||||
.syntaxhighlighter.ie .toolbar a {
|
||||
padding-top: 0px !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line.alt1 .content,
|
||||
.syntaxhighlighter.printing .line.alt2 .content,
|
||||
.syntaxhighlighter.printing .line.highlighted .number,
|
||||
.syntaxhighlighter.printing .line.highlighted.alt1 .content,
|
||||
.syntaxhighlighter.printing .line.highlighted.alt2 .content {
|
||||
background: none !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line .number {
|
||||
color: #bbbbbb !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line .content {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .toolbar {
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.printing a {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
|
||||
color: #008200 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
|
||||
color: blue !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .keyword {
|
||||
color: #006699 !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .preprocessor {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .variable {
|
||||
color: #aa7700 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .value {
|
||||
color: #009900 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .functions {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .constants {
|
||||
color: #0066cc !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .script {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
|
||||
color: red !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
|
||||
color: black !important;
|
||||
}
|
||||
+17
File diff suppressed because one or more lines are too long
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* SyntaxHighlighter
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter
|
||||
*
|
||||
* SyntaxHighlighter is donationware. If you are using it, please donate.
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
|
||||
*
|
||||
* @version
|
||||
* 3.0.83 (July 02 2010)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (C) 2004-2010 Alex Gorbatchev.
|
||||
*
|
||||
* @license
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*/
|
||||
.syntaxhighlighter {
|
||||
background-color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .line.alt1 {
|
||||
background-color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .line.alt2 {
|
||||
background-color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
|
||||
background-color: #e0e0e0 !important;
|
||||
}
|
||||
.syntaxhighlighter .line.highlighted.number {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter table caption {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter .gutter {
|
||||
color: #afafaf !important;
|
||||
}
|
||||
.syntaxhighlighter .gutter .line {
|
||||
border-right: 3px solid #6ce26c !important;
|
||||
}
|
||||
.syntaxhighlighter .gutter .line.highlighted {
|
||||
background-color: #6ce26c !important;
|
||||
color: white !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line .content {
|
||||
border: none !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed {
|
||||
overflow: visible !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar {
|
||||
color: blue !important;
|
||||
background: white !important;
|
||||
border: 1px solid #6ce26c !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar a {
|
||||
color: blue !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar a:hover {
|
||||
color: red !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar {
|
||||
color: white !important;
|
||||
background: #6ce26c !important;
|
||||
border: none !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a {
|
||||
color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a:hover {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
|
||||
color: #008200 !important;
|
||||
}
|
||||
.syntaxhighlighter .string, .syntaxhighlighter .string a {
|
||||
color: blue !important;
|
||||
}
|
||||
.syntaxhighlighter .keyword {
|
||||
color: #006699 !important;
|
||||
}
|
||||
.syntaxhighlighter .preprocessor {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter .variable {
|
||||
color: #aa7700 !important;
|
||||
}
|
||||
.syntaxhighlighter .value {
|
||||
color: #009900 !important;
|
||||
}
|
||||
.syntaxhighlighter .functions {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter .constants {
|
||||
color: #0066cc !important;
|
||||
}
|
||||
.syntaxhighlighter .script {
|
||||
font-weight: bold !important;
|
||||
color: #006699 !important;
|
||||
background-color: none !important;
|
||||
}
|
||||
.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
|
||||
color: red !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .keyword {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
3.0.83
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* All writing related code here. This is so that we can separate the async code from sync code
|
||||
* for testability
|
||||
*/
|
||||
require.paths.push(__dirname);
|
||||
var fs = require('fs');
|
||||
var OUTPUT_DIR = "build/docs/";
|
||||
|
||||
function output(docs, content, callback){
|
||||
callback();
|
||||
}
|
||||
|
||||
exports.output = function(file, content, callback){
|
||||
//console.log('writing', OUTPUT_DIR + file, '...');
|
||||
fs.writeFile(
|
||||
OUTPUT_DIR + file,
|
||||
exports.toString(content),
|
||||
callback);
|
||||
};
|
||||
|
||||
|
||||
exports.toString = function toString(obj){
|
||||
switch (typeof obj) {
|
||||
case 'string':
|
||||
return obj;
|
||||
case 'object':
|
||||
if (obj instanceof Array) {
|
||||
obj.forEach(function (value, key){
|
||||
obj[key] = toString(value);
|
||||
});
|
||||
return obj.join('');
|
||||
} else {
|
||||
return JSON.stringify(obj);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
exports.makeDir = function (path, callback) {
|
||||
var parts = path.split(/\//);
|
||||
path = '.';
|
||||
(function next(){
|
||||
if (parts.length) {
|
||||
path += '/' + parts.shift();
|
||||
fs.mkdir(path, 0777, next);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
exports.copy = function(filename, callback){
|
||||
copy('docs/src/templates/' + filename, OUTPUT_DIR + filename, callback);
|
||||
};
|
||||
|
||||
function copy(from, to, callback) {
|
||||
//console.log('writing', to, '...');
|
||||
fs.readFile(from, function(err, content){
|
||||
if (err) return callback.error(err);
|
||||
fs.writeFile(to, content, callback);
|
||||
});
|
||||
}
|
||||
|
||||
exports.copyImages = function(callback) {
|
||||
exports.makeDir(OUTPUT_DIR + '/img', callback.waitFor(function(){
|
||||
fs.readdir('docs/img', callback.waitFor(function(err, files){
|
||||
if (err) return this.error(err);
|
||||
files.forEach(function(file){
|
||||
if (file.match(/\.(png|gif|jpg|jpeg)$/)) {
|
||||
copy('docs/img/' + file, OUTPUT_DIR + '/img/' + file, callback.waitFor());
|
||||
}
|
||||
});
|
||||
callback();
|
||||
}));
|
||||
}));
|
||||
};
|
||||
@@ -1,58 +0,0 @@
|
||||
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;
|
||||
}
|
||||
//////////////////
|
||||
@@ -1,51 +0,0 @@
|
||||
(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"><angular/></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');
|
||||
});
|
||||
};
|
||||
});
|
||||
})();
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<!-- TODO: we need to expose $root so that we can delete cookies in the scenario runner, there
|
||||
must be a better way to do this -->
|
||||
<body ng:controller="example.personalLog.LogCtrl" ng:init="$window.$root = $root">
|
||||
<body ng:controller="example.personalLog.LogCtrl">
|
||||
|
||||
<form action="" ng:submit="addLog(newMsg)">
|
||||
<input type="text" name="newMsg" />
|
||||
|
||||
@@ -81,15 +81,14 @@ angular.scenario.dsl('clearCookies', function() {
|
||||
*/
|
||||
return function() {
|
||||
this.addFutureAction('clear all cookies', function($window, $document, done) {
|
||||
//TODO: accessing angular services is pretty nasty, we need a better way to reach them
|
||||
var $cookies = $window.$root.$cookies,
|
||||
var rootScope = $window.angular.element($document[0]).data('$scope'),
|
||||
$cookies = rootScope.$service('$cookies'),
|
||||
cookieName;
|
||||
|
||||
for (cookieName in $cookies) {
|
||||
console.log('deleting cookie: ' + cookieName);
|
||||
delete $cookies[cookieName];
|
||||
}
|
||||
$window.$root.$eval();
|
||||
rootScope.$eval();
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ describe('example.personalLog.LogCtrl', function() {
|
||||
|
||||
function createNotesCtrl() {
|
||||
var scope = angular.scope();
|
||||
scope.$cookies = scope.$service('$cookies');
|
||||
return scope.$new(example.personalLog.LogCtrl);
|
||||
}
|
||||
|
||||
|
||||
+17
-31
@@ -1,36 +1,22 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<script type="text/javascript"
|
||||
src="../angular-debug.js" ng:autobind ng:css="css/angular.css"></script>
|
||||
<title>angular dev sandbox</title>
|
||||
<script src="../src/angular-bootstrap.js" ng:autobind></script>
|
||||
<script>
|
||||
angular.service('routeConfig', function($route) {
|
||||
$route.when('/view1', {controller: MyCtrl, template: 'view1.html'});
|
||||
$route.when('/view2', {controller: MyCtrl, template: 'view2.html'});
|
||||
|
||||
function MyCtrl() {};
|
||||
}, {$inject: ['$route'], $eager: true});
|
||||
</script>
|
||||
</head>
|
||||
<body ng:init="$window.$root = this">
|
||||
|
||||
<script>
|
||||
angular.widget('my:greeter', function(compileElement){
|
||||
var compiler = this;
|
||||
compileElement.css('style', 'block');
|
||||
var salutaitonExp = compileElement.attr('salutation');
|
||||
var nameExp = compileElement.attr('name');
|
||||
return function(linkElement){
|
||||
var salutaitonSpan = angular.element('<span class="salutation"></span');
|
||||
var nameSpan = angular.element('<span class="name"></span>');
|
||||
linkElement.append(salutaitonSpan);
|
||||
linkElement.append(compiler.text(' '));
|
||||
linkElement.append(nameSpan);
|
||||
linkElement.append(compiler.text('!'));
|
||||
this.$watch(salutaitonExp, function(value){
|
||||
salutaitonSpan.text(value);
|
||||
});
|
||||
this.$watch(nameExp, function(value){
|
||||
nameSpan.text(value);
|
||||
});
|
||||
};
|
||||
});
|
||||
</script>
|
||||
<div ng:init="salutation='Hello'; name='World'">
|
||||
<my:greeter salutation="salutation" name="name"/>
|
||||
</div>
|
||||
<body ng:init="$service('$window').$root = this">
|
||||
<p>
|
||||
<a href="#/view1">view1</a> | <a href="#/view2">view2</a> | <a href="#">blank</a>
|
||||
</p>
|
||||
|
||||
view: <ng:view></ng:view>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
+3
-3
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
/usr/local/bin/node docs/collect.js
|
||||
#!/bin/bash
|
||||
. ~/.bashrc
|
||||
node docs/spec/specs.js --noColor | grep -v '/lib/jasmine' && node docs/src/gen-docs.js
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
server: http://localhost:9876
|
||||
|
||||
load:
|
||||
- lib/jasmine-1.0.1/jasmine.js
|
||||
- lib/jasmine-jstd-adapter/JasmineAdapter.js
|
||||
- lib/jquery/jquery-1.4.2.js
|
||||
- test/jquery_remove.js
|
||||
- src/Angular.js
|
||||
- src/JSON.js
|
||||
- src/*.js
|
||||
- example/personalLog/*.js
|
||||
- test/testabilityPatch.js
|
||||
- src/scenario/Scenario.js
|
||||
- src/scenario/output/*.js
|
||||
- src/scenario/*.js
|
||||
- src/angular-mocks.js
|
||||
- test/mocks.js
|
||||
- test/scenario/*.js
|
||||
- test/scenario/output/*.js
|
||||
- test/*.js
|
||||
- example/personalLog/test/*.js
|
||||
|
||||
exclude:
|
||||
- test/jquery_alias.js
|
||||
- src/angular.prefix
|
||||
- src/angular.suffix
|
||||
- src/angular-bootstrap.js
|
||||
- src/scenario/angular-bootstrap.js
|
||||
- src/AngularPublic.js
|
||||
|
||||
plugin:
|
||||
- name: "coverage"
|
||||
jar: "lib/jstestdriver/coverage.jar"
|
||||
module: "com.google.jstestdriver.coverage.CoverageModule"
|
||||
|
||||
@@ -8,14 +8,17 @@ load:
|
||||
- src/Angular.js
|
||||
- src/JSON.js
|
||||
- src/*.js
|
||||
- example/personalLog/*.js
|
||||
- test/testabilityPatch.js
|
||||
- src/scenario/Scenario.js
|
||||
- src/scenario/output/*.js
|
||||
- src/scenario/*.js
|
||||
- test/angular-mocks.js
|
||||
- src/angular-mocks.js
|
||||
- test/mocks.js
|
||||
- test/scenario/*.js
|
||||
- test/scenario/output/*.js
|
||||
- test/*.js
|
||||
- example/personalLog/test/*.js
|
||||
|
||||
exclude:
|
||||
- src/angular.prefix
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
server: http://localhost:9876
|
||||
|
||||
load:
|
||||
- lib/jasmine-1.0.1/jasmine.js
|
||||
- lib/jasmine-jstd-adapter/JasmineAdapter.js
|
||||
- lib/jquery/jquery-1.4.2.js
|
||||
- test/jquery_remove.js
|
||||
- build/angular.min.js
|
||||
- perf/data/*.js
|
||||
- perf/testUtils.js
|
||||
- perf/*.js
|
||||
|
||||
exclude:
|
||||
+2
-1
@@ -13,7 +13,8 @@ load:
|
||||
- src/scenario/Scenario.js
|
||||
- src/scenario/output/*.js
|
||||
- src/scenario/*.js
|
||||
- test/angular-mocks.js
|
||||
- src/angular-mocks.js
|
||||
- test/mocks.js
|
||||
- test/scenario/*.js
|
||||
- test/scenario/output/*.js
|
||||
- test/*.js
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
var fs = require('fs');
|
||||
var sys = require('sys');
|
||||
var path = require('path');
|
||||
|
||||
var filename = __dirname + '/jasmine.js';
|
||||
global.window = {
|
||||
setTimeout: setTimeout,
|
||||
clearTimeout: clearTimeout,
|
||||
setInterval: setInterval,
|
||||
clearInterval: clearInterval
|
||||
};
|
||||
var src = fs.readFileSync(filename);
|
||||
var jasmine = process.compile(src + '\njasmine;', filename);
|
||||
delete global.window;
|
||||
|
||||
function noop(){}
|
||||
|
||||
jasmine.executeSpecsInFolder = function(folder, done, isVerbose, showColors, matcher){
|
||||
var log = [];
|
||||
var columnCounter = 0;
|
||||
var start = 0;
|
||||
var elapsed = 0;
|
||||
var verbose = isVerbose || false;
|
||||
var fileMatcher = new RegExp(matcher || "\.js$");
|
||||
var colors = showColors || false;
|
||||
var specs = jasmine.getAllSpecFiles(folder, fileMatcher);
|
||||
|
||||
var ansi = {
|
||||
green: '\033[32m',
|
||||
red: '\033[31m',
|
||||
yellow: '\033[33m',
|
||||
none: '\033[0m'
|
||||
};
|
||||
|
||||
for (var i = 0, len = specs.length; i < len; ++i){
|
||||
var filename = specs[i];
|
||||
require(filename.replace(/\.*$/, ""));
|
||||
}
|
||||
|
||||
var jasmineEnv = jasmine.getEnv();
|
||||
jasmineEnv.reporter = {
|
||||
log: function(str){
|
||||
},
|
||||
|
||||
reportSpecStarting: function(runner) {
|
||||
},
|
||||
|
||||
reportRunnerStarting: function(runner) {
|
||||
sys.puts('Started');
|
||||
start = Number(new Date);
|
||||
},
|
||||
|
||||
reportSuiteResults: function(suite) {
|
||||
var specResults = suite.results();
|
||||
var path = [];
|
||||
while(suite) {
|
||||
path.unshift(suite.description);
|
||||
suite = suite.parentSuite;
|
||||
}
|
||||
var description = path.join(' ');
|
||||
|
||||
if (verbose)
|
||||
log.push('Spec ' + description);
|
||||
|
||||
specResults.items_.forEach(function(spec){
|
||||
if (spec.failedCount > 0 && spec.description) {
|
||||
if (!verbose)
|
||||
log.push(description);
|
||||
log.push(' it ' + spec.description);
|
||||
spec.items_.forEach(function(result){
|
||||
log.push(' ' + result.trace.stack + '\n');
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
reportSpecResults: function(spec) {
|
||||
var result = spec.results();
|
||||
var msg = '';
|
||||
if (result.passed())
|
||||
{
|
||||
msg = (colors) ? (ansi.green + '.' + ansi.none) : '.';
|
||||
// } else if (result.skipped) { TODO: Research why "result.skipped" returns false when "xit" is called on a spec?
|
||||
// msg = (colors) ? (ansi.yellow + '*' + ansi.none) : '*';
|
||||
} else {
|
||||
msg = (colors) ? (ansi.red + 'F' + ansi.none) : 'F';
|
||||
}
|
||||
sys.print(msg);
|
||||
if (columnCounter++ < 50) return;
|
||||
columnCounter = 0;
|
||||
sys.print('\n');
|
||||
},
|
||||
|
||||
|
||||
reportRunnerResults: function(runner) {
|
||||
elapsed = (Number(new Date) - start) / 1000;
|
||||
sys.puts('\n');
|
||||
log.forEach(function(log){
|
||||
sys.puts(log);
|
||||
});
|
||||
sys.puts('Finished in ' + elapsed + ' seconds');
|
||||
|
||||
var summary = jasmine.printRunnerResults(runner);
|
||||
if(colors)
|
||||
{
|
||||
if(runner.results().failedCount === 0 )
|
||||
sys.puts(ansi.green + summary + ansi.none);
|
||||
else
|
||||
sys.puts(ansi.red + summary + ansi.none);
|
||||
} else {
|
||||
sys.puts(summary);
|
||||
}
|
||||
(done||noop)(runner, log);
|
||||
}
|
||||
};
|
||||
jasmineEnv.execute();
|
||||
};
|
||||
|
||||
jasmine.getAllSpecFiles = function(dir, matcher){
|
||||
var specs = [];
|
||||
|
||||
if (fs.statSync(dir).isFile() && dir.match(matcher)) {
|
||||
specs.push(dir);
|
||||
} else {
|
||||
var files = fs.readdirSync(dir);
|
||||
for (var i = 0, len = files.length; i < len; ++i){
|
||||
var filename = dir + '/' + files[i];
|
||||
if (fs.statSync(filename).isFile() && filename.match(matcher)){
|
||||
specs.push(filename);
|
||||
}else if (fs.statSync(filename).isDirectory()){
|
||||
var subfiles = this.getAllSpecFiles(filename, matcher);
|
||||
subfiles.forEach(function(result){
|
||||
specs.push(result);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return specs;
|
||||
};
|
||||
|
||||
jasmine.printRunnerResults = function(runner){
|
||||
var results = runner.results();
|
||||
var suites = runner.suites();
|
||||
var msg = '';
|
||||
msg += suites.length + ' test' + ((suites.length === 1) ? '' : 's') + ', ';
|
||||
msg += results.totalCount + ' assertion' + ((results.totalCount === 1) ? '' : 's') + ', ';
|
||||
msg += results.failedCount + ' failure' + ((results.failedCount === 1) ? '' : 's') + '\n';
|
||||
return msg;
|
||||
};
|
||||
|
||||
function now(){
|
||||
return new Date().getTime();
|
||||
}
|
||||
|
||||
jasmine.asyncSpecWait = function(){
|
||||
var wait = jasmine.asyncSpecWait;
|
||||
wait.start = now();
|
||||
wait.done = false;
|
||||
(function innerWait(){
|
||||
waits(10);
|
||||
runs(function() {
|
||||
if (wait.start + wait.timeout < now()) {
|
||||
expect('timeout waiting for spec').toBeNull();
|
||||
} else if (wait.done) {
|
||||
wait.done = false;
|
||||
} else {
|
||||
innerWait();
|
||||
}
|
||||
});
|
||||
})();
|
||||
};
|
||||
jasmine.asyncSpecWait.timeout = 4 * 1000;
|
||||
jasmine.asyncSpecDone = function(){
|
||||
jasmine.asyncSpecWait.done = true;
|
||||
};
|
||||
|
||||
for ( var key in jasmine) {
|
||||
exports[key] = jasmine[key];
|
||||
}
|
||||
@@ -1,111 +1,176 @@
|
||||
/**
|
||||
* @fileoverview Jasmine JsTestDriver Adapter.
|
||||
* @author ibolmo@gmail.com (Olmo Maldonado)
|
||||
* @author misko@hevery.com (Misko Hevery)
|
||||
*/
|
||||
(function(window) {
|
||||
var rootDescribes = new Describes(window);
|
||||
var describePath = [];
|
||||
rootDescribes.collectMode();
|
||||
|
||||
var jasmineTest = TestCase('Jasmine Adapter Tests');
|
||||
|
||||
var jasminePlugin = {
|
||||
name:'jasmine',
|
||||
runTestConfiguration: function(testRunConfiguration, onTestDone, onTestRunConfigurationComplete){
|
||||
if (testRunConfiguration.testCaseInfo_.template_ !== jasmineTest) return;
|
||||
|
||||
var jasmineEnv = jasmine.currentEnv_ = new jasmine.Env();
|
||||
rootDescribes.playback();
|
||||
var specLog = jstestdriver.console.log_ = [];
|
||||
var start;
|
||||
jasmineEnv.specFilter = function(spec) {
|
||||
return rootDescribes.isExclusive(spec);
|
||||
};
|
||||
jasmineEnv.reporter = {
|
||||
log: function(str){
|
||||
specLog.push(str);
|
||||
},
|
||||
|
||||
(function() {
|
||||
reportRunnerStarting: function(runner) { },
|
||||
|
||||
function bind(_this, _function){
|
||||
return function(){
|
||||
return _function.call(_this);
|
||||
};
|
||||
reportSpecStarting: function(spec) {
|
||||
specLog = jstestdriver.console.log_ = [];
|
||||
start = new Date().getTime();
|
||||
},
|
||||
|
||||
reportSpecResults: function(spec) {
|
||||
var suite = spec.suite;
|
||||
var results = spec.results();
|
||||
if (results.skipped) return;
|
||||
var end = new Date().getTime();
|
||||
var messages = [];
|
||||
var resultItems = results.getItems();
|
||||
var state = 'passed';
|
||||
for ( var i = 0; i < resultItems.length; i++) {
|
||||
if (!resultItems[i].passed()) {
|
||||
state = resultItems[i].message.match(/AssertionError:/) ? 'error' : 'failed';
|
||||
messages.push(resultItems[i].toString());
|
||||
messages.push(formatStack(resultItems[i].trace.stack));
|
||||
}
|
||||
}
|
||||
onTestDone(
|
||||
new jstestdriver.TestResult(
|
||||
suite.getFullName(),
|
||||
spec.description,
|
||||
state,
|
||||
messages.join('\n'),
|
||||
specLog.join('\n'),
|
||||
end - start));
|
||||
},
|
||||
|
||||
reportSuiteResults: function(suite) {},
|
||||
|
||||
reportRunnerResults: function(runner) {
|
||||
onTestRunConfigurationComplete();
|
||||
}
|
||||
};
|
||||
jasmineEnv.execute();
|
||||
return true;
|
||||
},
|
||||
onTestsFinish: function(){
|
||||
jasmine.currentEnv_ = null;
|
||||
rootDescribes.collectMode();
|
||||
}
|
||||
};
|
||||
jstestdriver.pluginRegistrar.register(jasminePlugin);
|
||||
|
||||
function formatStack(stack) {
|
||||
var lines = (stack||'').split(/\r?\n/);
|
||||
var frames = [];
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].match(/\/jasmine[\.-]/)) {
|
||||
frames.push(lines[i].replace(/https?:\/\/\w+(:\d+)?\/test\//, '').replace(/^\s*/, ' '));
|
||||
}
|
||||
}
|
||||
return frames.join('\n');
|
||||
}
|
||||
|
||||
var currentFrame = frame(null, null);
|
||||
|
||||
function frame(parent, name){
|
||||
var caseName = (parent && parent.caseName ? parent.caseName + " " : '') + (name ? name : '');
|
||||
var frame = {
|
||||
name: name,
|
||||
caseName: caseName,
|
||||
parent: parent,
|
||||
testCase: TestCase(caseName),
|
||||
before: [],
|
||||
after: [],
|
||||
runBefore: function(){
|
||||
if (parent) parent.runBefore.apply(this);
|
||||
for ( var i = 0; i < frame.before.length; i++) {
|
||||
frame.before[i].apply(this);
|
||||
}
|
||||
},
|
||||
runAfter: function(){
|
||||
for ( var i = 0; i < frame.after.length; i++) {
|
||||
frame.after[i].apply(this);
|
||||
}
|
||||
if (parent) parent.runAfter.apply(this);
|
||||
}
|
||||
};
|
||||
return frame;
|
||||
};
|
||||
|
||||
jasmine.Env.prototype.describe = (function(describe){
|
||||
return function(description){
|
||||
currentFrame = frame(currentFrame, description);
|
||||
var val = describe.apply(this, arguments);
|
||||
currentFrame = currentFrame.parent;
|
||||
return val;
|
||||
};
|
||||
|
||||
})(jasmine.Env.prototype.describe);
|
||||
|
||||
var id = 0;
|
||||
|
||||
jasmine.Env.prototype.it = (function(it){
|
||||
return function(desc, itFn){
|
||||
var self = this;
|
||||
var spec = it.apply(this, arguments);
|
||||
var currentSpec = this.currentSpec;
|
||||
if (!currentSpec.$id) {
|
||||
currentSpec.$id = id++;
|
||||
}
|
||||
var frame = this.jstdFrame = currentFrame;
|
||||
var name = 'test that it ' + desc;
|
||||
if (this.jstdFrame.testCase.prototype[name])
|
||||
throw "Spec with name '" + desc + "' already exists.";
|
||||
this.jstdFrame.testCase.prototype[name] = function(){
|
||||
jasmine.getEnv().currentSpec = currentSpec;
|
||||
frame.runBefore.apply(currentSpec);
|
||||
try {
|
||||
itFn.apply(currentSpec);
|
||||
} finally {
|
||||
frame.runAfter.apply(currentSpec);
|
||||
function noop(){}
|
||||
function Describes(window){
|
||||
var describes = {};
|
||||
var beforeEachs = {};
|
||||
var afterEachs = {};
|
||||
var exclusive;
|
||||
var collectMode = true;
|
||||
intercept('describe', describes);
|
||||
intercept('xdescribe', describes);
|
||||
intercept('beforeEach', beforeEachs);
|
||||
intercept('afterEach', afterEachs);
|
||||
|
||||
function intercept(functionName, collection){
|
||||
window[functionName] = function(desc, fn){
|
||||
if (collectMode) {
|
||||
collection[desc] = function(){
|
||||
jasmine.getEnv()[functionName](desc, fn);
|
||||
};
|
||||
} else {
|
||||
jasmine.getEnv()[functionName](desc, fn);
|
||||
}
|
||||
};
|
||||
return spec;
|
||||
}
|
||||
window.ddescribe = function(name, fn){
|
||||
exclusive = true;
|
||||
console.log('ddescribe', name);
|
||||
window.describe(name, function(){
|
||||
var oldIt = window.it;
|
||||
window.it = window.iit;
|
||||
try {
|
||||
fn.call(this);
|
||||
} finally {
|
||||
window.it = oldIt;
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
})(jasmine.Env.prototype.it);
|
||||
|
||||
|
||||
jasmine.Env.prototype.beforeEach = (function(beforeEach){
|
||||
return function(beforeEachFunction) {
|
||||
beforeEach.apply(this, arguments);
|
||||
currentFrame.before.push(beforeEachFunction);
|
||||
window.iit = function(name, fn){
|
||||
exclusive = fn.exclusive = true;
|
||||
console.log(fn);
|
||||
jasmine.getEnv().it(name, fn);
|
||||
};
|
||||
|
||||
})(jasmine.Env.prototype.beforeEach);
|
||||
|
||||
|
||||
jasmine.Env.prototype.afterEach = (function(afterEach){
|
||||
return function(afterEachFunction) {
|
||||
afterEach.apply(this, arguments);
|
||||
currentFrame.after.push(afterEachFunction);
|
||||
|
||||
|
||||
this.collectMode = function() {
|
||||
collectMode = true;
|
||||
exclusive = false;
|
||||
};
|
||||
|
||||
})(jasmine.Env.prototype.afterEach);
|
||||
|
||||
|
||||
jasmine.NestedResults.prototype.addResult = (function(addResult){
|
||||
return function(result) {
|
||||
addResult.call(this, result);
|
||||
if (result.type != 'MessageResult' && !result.passed()) fail(result.message);
|
||||
this.playback = function(){
|
||||
collectMode = false;
|
||||
playback(beforeEachs);
|
||||
playback(afterEachs);
|
||||
playback(describes);
|
||||
|
||||
function playback(set) {
|
||||
for ( var name in set) {
|
||||
set[name]();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.isExclusive = function(spec) {
|
||||
if (exclusive) {
|
||||
var blocks = spec.queue.blocks;
|
||||
for ( var i = 0; i < blocks.length; i++) {
|
||||
if (blocks[i].func.exclusive) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
})(window);
|
||||
|
||||
})(jasmine.NestedResults.prototype.addResult);
|
||||
// Patch Jasmine for proper stack traces
|
||||
jasmine.Spec.prototype.fail = function (e) {
|
||||
var expectationResult = new jasmine.ExpectationResult({
|
||||
passed: false,
|
||||
message: e ? jasmine.util.formatException(e) : 'Exception'
|
||||
});
|
||||
// PATCH
|
||||
if (e) {
|
||||
expectationResult.trace = e;
|
||||
}
|
||||
this.results_.addResult(expectationResult);
|
||||
};
|
||||
|
||||
// Reset environment with overriden methods.
|
||||
jasmine.currentEnv_ = null;
|
||||
jasmine.getEnv();
|
||||
|
||||
})();
|
||||
|
||||
-77
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1,21 +0,0 @@
|
||||
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.
|
||||
@@ -1,344 +0,0 @@
|
||||
/*
|
||||
* 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 "&";
|
||||
case "\\": return "\\\\";
|
||||
case '"': return '"';
|
||||
case "'": return ''';
|
||||
case "<": return "<";
|
||||
case ">": return ">";
|
||||
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);
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
var fs = require('fs');
|
||||
|
||||
var filename = __dirname + '/showdown-0.9.js';
|
||||
var src = fs.readFileSync(filename);
|
||||
var Showdown = process.compile(src + '\nShowdown;', filename);
|
||||
exports.Showdown = Showdown;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
|
||||
This test demonstrates the time difference between document's DOMContentLoaded and window's load events.
|
||||
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
startTS = new Date().getTime();
|
||||
onDOMContentLoadedTS = 0; // default for browsers where DOMCL is not supported
|
||||
</script>
|
||||
<title>DOMContentLoaded test</title>
|
||||
<script src="../build/angular.min.js" ng:autobind></script>
|
||||
<script>
|
||||
angular.element(document).bind('DOMContentLoaded', function(e) {onDOMContentLoadedTS = new Date().getTime()});
|
||||
angular.element(window).bind('load', function(e) {
|
||||
onloadTS = new Date().getTime();
|
||||
log.innerHTML = 'start: ' + new Date(startTS) + '<br/>DOMContentLoaded: +' + (onDOMContentLoadedTS - startTS) + 'ms<br/> load: +' + (onloadTS - startTS) + 'ms';
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>DOMContentLoaded test</h1>
|
||||
<p>{{ 'yay!' || 'angular starting...' }}</p>
|
||||
|
||||
<img width="100px" src="http://lh5.ggpht.com/_BLyMhylclm0/TST_bbGH0zI/AAAAAAAAATY/oNUn9kivKN8/s912/1020047.jpg" />
|
||||
<img width="100px" src="http://lh5.ggpht.com/_MqEybfAuUFk/TSOOiegUlPI/AAAAAAAADHY/AEwEWc64_-M/s800/IMG_7294.JPG" />
|
||||
<img width="100px" src="http://lh3.ggpht.com/_LdjD3ua8rpE/TSOW99rwjZI/AAAAAAAAFC0/0qJRhhN45RM/s912/Saison%2010%20%2834%29.JPG" />
|
||||
<img width="100px" src="http://lh6.ggpht.com/_oy_-am3CVUw/TSOQBddZpwI/AAAAAAAACaw/ogFgoD79bVE/s912/P1100886.JPG" />
|
||||
<img width="100px" src="http://lh4.ggpht.com/_srSaA7ZN7oc/TDdxXbA_i1I/AAAAAAAAQ2w/ii3vgrnfCrM/s800/Urlaub10%20157.jpg" />
|
||||
<img width="100px" src="http://lh5.ggpht.com/_y6vXu6iRrfM/SIaYhRQBYNI/AAAAAAAAAmE/lV2NYwxtsQM/s912/North%20Dakota%20Trip%20014.JPG" />
|
||||
<img width="100px" src="http://lh5.ggpht.com/_Jjv9cIn9cS8/RuwZCgfOl6I/AAAAAAAAAOc/QrrMe8vpawg/s800/Shark%20Trip%20-%20day%202%20513.JPG" />
|
||||
|
||||
<p id="log"></p>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,49 @@
|
||||
def generate_object(f, objName, iterations)
|
||||
f.write("var #{objName}='[");
|
||||
|
||||
iterations.times do |i|
|
||||
f.write('{')
|
||||
|
||||
f.write('"simpleStringProperty":') #23
|
||||
f.write('"some string value ' + ('%07d' % i) + '"') #27
|
||||
f.write(',')
|
||||
|
||||
f.write('"stringWithQuotes":') #19
|
||||
f.write('"some string with \\\\"quotes\\\\" ' + ('%07d' % i) + '"') #36
|
||||
f.write(',')
|
||||
|
||||
f.write('"stringWithUnicode":')
|
||||
f.write('"short string with \\u1234 unicode \\u2345 chars ' + ('%07d' % i) + '"')
|
||||
f.write(',')
|
||||
|
||||
f.write('"aNumber":') #10
|
||||
f.write(i) #?
|
||||
f.write(',')
|
||||
|
||||
f.write('"smallArray":')
|
||||
f.write('["a",23,"b",42,' + i.to_s + ']')
|
||||
f.write(',')
|
||||
|
||||
f.write('"smallObj":')
|
||||
f.write('{"foo":"bar","baz":543,"num":' + i.to_s + ',"fuz":"fuz buz huz duz ' + i.to_s + '"}')
|
||||
f.write(',')
|
||||
|
||||
f.write('"timeStamp":')
|
||||
f.write('"2010-12-22T04:58:01.' + ("%03d" % (i%1000)) + '"')
|
||||
|
||||
f.write('},')
|
||||
end
|
||||
|
||||
f.write('"just a padding string"]\';' + "\n\n");
|
||||
end
|
||||
|
||||
file_path = File.join(File.dirname(__FILE__), 'jsonParserPayload.js')
|
||||
|
||||
File.open(file_path, 'w') do |f|
|
||||
generate_object(f, 'superTinyJsonString', 1) #~300b
|
||||
generate_object(f, 'tinyJsonString', 3) #~1kb
|
||||
generate_object(f, 'smallJsonString', 30) #~10kb
|
||||
generate_object(f, 'mediumJsonString', 600) #~200kb
|
||||
generate_object(f, 'largeJsonString', 2000) #~650kb
|
||||
end
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
describe('json', function() {
|
||||
|
||||
it('angular parser', function() {
|
||||
var duration = time(function() {
|
||||
expect(angular.fromJson(largeJsonString)).toBeTruthy();
|
||||
}, 1);
|
||||
|
||||
dump(duration/1 + ' ms per iteration');
|
||||
});
|
||||
|
||||
|
||||
it('angular delegating to native parser', function() {
|
||||
var duration = time(function() {
|
||||
expect(angular.fromJson(largeJsonString, true)).toBeTruthy();
|
||||
}, 100);
|
||||
|
||||
dump(duration/100 + ' ms per iteration');
|
||||
});
|
||||
|
||||
|
||||
it('native json', function() {
|
||||
var duration = time(function() {
|
||||
expect(JSON.parse(largeJsonString)).toBeTruthy();
|
||||
}, 100);
|
||||
|
||||
dump(duration/100 + ' ms per iteration');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<script>
|
||||
function el(id) {
|
||||
return document.getElementById(id);
|
||||
}
|
||||
function update() {
|
||||
el("output").innerHTML = el("input").value;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Your name: <input id="input" type="text" value="World"
|
||||
onkeydown="setTimeout(update,0)"/>
|
||||
<hr/>
|
||||
Hello <span id="output">{{yourname}}</span>!
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,20 @@
|
||||
if (window.jstestdriver) {
|
||||
jstd = jstestdriver;
|
||||
dump = angular.bind(jstd.console, jstd.console.log);
|
||||
}
|
||||
|
||||
function time(fn, times) {
|
||||
times = times || 1;
|
||||
|
||||
var i,
|
||||
start,
|
||||
duration = 0;
|
||||
|
||||
for (i=0; i<times; i++) {
|
||||
start = Date.now();
|
||||
fn();
|
||||
duration += Date.now() - start;
|
||||
}
|
||||
|
||||
return duration;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<script type="text/javascript" src="../src/angular-bootstrap.js" ng:autobind></script>
|
||||
<body>
|
||||
<span ng:init='x = {d:3}; x1 = {bar:[x,5]}; x1.bar[0].d = 4'>
|
||||
<input name="x1.bar[0].d" type="text"></input>
|
||||
<input name="x.d" type="text"></input>
|
||||
<span> {{x1}} -- {{x1.bar[0].d}}</span>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<script type="text/javascript" src="../lib/jquery/jquery-1.4.2.js"></script>
|
||||
<script type="text/javascript" src="../src/angular-bootstrap.js" ng:autobind></script>
|
||||
<head>
|
||||
<body>
|
||||
<select name='selection0' style="display:block;">
|
||||
<option ng:repeat='value in ["FOO","BAR"]'">{{value}}</option>
|
||||
</select>
|
||||
{{selection0}} <-- FOO should be shown here
|
||||
|
||||
<hr/>
|
||||
|
||||
<select ng:init="selection1='ignore'" name='selection1' style="display:block;">
|
||||
<option ng:repeat='value in ["FOO","BAR"]' ng:bind-attr="{selected:'{{value==\'BAR\'}}'}">{{value}}</option>
|
||||
</select>
|
||||
{{selection1}} <-- BAR should be shown here
|
||||
|
||||
<hr/>
|
||||
|
||||
<select ng:init="selection2=1" name="selection2" style="display:block;">
|
||||
<option value="{{$index}}" ng:repeat="opt in ['zero', 'one']">{{opt}}</option>
|
||||
</select>
|
||||
{{selection2}} <-- 1 should be shown here
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,8 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<script type="text/javascript" src="../src/angular-bootstrap.js" ng:autobind></script>
|
||||
<body>
|
||||
<textarea name="html" rows="10" cols="100"></textarea>
|
||||
<div>{{html|html}}</div>
|
||||
</body>
|
||||
</html>
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
java -jar lib/jstestdriver/JsTestDriver.jar --port 9876 --browserTimeout 20000 --config jsTestDriver-coverage.conf
|
||||
@@ -1 +1,3 @@
|
||||
java -jar lib/jstestdriver/JsTestDriver.jar --port 9876 --browserTimeout 20000
|
||||
#!/bin/bash
|
||||
|
||||
java -jar lib/jstestdriver/JsTestDriver.jar --port 9876 --browserTimeout 90000
|
||||
|
||||
+568
-125
@@ -4,27 +4,29 @@ if (typeof document.getAttribute == $undefined)
|
||||
document.getAttribute = function() {};
|
||||
|
||||
/**
|
||||
* @ngdoc
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.lowercase
|
||||
* @function
|
||||
*
|
||||
* @description Converts string to lowercase
|
||||
* @param {string} value
|
||||
* @param {string} string String to be lowercased.
|
||||
* @returns {string} Lowercased string.
|
||||
*/
|
||||
var lowercase = function (value){ return isString(value) ? value.toLowerCase() : value; };
|
||||
var lowercase = function (string){ return isString(string) ? string.toLowerCase() : string; };
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc
|
||||
* @name angular#uppercase
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.uppercase
|
||||
* @function
|
||||
*
|
||||
* @description Converts string to uppercase.
|
||||
* @param {string} value
|
||||
* @param {string} string String to be uppercased.
|
||||
* @returns {string} Uppercased string.
|
||||
*/
|
||||
var uppercase = function (value){ return isString(value) ? value.toUpperCase() : value; };
|
||||
var uppercase = function (string){ return isString(string) ? string.toUpperCase() : string; };
|
||||
|
||||
|
||||
var manualLowercase = function (s) {
|
||||
@@ -51,6 +53,9 @@ function fromCharCode(code) { return String.fromCharCode(code); }
|
||||
var _undefined = undefined,
|
||||
_null = null,
|
||||
$$element = '$element',
|
||||
$$update = '$update',
|
||||
$$scope = '$scope',
|
||||
$$validate = '$validate',
|
||||
$angular = 'angular',
|
||||
$array = 'array',
|
||||
$boolean = 'boolean',
|
||||
@@ -67,6 +72,8 @@ var _undefined = undefined,
|
||||
$number = 'number',
|
||||
$object = 'object',
|
||||
$string = 'string',
|
||||
$value = 'value',
|
||||
$selected = 'selected',
|
||||
$undefined = 'undefined',
|
||||
NG_EXCEPTION = 'ng-exception',
|
||||
NG_VALIDATION_ERROR = 'ng-validation-error',
|
||||
@@ -75,6 +82,7 @@ var _undefined = undefined,
|
||||
PRIORITY_WATCH = -1000,
|
||||
PRIORITY_LAST = 99999,
|
||||
PRIORITY = {'FIRST': PRIORITY_FIRST, 'LAST': PRIORITY_LAST, 'WATCH':PRIORITY_WATCH},
|
||||
Error = window.Error,
|
||||
jQuery = window['jQuery'] || window['$'], // weirdness to make IE happy
|
||||
_ = window['_'],
|
||||
/** holds major version number for IE or NaN for real browsers */
|
||||
@@ -84,106 +92,56 @@ var _undefined = undefined,
|
||||
push = Array.prototype.push,
|
||||
error = window[$console] ? bind(window[$console], window[$console]['error'] || noop) : noop,
|
||||
|
||||
/**
|
||||
* @name angular
|
||||
* @namespace The exported angular namespace.
|
||||
*/
|
||||
/** @name angular */
|
||||
angular = window[$angular] || (window[$angular] = {}),
|
||||
angularTextMarkup = extensionMap(angular, 'markup'),
|
||||
angularAttrMarkup = extensionMap(angular, 'attrMarkup'),
|
||||
/** @name angular.directive */
|
||||
angularDirective = extensionMap(angular, 'directive'),
|
||||
/** @name angular.widget */
|
||||
angularWidget = extensionMap(angular, 'widget', lowercase),
|
||||
/** @name angular.validator */
|
||||
angularValidator = extensionMap(angular, 'validator'),
|
||||
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* Filters allow you to do just that. In addition to transforming the data, filters also modify
|
||||
* the DOM. This allows the filters to for example apply css styles to the filtered output if
|
||||
* certain conditions were met.
|
||||
*
|
||||
*
|
||||
* # Standard Filters
|
||||
*
|
||||
* The Angular framework provides a standard set of filters for common operations, including:
|
||||
* {@link angular.filter.currency}, {@link angular.filter.json}, {@link angular.filter.number},
|
||||
* and {@link angular.filter.html}. You can also add your own filters.
|
||||
*
|
||||
*
|
||||
* # Syntax
|
||||
*
|
||||
* Filters can be part of any {@link angular.scope} evaluation but are typically used with
|
||||
* {{bindings}}. Filters typically transform the data to a new data type, formating the data in
|
||||
* the process. Filters can be chained and take optional arguments. Here are few examples:
|
||||
*
|
||||
* * No filter: {{1234.5678}} => 1234.5678
|
||||
* * Number filter: {{1234.5678|number}} => 1,234.57. Notice the “,” and rounding to two
|
||||
* significant digits.
|
||||
* * Filter with arguments: {{1234.5678|number:5}} => 1,234.56780. Filters can take optional
|
||||
* arguments, separated by colons in a binding. To number, the argument “5” requests 5 digits
|
||||
* to the right of the decimal point.
|
||||
*
|
||||
*
|
||||
* # Writing your own Filters
|
||||
*
|
||||
* Writing your own filter is very easy: just define a JavaScript function on `angular.filter`.
|
||||
* The framework passes in the input value as the first argument to your function. Any filter
|
||||
* arguments are passed in as additional function arguments.
|
||||
*
|
||||
* You can use these variables in the function:
|
||||
*
|
||||
* * `this` — The current scope.
|
||||
* * `$element` — The DOM element containing the binding. This allows the filter to manipulate
|
||||
* the DOM in addition to transforming the input.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* //TODO this example current doesn't show up anywhere because the overview template doesn't
|
||||
* // render it.
|
||||
*
|
||||
* The following example filter reverses a text string. In addition, it conditionally makes the
|
||||
* text upper-case (to demonstrate optional arguments) and assigns color (to demonstrate DOM
|
||||
* modification).
|
||||
|
||||
<script type="text/javascript">
|
||||
angular.filter.reverse = function(input, uppercase, color) {
|
||||
var out = "";
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
out = input.charAt(i) + out;
|
||||
}
|
||||
if (uppercase) {
|
||||
out = out.toUpperCase();
|
||||
}
|
||||
if (color) {
|
||||
this.$element.css('color', color);
|
||||
}
|
||||
return out;
|
||||
};
|
||||
</script>
|
||||
<span ng:non-bindable="true">{{"hello"|reverse}}</span>: {{"hello"|reverse}}<br>
|
||||
<span ng:non-bindable="true">{{"hello"|reverse:true}}</span>: {{"hello"|reverse:true}}<br>
|
||||
<span ng:non-bindable="true">{{"hello"|reverse:true:"blue"}}</span>:
|
||||
{{"hello"|reverse:true:"blue"}}
|
||||
|
||||
* //TODO: I completely dropped a mention of using the other option (setter method), it's
|
||||
* confusing to have two ways to do the same thing. I just wonder if we should prefer using the
|
||||
* setter way over direct assignment because in the future we might want to be able to intercept
|
||||
* filter registrations for some reason.
|
||||
*/
|
||||
/** @name angular.fileter */
|
||||
angularFilter = extensionMap(angular, 'filter'),
|
||||
/** @name angular.formatter */
|
||||
angularFormatter = extensionMap(angular, 'formatter'),
|
||||
/** @name angular.service */
|
||||
angularService = extensionMap(angular, 'service'),
|
||||
angularCallbacks = extensionMap(angular, 'callbacks'),
|
||||
nodeName,
|
||||
rngScript = /^(|.*\/)angular(-.*?)?(\.min)?.js(\?[^#]*)?(#(.*))?$/;
|
||||
nodeName_,
|
||||
rngScript = /^(|.*\/)angular(-.*?)?(\.min)?.js(\?[^#]*)?(#(.*))?$/,
|
||||
DATE_ISOSTRING_LN = 24;
|
||||
|
||||
function foreach(obj, iterator, context) {
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.forEach
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Invokes the `iterator` function once for each item in `obj` collection. The collection can either
|
||||
* be an object or an array. The `iterator` function is invoked with `iterator(value, key)`, where
|
||||
* `value` is the value of an object property or an array element and `key` is the object property
|
||||
* key or array element index. Optionally, `context` can be specified for the iterator function.
|
||||
*
|
||||
* Note: this function was previously known as `angular.foreach`.
|
||||
*
|
||||
<pre>
|
||||
var values = {name: 'misko', gender: 'male'};
|
||||
var log = [];
|
||||
angular.forEach(values, function(value, key){
|
||||
this.push(key + ': ' + value);
|
||||
}, log);
|
||||
expect(log).toEqual(['name: misko', 'gender:male']);
|
||||
</pre>
|
||||
*
|
||||
* @param {Object|Array} obj Object to iterate over.
|
||||
* @param {function()} iterator Iterator function.
|
||||
* @param {Object} context Object to become context (`this`) for the iterator function.
|
||||
* @returns {Objet|Array} Reference to `obj`.
|
||||
*/
|
||||
function forEach(obj, iterator, context) {
|
||||
var key;
|
||||
if (obj) {
|
||||
if (isFunction(obj)){
|
||||
@@ -192,7 +150,7 @@ function foreach(obj, iterator, context) {
|
||||
iterator.call(context, obj[key], key);
|
||||
}
|
||||
}
|
||||
} else if (obj.forEach) {
|
||||
} else if (obj.forEach && obj.forEach !== forEach) {
|
||||
obj.forEach(iterator, context);
|
||||
} else if (isObject(obj) && isNumber(obj.length)) {
|
||||
for (key = 0; key < obj.length; key++)
|
||||
@@ -205,7 +163,7 @@ function foreach(obj, iterator, context) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
function foreachSorted(obj, iterator, context) {
|
||||
function forEachSorted(obj, iterator, context) {
|
||||
var keys = [];
|
||||
for (var key in obj) keys.push(key);
|
||||
keys.sort();
|
||||
@@ -216,10 +174,35 @@ function foreachSorted(obj, iterator, context) {
|
||||
}
|
||||
|
||||
|
||||
function formatError(arg) {
|
||||
if (arg instanceof Error) {
|
||||
if (arg.stack) {
|
||||
arg = arg.stack;
|
||||
} else if (arg.sourceURL) {
|
||||
arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
|
||||
}
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.extend
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Extends the destination object `dst` by copying all of the properties from the `src` objects to
|
||||
* `dst`. You can specify multiple `src` objects.
|
||||
*
|
||||
* @param {Object} dst The destination object.
|
||||
* @param {...Object} src The source object(s).
|
||||
*/
|
||||
function extend(dst) {
|
||||
foreach(arguments, function(obj){
|
||||
forEach(arguments, function(obj){
|
||||
if (obj !== dst) {
|
||||
foreach(obj, function(value, key){
|
||||
forEach(obj, function(value, key){
|
||||
dst[key] = value;
|
||||
});
|
||||
}
|
||||
@@ -227,13 +210,52 @@ function extend(dst) {
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
function inherit(parent, extra) {
|
||||
return extend(new (extend(function(){}, {prototype:parent}))(), extra);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.noop
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Empty function that performs no operation whatsoever. This function is useful when writing code
|
||||
* in the functional style.
|
||||
<pre>
|
||||
function foo(callback) {
|
||||
var result = calculateResult();
|
||||
(callback || angular.noop)(result);
|
||||
}
|
||||
</pre>
|
||||
*/
|
||||
function noop() {}
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.identity
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* A function that does nothing except for returning its first argument. This function is useful
|
||||
* when writing code in the functional style.
|
||||
*
|
||||
<pre>
|
||||
function transformer(transformationFn, value) {
|
||||
return (transformationFn || identity)(value);
|
||||
};
|
||||
</pre>
|
||||
*/
|
||||
function identity($) {return $;}
|
||||
|
||||
|
||||
function valueFn(value) {return function(){ return value; };}
|
||||
|
||||
function extensionMap(angular, name, transform) {
|
||||
var extPoint;
|
||||
return angular[name] || (extPoint = angular[name] = function (name, fn, prop){
|
||||
@@ -252,20 +274,148 @@ function jqLiteWrap(element) {
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = element;
|
||||
element = new JQLite(div.childNodes);
|
||||
} else if (!(element instanceof JQLite) && isElement(element)) {
|
||||
} else if (!(element instanceof JQLite)) {
|
||||
element = new JQLite(element);
|
||||
}
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.isUndefined
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Checks if a reference is undefined.
|
||||
*
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is undefined.
|
||||
*/
|
||||
function isUndefined(value){ return typeof value == $undefined; }
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.isDefined
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Checks if a reference is defined.
|
||||
*
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is defined.
|
||||
*/
|
||||
function isDefined(value){ return typeof value != $undefined; }
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.isObject
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Checks if a reference is an `Object`. Unlike in JavaScript `null`s are not considered to be
|
||||
* objects.
|
||||
*
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is an `Object` but not `null`.
|
||||
*/
|
||||
function isObject(value){ return value!=_null && typeof value == $object;}
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.isString
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Checks if a reference is a `String`.
|
||||
*
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is a `String`.
|
||||
*/
|
||||
function isString(value){ return typeof value == $string;}
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.isNumber
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Checks if a reference is a `Number`.
|
||||
*
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is a `Number`.
|
||||
*/
|
||||
function isNumber(value){ return typeof value == $number;}
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.isDate
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Checks if value is a date.
|
||||
*
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is a `Date`.
|
||||
*/
|
||||
function isDate(value){ return value instanceof Date; }
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.isArray
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Checks if a reference is an `Array`.
|
||||
*
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is an `Array`.
|
||||
*/
|
||||
function isArray(value) { return value instanceof Array; }
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.isFunction
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Checks if a reference is a `Function`.
|
||||
*
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is a `Function`.
|
||||
*/
|
||||
function isFunction(value){ return typeof value == $function;}
|
||||
function isTextNode(node) { return nodeName(node) == '#text'; }
|
||||
|
||||
|
||||
/**
|
||||
* Checks if `obj` is a window object.
|
||||
*
|
||||
* @private
|
||||
* @param {*} obj Object to check
|
||||
* @returns {boolean} True if `obj` is a window obj.
|
||||
*/
|
||||
function isWindow(obj) {
|
||||
return obj && obj.document && obj.location && obj.alert && obj.setInterval;
|
||||
}
|
||||
|
||||
function isBoolean(value) { return typeof value == $boolean;}
|
||||
function isTextNode(node) { return nodeName_(node) == '#text'; }
|
||||
function trim(value) { return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; }
|
||||
function isElement(node) {
|
||||
return node && (node.nodeName || node instanceof JQLite || (jQuery && node instanceof jQuery));
|
||||
@@ -289,12 +439,12 @@ function HTML(html, option) {
|
||||
}
|
||||
|
||||
if (msie) {
|
||||
nodeName = function(element) {
|
||||
nodeName_ = function(element) {
|
||||
element = element.nodeName ? element : element[0];
|
||||
return (element.scopeName && element.scopeName != 'HTML' ) ? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName;
|
||||
};
|
||||
} else {
|
||||
nodeName = function(element) {
|
||||
nodeName_ = function(element) {
|
||||
return element.nodeName ? element.nodeName : element[0].nodeName;
|
||||
};
|
||||
}
|
||||
@@ -312,13 +462,39 @@ function isVisible(element) {
|
||||
|
||||
function map(obj, iterator, context) {
|
||||
var results = [];
|
||||
foreach(obj, function(value, index, list) {
|
||||
forEach(obj, function(value, index, list) {
|
||||
results.push(iterator.call(context, value, index, list));
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.Object.size
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Determines the number of elements in an array or number of properties of an object.
|
||||
*
|
||||
* Note: this function is used to augment the Object type in angular expressions. See
|
||||
* {@link angular.Object} for more info.
|
||||
*
|
||||
* @param {Object|Array} obj Object or array to inspect.
|
||||
* @returns {number} The size of `obj` or `0` if `obj` is neither an object or an array.
|
||||
*
|
||||
* @example
|
||||
* Number of items in array: {{ [1,2].$size() }}<br/>
|
||||
* Number of items in object: {{ {a:1, b:2, c:3}.$size() }}<br/>
|
||||
*
|
||||
* @scenario
|
||||
it('should print correct sizes for an array and an object', function() {
|
||||
expect(binding('[1,2].$size()')).toBe('2');
|
||||
expect(binding('{a:1, b:2, c:3}.$size()')).toBe('3');
|
||||
});
|
||||
*/
|
||||
function size(obj) {
|
||||
var size = 0;
|
||||
var size = 0, key;
|
||||
if (obj) {
|
||||
if (isNumber(obj.length)) {
|
||||
return obj.length;
|
||||
@@ -356,19 +532,51 @@ function isLeafNode (node) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies stuff.
|
||||
* @ngdoc function
|
||||
* @name angular.Object.copy
|
||||
* @function
|
||||
*
|
||||
* If destination is not provided and source is an object or an array, a copy is created & returned,
|
||||
* otherwise the source is returned.
|
||||
* @description
|
||||
* Creates a deep copy of `source`.
|
||||
*
|
||||
* If destination is provided, all of its properties will be deleted and if source is an object or
|
||||
* an array, all of its members will be copied into the destination object. Finally the destination
|
||||
* is returned just for kicks.
|
||||
* If `source` is an object or an array, all of its members will be copied into the `destination`
|
||||
* object.
|
||||
*
|
||||
* @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
|
||||
* @returns {*}
|
||||
* If `destination` is not provided and `source` is an object or an array, a copy is created &
|
||||
* returned, otherwise the `source` is returned.
|
||||
*
|
||||
* If `destination` is provided, all of its properties will be deleted.
|
||||
*
|
||||
* Note: this function is used to augment the Object type in angular expressions. See
|
||||
* {@link angular.Object} for more info.
|
||||
*
|
||||
* @param {*} source The source to be used to make a copy.
|
||||
* Can be any type including primitives, `null` and `undefined`.
|
||||
* @param {(Object|Array)=} destination Optional destination into which the source is copied.
|
||||
* @returns {*} The copy or updated `destination` if `destination` was specified.
|
||||
*
|
||||
* @example
|
||||
Salutation: <input type="text" name="master.salutation" value="Hello" /><br/>
|
||||
Name: <input type="text" name="master.name" value="world"/><br/>
|
||||
<button ng:click="form = master.$copy()">copy</button>
|
||||
<hr/>
|
||||
|
||||
The master object is <span ng:hide="master.$equals(form)">NOT</span> equal to the form object.
|
||||
|
||||
<pre>master={{master}}</pre>
|
||||
<pre>form={{form}}</pre>
|
||||
|
||||
* @scenario
|
||||
it('should print that initialy the form object is NOT equal to master', function() {
|
||||
expect(element('.doc-example input[name=master.salutation]').val()).toBe('Hello');
|
||||
expect(element('.doc-example input[name=master.name]').val()).toBe('world');
|
||||
expect(element('.doc-example span').css('display')).toBe('inline');
|
||||
});
|
||||
|
||||
it('should make form and master equal when the copy button is clicked', function() {
|
||||
element('.doc-example button').click();
|
||||
expect(element('.doc-example span').css('display')).toBe('none');
|
||||
});
|
||||
*/
|
||||
function copy(source, destination){
|
||||
if (!destination) {
|
||||
@@ -376,7 +584,7 @@ function copy(source, destination){
|
||||
if (source) {
|
||||
if (isArray(source)) {
|
||||
destination = copy(source, []);
|
||||
} else if (source instanceof Date) {
|
||||
} else if (isDate(source)) {
|
||||
destination = new Date(source.getTime());
|
||||
} else if (isObject(source)) {
|
||||
destination = copy(source, {});
|
||||
@@ -391,7 +599,7 @@ function copy(source, destination){
|
||||
destination.push(copy(source[i]));
|
||||
}
|
||||
} else {
|
||||
foreach(destination, function(value, key){
|
||||
forEach(destination, function(value, key){
|
||||
delete destination[key];
|
||||
});
|
||||
for ( var key in source) {
|
||||
@@ -402,8 +610,54 @@ function copy(source, destination){
|
||||
return destination;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.Object.equals
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Determines if two objects or value are equivalent.
|
||||
*
|
||||
* To be equivalent, they must pass `==` comparison or be of the same type and have all their
|
||||
* properties pass `==` comparison. During property comparision properties of `function` type and
|
||||
* properties with name starting with `$` are ignored.
|
||||
*
|
||||
* Supports values types, arrays and objects.
|
||||
*
|
||||
* Note: this function is used to augment the Object type in angular expressions. See
|
||||
* {@link angular.Object} for more info.
|
||||
*
|
||||
* @param {*} o1 Object or value to compare.
|
||||
* @param {*} o2 Object or value to compare.
|
||||
* @returns {boolean} True if arguments are equal.
|
||||
*
|
||||
* @example
|
||||
Salutation: <input type="text" name="greeting.salutation" value="Hello" /><br/>
|
||||
Name: <input type="text" name="greeting.name" value="world"/><br/>
|
||||
<hr/>
|
||||
|
||||
The <code>greeting</code> object is
|
||||
<span ng:hide="greeting.$equals({salutation:'Hello', name:'world'})">NOT</span> equal to
|
||||
<code>{salutation:'Hello', name:'world'}</code>.
|
||||
|
||||
<pre>greeting={{greeting}}</pre>
|
||||
|
||||
@scenario
|
||||
it('should print that initialy greeting is equal to the hardcoded value object', function() {
|
||||
expect(element('.doc-example input[name=greeting.salutation]').val()).toBe('Hello');
|
||||
expect(element('.doc-example input[name=greeting.name]').val()).toBe('world');
|
||||
expect(element('.doc-example span').css('display')).toBe('none');
|
||||
});
|
||||
|
||||
it('should say that the objects are not equal when the form is modified', function() {
|
||||
input('greeting.name').enter('kitty');
|
||||
expect(element('.doc-example span').css('display')).toBe('inline');
|
||||
});
|
||||
*/
|
||||
function equals(o1, o2) {
|
||||
if (o1 == o2) return true;
|
||||
if (o1 === null || o2 === null) return false;
|
||||
var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
|
||||
if (t1 == t2 && t1 == 'object') {
|
||||
if (o1 instanceof Array) {
|
||||
@@ -465,16 +719,33 @@ function concat(array1, array2, index) {
|
||||
return array1.concat(slice.call(array2, index, array2.length));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.bind
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Returns function which calls function `fn` bound to `self` (`self` becomes the `this` for `fn`).
|
||||
* Optional `args` can be supplied which are prebound to the function, also known as
|
||||
* [function currying](http://en.wikipedia.org/wiki/Currying).
|
||||
*
|
||||
* @param {Object} self Context in which `fn` should be evaluated in.
|
||||
* @param {function()} fn Function to be bound.
|
||||
* @param {...*} args Optional arguments to be prebound to the `fn` function call.
|
||||
* @returns {function()} Function that wraps the `fn` with all the specified bindings.
|
||||
*/
|
||||
function bind(self, fn) {
|
||||
var curryArgs = arguments.length > 2 ? slice.call(arguments, 2, arguments.length) : [];
|
||||
if (typeof fn == $function) {
|
||||
if (typeof fn == $function && !(fn instanceof RegExp)) {
|
||||
return curryArgs.length ? function() {
|
||||
return arguments.length ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0, arguments.length))) : fn.apply(self, curryArgs);
|
||||
}: function() {
|
||||
return arguments.length ? fn.apply(self, arguments) : fn.call(self);
|
||||
};
|
||||
} else {
|
||||
// in IE, native methods ore not functions and so they can not be bound (but they don't need to be)
|
||||
// in IE, native methods are not functions and so they can not be bound (but they don't need to be)
|
||||
return fn;
|
||||
}
|
||||
}
|
||||
@@ -502,10 +773,31 @@ function merge(src, dst) {
|
||||
}
|
||||
}
|
||||
|
||||
function compile(element, existingScope) {
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.compile
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Compiles a piece of HTML or DOM into a {@link angular.scope scope} object.
|
||||
<pre>
|
||||
var scope1 = angular.compile(window.document);
|
||||
scope1.$init();
|
||||
|
||||
var scope2 = angular.compile('<div ng:click="clicked = true">click me</div>');
|
||||
scope2.$init();
|
||||
</pre>
|
||||
*
|
||||
* @param {string|DOMElement} element Element to compile.
|
||||
* @param {Object=} parentScope Scope to become the parent scope of the newly compiled scope.
|
||||
* @returns {Object} Compiled scope object.
|
||||
*/
|
||||
function compile(element, parentScope) {
|
||||
var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget),
|
||||
$element = jqLite(element);
|
||||
return compiler.compile($element)($element, existingScope);
|
||||
return compiler.compile($element)($element, parentScope);
|
||||
}
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
@@ -515,7 +807,7 @@ function compile(element, existingScope) {
|
||||
*/
|
||||
function parseKeyValue(/**string*/keyValue) {
|
||||
var obj = {}, key_value, key;
|
||||
foreach((keyValue || "").split('&'), function(keyValue){
|
||||
forEach((keyValue || "").split('&'), function(keyValue){
|
||||
if (keyValue) {
|
||||
key_value = keyValue.split('=');
|
||||
key = unescape(key_value[0]);
|
||||
@@ -527,17 +819,168 @@ function parseKeyValue(/**string*/keyValue) {
|
||||
|
||||
function toKeyValue(obj) {
|
||||
var parts = [];
|
||||
foreach(obj, function(value, key) {
|
||||
forEach(obj, function(value, key) {
|
||||
parts.push(escape(key) + (value === true ? '' : '=' + escape(value)));
|
||||
});
|
||||
return parts.length ? parts.join('&') : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc directive
|
||||
* @name angular.directive.ng:autobind
|
||||
* @element script
|
||||
*
|
||||
* @TODO ng:autobind is not a directive!! it should be documented as bootstrap parameter in a
|
||||
* separate bootstrap section.
|
||||
* @TODO rename to ng:autobind to ng:autoboot
|
||||
*
|
||||
* @description
|
||||
* This section explains how to bootstrap your application with angular using either the angular
|
||||
* javascript file.
|
||||
*
|
||||
*
|
||||
* ## The angular distribution
|
||||
* Note that there are two versions of the angular javascript file that you can use:
|
||||
*
|
||||
* * `angular.js` - the development version - this file is unobfuscated, uncompressed, and thus
|
||||
* human-readable and useful when developing your angular applications.
|
||||
* * `angular.min.js` - the production version - this is a minified and obfuscated version of
|
||||
* `angular.js`. You want to use this version when you want to load a smaller but functionally
|
||||
* equivalent version of the code in your application. We use the Closure compiler to create this
|
||||
* file.
|
||||
*
|
||||
*
|
||||
* ## Auto-bootstrap with `ng:autobind`
|
||||
* The simplest way to get an <angular/> application up and running is by inserting a script tag in
|
||||
* your HTML file that bootstraps the `http://code.angularjs.org/angular-x.x.x.min.js` code and uses
|
||||
* the special `ng:autobind` attribute, like in this snippet of HTML:
|
||||
*
|
||||
* <pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<script type="text/javascript" src="http://code.angularjs.org/angular-0.9.3.min.js"
|
||||
ng:autobind></script>
|
||||
</head>
|
||||
<body>
|
||||
Hello {{'world'}}!
|
||||
</body>
|
||||
</html>
|
||||
* </pre>
|
||||
*
|
||||
* The `ng:autobind` attribute tells <angular/> to compile and manage the whole HTML document. The
|
||||
* compilation occurs in the page's `onLoad` handler. Note that you don't need to explicitly add an
|
||||
* `onLoad` event; auto bind mode takes care of all the magic for you.
|
||||
*
|
||||
*
|
||||
* ## Auto-bootstrap with `#autobind`
|
||||
* In rare cases when you can't define the `ng` namespace before the script tag (e.g. in some CMS
|
||||
* systems, etc), it is possible to auto-bootstrap angular by appending `#autobind` to the script
|
||||
* src URL, like in this snippet:
|
||||
*
|
||||
* <pre>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript"
|
||||
src="http://code.angularjs.org/angular-0.9.3.min.js#autobind"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div xmlns:ng="http://angularjs.org">
|
||||
Hello {{'world'}}!
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
* </pre>
|
||||
*
|
||||
* In this case it's the `#autobind` URL fragment that tells angular to auto-bootstrap.
|
||||
*
|
||||
*
|
||||
* ## Filename Restrictions for Auto-bootstrap
|
||||
* In order for us to find the auto-bootstrap script attribute or URL fragment, the value of the
|
||||
* `script` `src` attribute that loads angular script must match one of these naming
|
||||
* conventions:
|
||||
*
|
||||
* - `angular.js`
|
||||
* - `angular-min.js`
|
||||
* - `angular-x.x.x.js`
|
||||
* - `angular-x.x.x.min.js`
|
||||
* - `angular-x.x.x-xxxxxxxx.js` (dev snapshot)
|
||||
* - `angular-x.x.x-xxxxxxxx.min.js` (dev snapshot)
|
||||
* - `angular-bootstrap.js` (used for development of angular)
|
||||
*
|
||||
* Optionally, any of the filename format above can be prepended with relative or absolute URL that
|
||||
* ends with `/`.
|
||||
*
|
||||
*
|
||||
* ## Manual Bootstrap
|
||||
* Using auto-bootstrap is a handy way to start using <angular/>, but advanced users who want more
|
||||
* control over the initialization process might prefer to use manual bootstrap instead.
|
||||
*
|
||||
* The best way to get started with manual bootstraping is to look at the magic behind `ng:autobind`
|
||||
* by writing out each step of the autobind process explicitly. Note that the following code is
|
||||
* equivalent to the code in the previous section.
|
||||
*
|
||||
* <pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<script type="text/javascript" src="http://code.angularjs.org/angular-0.9.3.min.js"
|
||||
ng:autobind></script>
|
||||
<script type="text/javascript">
|
||||
(function(window, previousOnLoad){
|
||||
window.onload = function(){
|
||||
try { (previousOnLoad||angular.noop)(); } catch(e) {}
|
||||
angular.compile(window.document).$init();
|
||||
};
|
||||
})(window, window.onload);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Hello {{'World'}}!
|
||||
</body>
|
||||
</html>
|
||||
* </pre>
|
||||
*
|
||||
* This is the sequence that your code should follow if you're bootstrapping angular on your own:
|
||||
*
|
||||
* * After the page is loaded, find the root of the HTML template, which is typically the root of
|
||||
* the document.
|
||||
* * Run the HTML compiler, which converts the templates into an executable, bi-directionally bound
|
||||
* application.
|
||||
*
|
||||
*
|
||||
* ##XML Namespace
|
||||
* *IMPORTANT:* When using <angular/> you must declare the ng namespace using the xmlns tag. If you
|
||||
* don't declare the namespace, Internet Explorer does not render widgets properly.
|
||||
*
|
||||
* <pre>
|
||||
* <html xmlns:ng="http://angularjs.org">
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* ## Create your own namespace
|
||||
* If you want to define your own widgets, you must create your own namespace and use that namespace
|
||||
* to form the fully qualified widget name. For example, you could map the alias `my` to your domain
|
||||
* and create a widget called my:widget. To create your own namespace, simply add another xmlsn tag
|
||||
* to your page, create an alias, and set it to your unique domain:
|
||||
*
|
||||
* <pre>
|
||||
* <html xmlns:ng="http://angularjs.org" xmlns:my="http://mydomain.com">
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* ## Global Object
|
||||
* The <angular/> script creates a single global variable `angular` in the global namespace. All
|
||||
* APIs are bound to fields of this global object.
|
||||
*
|
||||
*/
|
||||
function angularInit(config){
|
||||
if (config.autobind) {
|
||||
// TODO default to the source of angular.js
|
||||
var scope = compile(window.document, _null, {'$config':config}),
|
||||
$browser = scope.$inject('$browser');
|
||||
$browser = scope.$service('$browser');
|
||||
|
||||
if (config.css)
|
||||
$browser.addCss(config.base_url + config.css);
|
||||
|
||||
+19
-9
@@ -1,17 +1,27 @@
|
||||
var browserSingleton;
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
* @name angular.service.$browser
|
||||
* @requires $log
|
||||
*
|
||||
* @description
|
||||
* Represents the browser.
|
||||
*/
|
||||
angularService('$browser', function($log){
|
||||
if (!browserSingleton) {
|
||||
browserSingleton = new Browser(
|
||||
window.location,
|
||||
jqLite(window.document),
|
||||
jqLite(window.document.getElementsByTagName('head')[0]),
|
||||
XHR,
|
||||
$log);
|
||||
browserSingleton.startPoller(50, function(delay, fn){setTimeout(delay,fn);});
|
||||
browserSingleton = new Browser(window, jqLite(window.document), jqLite(window.document.body),
|
||||
XHR, $log);
|
||||
var addPollFn = browserSingleton.addPollFn;
|
||||
browserSingleton.addPollFn = function(){
|
||||
browserSingleton.addPollFn = addPollFn;
|
||||
browserSingleton.startPoller(100, function(delay, fn){setTimeout(delay,fn);});
|
||||
return addPollFn.apply(browserSingleton, arguments);
|
||||
};
|
||||
browserSingleton.bind();
|
||||
}
|
||||
return browserSingleton;
|
||||
}, {inject:['$log']});
|
||||
}, {$inject:['$log']});
|
||||
|
||||
extend(angular, {
|
||||
'element': jqLite,
|
||||
@@ -20,7 +30,7 @@ extend(angular, {
|
||||
'copy': copy,
|
||||
'extend': extend,
|
||||
'equals': equals,
|
||||
'foreach': foreach,
|
||||
'forEach': forEach,
|
||||
'injector': createInjector,
|
||||
'noop':noop,
|
||||
'bind':bind,
|
||||
|
||||
+251
-45
@@ -8,8 +8,29 @@ var XHR = window.XMLHttpRequest || function () {
|
||||
throw new Error("This browser does not support XMLHttpRequest.");
|
||||
};
|
||||
|
||||
function Browser(location, document, head, XHR, $log) {
|
||||
var self = this;
|
||||
/**
|
||||
* @private
|
||||
* @name Browser
|
||||
*
|
||||
* @description
|
||||
* Constructor for the object exposed as $browser service.
|
||||
*
|
||||
* This object has two goals:
|
||||
*
|
||||
* - hide all the global state in the browser caused by the window object
|
||||
* - abstract away all the browser specific features and inconsistencies
|
||||
*
|
||||
* @param {object} window The global window object.
|
||||
* @param {object} document jQuery wrapped document.
|
||||
* @param {object} body jQuery wrapped document.body.
|
||||
* @param {function()} XHR XMLHttpRequest constructor.
|
||||
* @param {object} $log console.log or an object with the same interface.
|
||||
*/
|
||||
function Browser(window, document, body, XHR, $log) {
|
||||
var self = this,
|
||||
location = window.location,
|
||||
setTimeout = window.setTimeout;
|
||||
|
||||
self.isMock = false;
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
@@ -19,7 +40,43 @@ function Browser(location, document, head, XHR, $log) {
|
||||
var outstandingRequestCount = 0;
|
||||
var outstandingRequestCallbacks = [];
|
||||
|
||||
self.xhr = function(method, url, post, callback){
|
||||
|
||||
/**
|
||||
* Executes the `fn` function (supports currying) and decrements the `outstandingRequestCallbacks`
|
||||
* counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
|
||||
*/
|
||||
function completeOutstandingRequest(fn) {
|
||||
try {
|
||||
fn.apply(null, slice.call(arguments, 1));
|
||||
} finally {
|
||||
outstandingRequestCount--;
|
||||
if (outstandingRequestCount === 0) {
|
||||
while(outstandingRequestCallbacks.length) {
|
||||
try {
|
||||
outstandingRequestCallbacks.pop()();
|
||||
} catch (e) {
|
||||
$log.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#xhr
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @param {string} method Requested method (get|post|put|delete|head|json)
|
||||
* @param {string} url Requested url
|
||||
* @param {string=} post Post data to send
|
||||
* @param {function(number, string)} callback Function that will be called on response
|
||||
*
|
||||
* @description
|
||||
* Send ajax request
|
||||
*/
|
||||
self.xhr = function(method, url, post, callback) {
|
||||
if (isFunction(post)) {
|
||||
callback = post;
|
||||
post = _null;
|
||||
@@ -34,7 +91,7 @@ function Browser(location, document, head, XHR, $log) {
|
||||
window[callbackId] = _undefined;
|
||||
callback(200, data);
|
||||
};
|
||||
head.append(script);
|
||||
body.append(script);
|
||||
} else {
|
||||
var xhr = new XHR();
|
||||
xhr.open(method, url, true);
|
||||
@@ -44,26 +101,22 @@ function Browser(location, document, head, XHR, $log) {
|
||||
outstandingRequestCount ++;
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == 4) {
|
||||
try {
|
||||
callback(xhr.status || 200, xhr.responseText);
|
||||
} finally {
|
||||
outstandingRequestCount--;
|
||||
if (outstandingRequestCount === 0) {
|
||||
while(outstandingRequestCallbacks.length) {
|
||||
try {
|
||||
outstandingRequestCallbacks.pop()();
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
completeOutstandingRequest(callback, xhr.status || 200, xhr.responseText);
|
||||
}
|
||||
};
|
||||
xhr.send(post || '');
|
||||
}
|
||||
};
|
||||
|
||||
self.notifyWhenNoOutstandingRequests = function(callback){
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#notifyWhenNoOutstandingRequests
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @param {function()} callback Function that will be called when no outstanding request
|
||||
*/
|
||||
self.notifyWhenNoOutstandingRequests = function(callback) {
|
||||
if (outstandingRequestCount === 0) {
|
||||
callback();
|
||||
} else {
|
||||
@@ -75,27 +128,51 @@ function Browser(location, document, head, XHR, $log) {
|
||||
// Poll Watcher API
|
||||
//////////////////////////////////////////////////////////////
|
||||
var pollFns = [];
|
||||
function poll(){
|
||||
foreach(pollFns, function(pollFn){ pollFn(); });
|
||||
}
|
||||
self.poll = poll;
|
||||
|
||||
/**
|
||||
* Adds a function to the list of functions that poller periodically executes
|
||||
* @return {Function} the added function
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#poll
|
||||
* @methodOf angular.service.$browser
|
||||
*/
|
||||
self.addPollFn = function(/**Function*/fn){
|
||||
self.poll = function() {
|
||||
forEach(pollFns, function(pollFn){ pollFn(); });
|
||||
};
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#addPollFn
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @param {function()} fn Poll function to add
|
||||
*
|
||||
* @description
|
||||
* Adds a function to the list of functions that poller periodically executes
|
||||
*
|
||||
* @returns {function()} the added function
|
||||
*/
|
||||
self.addPollFn = function(fn) {
|
||||
pollFns.push(fn);
|
||||
return fn;
|
||||
};
|
||||
|
||||
/**
|
||||
* Configures the poller to run in the specified intervals, using the specified setTimeout fn and
|
||||
* kicks it off.
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#startPoller
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @param {number} interval How often should browser call poll functions (ms)
|
||||
* @param {function()} setTimeout Reference to a real or fake `setTimeout` function.
|
||||
*
|
||||
* @description
|
||||
* Configures the poller to run in the specified intervals, using the specified
|
||||
* setTimeout fn and kicks it off.
|
||||
*/
|
||||
self.startPoller = function(/**number*/interval, /**Function*/setTimeout){
|
||||
self.startPoller = function(interval, setTimeout) {
|
||||
(function check(){
|
||||
poll();
|
||||
self.poll();
|
||||
setTimeout(check, interval);
|
||||
})();
|
||||
};
|
||||
@@ -103,15 +180,74 @@ function Browser(location, document, head, XHR, $log) {
|
||||
//////////////////////////////////////////////////////////////
|
||||
// URL API
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#setUrl
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @param {string} url New url
|
||||
*
|
||||
* @description
|
||||
* Sets browser's url
|
||||
*/
|
||||
self.setUrl = function(url) {
|
||||
var existingURL = location.href;
|
||||
if (!existingURL.match(/#/)) existingURL += '#';
|
||||
if (!url.match(/#/)) url += '#';
|
||||
location.href = url;
|
||||
};
|
||||
self.getUrl = function() {
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#getUrl
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @description
|
||||
* Get current browser's url
|
||||
*
|
||||
* @returns {string} Browser's url
|
||||
*/
|
||||
self.getUrl = function() {
|
||||
return location.href;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#onHashChange
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @description
|
||||
* Detects if browser support onhashchange events and register a listener otherwise registers
|
||||
* $browser poller. The `listener` will then get called when the hash changes.
|
||||
*
|
||||
* The listener gets called with either HashChangeEvent object or simple object that also contains
|
||||
* `oldURL` and `newURL` properties.
|
||||
*
|
||||
* NOTE: this is a api is intended for sole use by $location service. Please use
|
||||
* {@link angular.service.$location $location service} to monitor hash changes in angular apps.
|
||||
*
|
||||
* @param {function(event)} listener Listener function to be called when url hash changes.
|
||||
* @return {function()} Returns the registered listener fn - handy if the fn is anonymous.
|
||||
*/
|
||||
self.onHashChange = function(listener) {
|
||||
if ('onhashchange' in window) {
|
||||
jqLite(window).bind('hashchange', listener);
|
||||
} else {
|
||||
var lastBrowserUrl = self.getUrl();
|
||||
|
||||
self.addPollFn(function() {
|
||||
if (lastBrowserUrl != self.getUrl()) {
|
||||
listener();
|
||||
}
|
||||
});
|
||||
}
|
||||
return listener;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Cookies API
|
||||
@@ -121,19 +257,28 @@ function Browser(location, document, head, XHR, $log) {
|
||||
var lastCookieString = '';
|
||||
|
||||
/**
|
||||
* The cookies method provides a 'private' low level access to browser cookies. It is not meant to
|
||||
* be used directly, use the $cookie service instead.
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#cookies
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @param {string=} name Cookie name
|
||||
* @param {string=} value Cokkie value
|
||||
*
|
||||
* @description
|
||||
* The cookies method provides a 'private' low level access to browser cookies.
|
||||
* It is not meant to be used directly, use the $cookie service instead.
|
||||
*
|
||||
* The return values vary depending on the arguments that the method was called with as follows:
|
||||
* <ul><li>
|
||||
* cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify it
|
||||
* </li><li>
|
||||
* cookies(name, value) -> set name to value, if value is undefined delete the cookie
|
||||
* </li><li>
|
||||
* cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that way)
|
||||
* </li></ul>
|
||||
* <ul>
|
||||
* <li>cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify it</li>
|
||||
* <li>cookies(name, value) -> set name to value, if value is undefined delete the cookie</li>
|
||||
* <li>cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that way)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @returns {Object} Hash of all cookies (if called without any parameter)
|
||||
*/
|
||||
self.cookies = function (/**string*/name, /**string*/value){
|
||||
self.cookies = function (name, value) {
|
||||
var cookieLength, cookieArray, i, keyValue;
|
||||
|
||||
if (name) {
|
||||
@@ -171,11 +316,56 @@ function Browser(location, document, head, XHR, $log) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#defer
|
||||
* @methodOf angular.service.$browser
|
||||
* @param {function()} fn A function, who's execution should be defered.
|
||||
* @param {int=} [delay=0] of milliseconds to defer the function execution.
|
||||
*
|
||||
* @description
|
||||
* Executes a fn asynchroniously via `setTimeout(fn, delay)`.
|
||||
*
|
||||
* Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
|
||||
* `setTimeout` in tests, the fns are queued in an array, which can be programaticaly flushed via
|
||||
* `$browser.defer.flush()`.
|
||||
*
|
||||
*/
|
||||
self.defer = function(fn, delay) {
|
||||
outstandingRequestCount++;
|
||||
setTimeout(function() { completeOutstandingRequest(fn); }, delay || 0);
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Misc API
|
||||
//////////////////////////////////////////////////////////////
|
||||
var hoverListener = noop;
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#hover
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @description
|
||||
* Set hover listener.
|
||||
*
|
||||
* @param {function(Object, boolean)} listener Function that will be called when hover event
|
||||
* occurs.
|
||||
*/
|
||||
self.hover = function(listener) { hoverListener = listener; };
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#bind
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @description
|
||||
* Register hover function to real browser
|
||||
*/
|
||||
self.bind = function() {
|
||||
document.bind("mouseover", function(event){
|
||||
hoverListener(jqLite(msie ? event.srcElement : event.target), true);
|
||||
@@ -189,25 +379,41 @@ function Browser(location, document, head, XHR, $log) {
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#addCss
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @param {string} url Url to css file
|
||||
* @description
|
||||
* Adds a stylesheet tag to the head.
|
||||
*/
|
||||
self.addCss = function(/**string*/url) {
|
||||
self.addCss = function(url) {
|
||||
var link = jqLite(rawDocument.createElement('link'));
|
||||
link.attr('rel', 'stylesheet');
|
||||
link.attr('type', 'text/css');
|
||||
link.attr('href', url);
|
||||
head.append(link);
|
||||
body.append(link);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#addJs
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @param {string} url Url to js file
|
||||
* @param {string=} dom_id Optional id for the script tag
|
||||
*
|
||||
* @description
|
||||
* Adds a script tag to the head.
|
||||
*/
|
||||
self.addJs = function(/**string*/url, /**string*/dom_id) {
|
||||
self.addJs = function(url, dom_id) {
|
||||
var script = jqLite(rawDocument.createElement('script'));
|
||||
script.attr('type', 'text/javascript');
|
||||
script.attr('src', url);
|
||||
if (dom_id) script.attr('id', dom_id);
|
||||
head.append(script);
|
||||
body.append(script);
|
||||
};
|
||||
}
|
||||
|
||||
+79
-13
@@ -16,8 +16,8 @@ Template.prototype = {
|
||||
init: function(element, scope) {
|
||||
var inits = {};
|
||||
this.collectInits(element, inits, scope);
|
||||
foreachSorted(inits, function(queue){
|
||||
foreach(queue, function(fn) {fn();});
|
||||
forEachSorted(inits, function(queue){
|
||||
forEach(queue, function(fn) {fn();});
|
||||
});
|
||||
},
|
||||
|
||||
@@ -30,11 +30,12 @@ Template.prototype = {
|
||||
if (this.newScope) {
|
||||
childScope = createScope(scope);
|
||||
scope.$onEval(childScope.$eval);
|
||||
element.data($$scope, childScope);
|
||||
}
|
||||
foreach(this.inits, function(fn) {
|
||||
forEach(this.inits, function(fn) {
|
||||
queue.push(function() {
|
||||
childScope.$tryEval(function(){
|
||||
return childScope.$inject(fn, childScope, element);
|
||||
return childScope.$service(fn, childScope, element);
|
||||
}, element);
|
||||
});
|
||||
});
|
||||
@@ -68,6 +69,18 @@ Template.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Function walks up the element chain looking for the scope associated with the give element.
|
||||
*/
|
||||
function retrieveScope(element) {
|
||||
var scope;
|
||||
element = jqLite(element);
|
||||
while (element && element.length && !(scope = element.data($$scope))) {
|
||||
element = element.parent();
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
|
||||
///////////////////////////////////
|
||||
//Compiler
|
||||
//////////////////////////////////
|
||||
@@ -79,24 +92,25 @@ function Compiler(markup, attrMarkup, directives, widgets){
|
||||
}
|
||||
|
||||
Compiler.prototype = {
|
||||
compile: function(rawElement) {
|
||||
rawElement = jqLite(rawElement);
|
||||
compile: function(element) {
|
||||
element = jqLite(element);
|
||||
var index = 0,
|
||||
template,
|
||||
parent = rawElement.parent();
|
||||
parent = element.parent();
|
||||
if (parent && parent[0]) {
|
||||
parent = parent[0];
|
||||
for(var i = 0; i < parent.childNodes.length; i++) {
|
||||
if (parent.childNodes[i] == rawElement[0]) {
|
||||
if (parent.childNodes[i] == element[0]) {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
template = this.templatize(rawElement, index, 0) || new Template();
|
||||
template = this.templatize(element, index, 0) || new Template();
|
||||
return function(element, parentScope){
|
||||
element = jqLite(element);
|
||||
var scope = parentScope && parentScope.$eval ?
|
||||
parentScope : createScope(parentScope);
|
||||
element.data($$scope, scope);
|
||||
return extend(scope, {
|
||||
$element:element,
|
||||
$init: function() {
|
||||
@@ -109,6 +123,58 @@ Compiler.prototype = {
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc directive
|
||||
* @name angular.directive.ng:eval-order
|
||||
*
|
||||
* @description
|
||||
* Normally the view is updated from top to bottom. This usually is
|
||||
* not a problem, but under some circumstances the values for data
|
||||
* is not available until after the full view is computed. If such
|
||||
* values are needed before they are computed the order of
|
||||
* evaluation can be change using ng:eval-order
|
||||
*
|
||||
* @element ANY
|
||||
* @param {integer|string=} [priority=0] priority integer, or FIRST, LAST constant
|
||||
*
|
||||
* @exampleDescription
|
||||
* try changing the invoice and see that the Total will lag in evaluation
|
||||
* @example
|
||||
<div>TOTAL: without ng:eval-order {{ items.$sum('total') | currency }}</div>
|
||||
<div ng:eval-order='LAST'>TOTAL: with ng:eval-order {{ items.$sum('total') | currency }}</div>
|
||||
<table ng:init="items=[{qty:1, cost:9.99, desc:'gadget'}]">
|
||||
<tr>
|
||||
<td>QTY</td>
|
||||
<td>Description</td>
|
||||
<td>Cost</td>
|
||||
<td>Total</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr ng:repeat="item in items">
|
||||
<td><input name="item.qty"/></td>
|
||||
<td><input name="item.desc"/></td>
|
||||
<td><input name="item.cost"/></td>
|
||||
<td>{{item.total = item.qty * item.cost | currency}}</td>
|
||||
<td><a href="" ng:click="items.$remove(item)">X</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3"><a href="" ng:click="items.$add()">add</a></td>
|
||||
<td>{{ items.$sum('total') | currency }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
*
|
||||
* @scenario
|
||||
it('should check ng:format', function(){
|
||||
expect(using('.doc-example-live div:first').binding("items.$sum('total')")).toBe('$9.99');
|
||||
expect(using('.doc-example-live div:last').binding("items.$sum('total')")).toBe('$9.99');
|
||||
input('item.qty').enter('2');
|
||||
expect(using('.doc-example-live div:first').binding("items.$sum('total')")).toBe('$9.99');
|
||||
expect(using('.doc-example-live div:last').binding("items.$sum('total')")).toBe('$19.98');
|
||||
});
|
||||
*/
|
||||
|
||||
templatize: function(element, elementIndex, priority){
|
||||
var self = this,
|
||||
widget,
|
||||
@@ -116,7 +182,7 @@ Compiler.prototype = {
|
||||
directiveFns = self.directives,
|
||||
descend = true,
|
||||
directives = true,
|
||||
elementName = nodeName(element),
|
||||
elementName = nodeName_(element),
|
||||
template,
|
||||
selfApi = {
|
||||
compile: bind(self, self.compile),
|
||||
@@ -166,7 +232,7 @@ Compiler.prototype = {
|
||||
for(var i=0, child=element[0].childNodes;
|
||||
i<child.length; i++) {
|
||||
if (isTextNode(child[i])) {
|
||||
foreach(self.markup, function(markup){
|
||||
forEach(self.markup, function(markup){
|
||||
if (i<child.length) {
|
||||
var textNode = jqLite(child[i]);
|
||||
markup.call(selfApi, textNode.text(), textNode, element);
|
||||
@@ -179,7 +245,7 @@ Compiler.prototype = {
|
||||
if (directives) {
|
||||
// Process attributes/directives
|
||||
eachAttribute(element, function(value, name){
|
||||
foreach(self.attrMarkup, function(markup){
|
||||
forEach(self.attrMarkup, function(markup){
|
||||
markup.call(selfApi, value, name, element);
|
||||
});
|
||||
});
|
||||
@@ -221,6 +287,6 @@ function eachAttribute(element, fn){
|
||||
}
|
||||
attrValue[name] = value;
|
||||
}
|
||||
foreachSorted(attrValue, fn);
|
||||
forEachSorted(attrValue, fn);
|
||||
}
|
||||
|
||||
|
||||
+36
-17
@@ -1,9 +1,20 @@
|
||||
/**
|
||||
* Create an inject method
|
||||
* @param providerScope provider's "this"
|
||||
* @param providers a function(name) which returns provider function
|
||||
* @param cache place where instances are saved for reuse
|
||||
* @returns {Function}
|
||||
* @ngdoc function
|
||||
* @name angular.injector
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Creates an inject function that can be used for dependency injection.
|
||||
*
|
||||
* @param {Object=} [providerScope={}] provider's `this`
|
||||
* @param {Object.<string, function()>=} [providers=angular.service] Map of provider (factory)
|
||||
* function.
|
||||
* @param {Object.<string, function()>=} [cache={}] Place where instances are saved for reuse. Can
|
||||
* also be used to override services speciafied by `providers` (useful in tests).
|
||||
* @returns {function()} Injector function.
|
||||
*
|
||||
* @TODO These docs need a lot of work. Specifically the returned function should be described in
|
||||
* great detail + we need to provide some examples.
|
||||
*/
|
||||
function createInjector(providerScope, providers, cache) {
|
||||
providers = providers || angularService;
|
||||
@@ -19,13 +30,14 @@ function createInjector(providerScope, providers, cache) {
|
||||
* string: return an instance for the injection key.
|
||||
* array of keys: returns an array of instances.
|
||||
* function: look at $inject property of function to determine instances
|
||||
* and then call the function with instances and scope. Any
|
||||
* additional arguments are passed on to function.
|
||||
* and then call the function with instances and `scope`. Any
|
||||
* additional arguments (`args`) are appended to the function
|
||||
* arguments.
|
||||
* object: initialize eager providers and publish them the ones with publish here.
|
||||
* none: same as object but use providerScope as place to publish.
|
||||
*/
|
||||
return function inject(value, scope, args){
|
||||
var returnValue, provider, creation;
|
||||
var returnValue, provider;
|
||||
if (isString(value)) {
|
||||
if (!cache.hasOwnProperty(value)) {
|
||||
provider = providers[value];
|
||||
@@ -35,25 +47,32 @@ function createInjector(providerScope, providers, cache) {
|
||||
returnValue = cache[value];
|
||||
} else if (isArray(value)) {
|
||||
returnValue = [];
|
||||
foreach(value, function(name) {
|
||||
forEach(value, function(name) {
|
||||
returnValue.push(inject(name));
|
||||
});
|
||||
} else if (isFunction(value)) {
|
||||
returnValue = inject(value.$inject || []);
|
||||
returnValue = value.apply(scope, concat(returnValue, arguments, 2));
|
||||
} else if (isObject(value)) {
|
||||
foreach(providers, function(provider, name){
|
||||
creation = provider.$creation;
|
||||
if (creation == 'eager') {
|
||||
forEach(providers, function(provider, name){
|
||||
if (provider.$eager)
|
||||
inject(name);
|
||||
}
|
||||
if (creation == 'eager-published') {
|
||||
setter(value, name, inject(name));
|
||||
}
|
||||
|
||||
if (provider.$creation)
|
||||
throw new Error("Failed to register service '" + name +
|
||||
"': $creation property is unsupported. Use $eager:true or see release notes.");
|
||||
});
|
||||
} else {
|
||||
returnValue = inject(providerScope);
|
||||
}
|
||||
return returnValue;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function injectService(services, fn) {
|
||||
return extend(fn, {$inject:services});;
|
||||
}
|
||||
|
||||
function injectUpdateView(fn) {
|
||||
return injectService(['$updateView'], fn);
|
||||
}
|
||||
|
||||
+75
-19
@@ -1,61 +1,117 @@
|
||||
var array = [].constructor;
|
||||
|
||||
function toJson(obj, pretty){
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.toJson
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Serializes the input into a JSON formated string.
|
||||
*
|
||||
* @param {Object|Array|Date|string|number} obj Input to jsonify.
|
||||
* @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace.
|
||||
* @returns {string} Jsonified string representing `obj`.
|
||||
*/
|
||||
function toJson(obj, pretty) {
|
||||
var buf = [];
|
||||
toJsonArray(buf, obj, pretty ? "\n " : _null, []);
|
||||
return buf.join('');
|
||||
}
|
||||
|
||||
function fromJson(json) {
|
||||
if (!json) return json;
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.fromJson
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Deserializes a string in the JSON format.
|
||||
*
|
||||
* @param {string} json JSON string to deserialize.
|
||||
* @param {boolean} [useNative=false] Use native JSON parser if available
|
||||
* @returns {Object|Array|Date|string|number} Deserialized thingy.
|
||||
*/
|
||||
function fromJson(json, useNative) {
|
||||
if (!isString(json)) return json;
|
||||
|
||||
var obj, p, expression;
|
||||
|
||||
try {
|
||||
var p = parser(json, true);
|
||||
var expression = p.primary();
|
||||
if (useNative && JSON && JSON.parse) {
|
||||
obj = JSON.parse(json);
|
||||
return transformDates(obj);
|
||||
}
|
||||
|
||||
p = parser(json, true);
|
||||
expression = p.primary();
|
||||
p.assertAllConsumed();
|
||||
return expression();
|
||||
|
||||
} catch (e) {
|
||||
error("fromJson error: ", json, e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
// TODO make forEach optionally recursive and remove this function
|
||||
function transformDates(obj) {
|
||||
if (isString(obj) && obj.length === DATE_ISOSTRING_LN) {
|
||||
return angularString.toDate(obj);
|
||||
} else if (isArray(obj) || isObject(obj)) {
|
||||
forEach(obj, function(val, name) {
|
||||
obj[name] = transformDates(val);
|
||||
});
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
angular['toJson'] = toJson;
|
||||
angular['fromJson'] = fromJson;
|
||||
|
||||
function toJsonArray(buf, obj, pretty, stack){
|
||||
if (typeof obj == "object") {
|
||||
function toJsonArray(buf, obj, pretty, stack) {
|
||||
if (isObject(obj)) {
|
||||
if (obj === window) {
|
||||
buf.push('WINDOW');
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj === document) {
|
||||
buf.push('DOCUMENT');
|
||||
return;
|
||||
}
|
||||
|
||||
if (includes(stack, obj)) {
|
||||
buf.push("RECURSION");
|
||||
buf.push('RECURSION');
|
||||
return;
|
||||
}
|
||||
stack.push(obj);
|
||||
}
|
||||
var type = typeof obj;
|
||||
if (obj === _null) {
|
||||
buf.push($null);
|
||||
} else if (obj instanceof RegExp) {
|
||||
buf.push(angular['String']['quoteUnicode'](obj.toString()));
|
||||
} else if (type === $function) {
|
||||
} else if (isFunction(obj)) {
|
||||
return;
|
||||
} else if (type === $boolean) {
|
||||
} else if (isBoolean(obj)) {
|
||||
buf.push('' + obj);
|
||||
} else if (type === $number) {
|
||||
} else if (isNumber(obj)) {
|
||||
if (isNaN(obj)) {
|
||||
buf.push($null);
|
||||
} else {
|
||||
buf.push('' + obj);
|
||||
}
|
||||
} else if (type === $string) {
|
||||
} else if (isString(obj)) {
|
||||
return buf.push(angular['String']['quoteUnicode'](obj));
|
||||
} else if (type === $object) {
|
||||
if (obj instanceof Array) {
|
||||
} else if (isObject(obj)) {
|
||||
if (isArray(obj)) {
|
||||
buf.push("[");
|
||||
var len = obj.length;
|
||||
var sep = false;
|
||||
for(var i=0; i<len; i++) {
|
||||
var item = obj[i];
|
||||
if (sep) buf.push(",");
|
||||
if (!(item instanceof RegExp) && (typeof item == $function || typeof item == $undefined)) {
|
||||
if (!(item instanceof RegExp) && (isFunction(item) || isUndefined(item))) {
|
||||
buf.push($null);
|
||||
} else {
|
||||
toJsonArray(buf, item, pretty, stack);
|
||||
@@ -63,7 +119,7 @@ function toJsonArray(buf, obj, pretty, stack){
|
||||
sep = true;
|
||||
}
|
||||
buf.push("]");
|
||||
} else if (obj instanceof Date) {
|
||||
} else if (isDate(obj)) {
|
||||
buf.push(angular['String']['quoteUnicode'](angular['Date']['toString'](obj)));
|
||||
} else {
|
||||
buf.push("{");
|
||||
@@ -72,7 +128,7 @@ function toJsonArray(buf, obj, pretty, stack){
|
||||
var childPretty = pretty ? pretty + " " : false;
|
||||
var keys = [];
|
||||
for(var k in obj) {
|
||||
if (k.indexOf('$') === 0 || obj[k] === _undefined)
|
||||
if (obj[k] === _undefined)
|
||||
continue;
|
||||
keys.push(k);
|
||||
}
|
||||
@@ -94,7 +150,7 @@ function toJsonArray(buf, obj, pretty, stack){
|
||||
buf.push("}");
|
||||
}
|
||||
}
|
||||
if (typeof obj == $object) {
|
||||
if (isObject(obj)) {
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
+7
-6
@@ -4,7 +4,7 @@ function Route(template, defaults) {
|
||||
this.template = template = template + '#';
|
||||
this.defaults = defaults || {};
|
||||
var urlParams = this.urlParams = {};
|
||||
foreach(template.split(/\W/), function(param){
|
||||
forEach(template.split(/\W/), function(param){
|
||||
if (param && template.match(new RegExp(":" + param + "\\W"))) {
|
||||
urlParams[param] = true;
|
||||
}
|
||||
@@ -17,13 +17,13 @@ Route.prototype = {
|
||||
var self = this;
|
||||
var url = this.template;
|
||||
params = params || {};
|
||||
foreach(this.urlParams, function(_, urlParam){
|
||||
forEach(this.urlParams, function(_, urlParam){
|
||||
var value = params[urlParam] || self.defaults[urlParam] || "";
|
||||
url = url.replace(new RegExp(":" + urlParam + "(\\W)"), value + "$1");
|
||||
});
|
||||
url = url.replace(/\/?#$/, '');
|
||||
var query = [];
|
||||
foreachSorted(params, function(value, key){
|
||||
forEachSorted(params, function(value, key){
|
||||
if (!self.urlParams[key]) {
|
||||
query.push(encodeURI(key) + '=' + encodeURI(value));
|
||||
}
|
||||
@@ -52,7 +52,7 @@ ResourceFactory.prototype = {
|
||||
actions = extend({}, ResourceFactory.DEFAULT_ACTIONS, actions);
|
||||
function extractParams(data){
|
||||
var ids = {};
|
||||
foreach(paramDefaults || {}, function(value, key){
|
||||
forEach(paramDefaults || {}, function(value, key){
|
||||
ids[key] = value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value;
|
||||
});
|
||||
return ids;
|
||||
@@ -62,7 +62,7 @@ ResourceFactory.prototype = {
|
||||
copy(value || {}, this);
|
||||
}
|
||||
|
||||
foreach(actions, function(action, name){
|
||||
forEach(actions, function(action, name){
|
||||
var isPostOrPut = action.method == 'POST' || action.method == 'PUT';
|
||||
Resource[name] = function (a1, a2, a3) {
|
||||
var params = {};
|
||||
@@ -73,6 +73,7 @@ ResourceFactory.prototype = {
|
||||
case 2:
|
||||
if (isFunction(a2)) {
|
||||
callback = a2;
|
||||
//fallthrough
|
||||
} else {
|
||||
params = a1;
|
||||
data = a2;
|
||||
@@ -97,7 +98,7 @@ ResourceFactory.prototype = {
|
||||
if (status == 200) {
|
||||
if (action.isArray) {
|
||||
value.length = 0;
|
||||
foreach(response, function(item){
|
||||
forEach(response, function(item){
|
||||
value.push(new Resource(item));
|
||||
});
|
||||
} else {
|
||||
|
||||
+431
-38
@@ -48,7 +48,7 @@ var scopeId = 0,
|
||||
getterFnCache = {},
|
||||
compileCache = {},
|
||||
JS_KEYWORDS = {};
|
||||
foreach(
|
||||
forEach(
|
||||
("abstract,boolean,break,byte,case,catch,char,class,const,continue,debugger,default," +
|
||||
"delete,do,double,else,enum,export,extends,false,final,finally,float,for,function,goto," +
|
||||
"if,implements,import,ininstanceof,intinterface,long,native,new,null,package,private," +
|
||||
@@ -61,12 +61,12 @@ function getterFn(path){
|
||||
if (fn) return fn;
|
||||
|
||||
var code = 'var l, fn, t;\n';
|
||||
foreach(path.split('.'), function(key) {
|
||||
forEach(path.split('.'), function(key) {
|
||||
key = (JS_KEYWORDS[key]) ? '["' + key + '"]' : '.' + key;
|
||||
code += 'if(!s) return s;\n' +
|
||||
'l=s;\n' +
|
||||
's=s' + key + ';\n' +
|
||||
'if(typeof s=="function") s = function(){ return l'+key+'.apply(l, arguments); };\n';
|
||||
'if(typeof s=="function" && !(s instanceof RegExp)) s = function(){ return l'+key+'.apply(l, arguments); };\n';
|
||||
if (key.charAt(1) == '$') {
|
||||
// special code for super-imposed functions
|
||||
var name = key.substr(2);
|
||||
@@ -101,25 +101,264 @@ function expressionCompile(exp){
|
||||
}
|
||||
|
||||
function errorHandlerFor(element, error) {
|
||||
elementError(element, NG_EXCEPTION, isDefined(error) ? toJson(error) : error);
|
||||
elementError(element, NG_EXCEPTION, isDefined(error) ? formatError(error) : error);
|
||||
}
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc overview
|
||||
* @name angular.scope
|
||||
*
|
||||
* @description
|
||||
* Scope is a JavaScript object and the execution context for expressions. You can think about
|
||||
* scopes as JavaScript objects that have extra APIs for registering watchers. A scope is the model
|
||||
* in the model-view-controller design pattern.
|
||||
*
|
||||
* A few other characteristics of scopes:
|
||||
*
|
||||
* - Scopes can be nested. A scope (prototypically) inherits properties from its parent scope.
|
||||
* - Scopes can be attached (bound) to the HTML DOM tree (the view).
|
||||
* - A scope {@link angular.scope.$become becomes} `this` for a controller.
|
||||
* - Scope's {@link angular.scope.$eval $eval} is used to update its view.
|
||||
* - Scopes can {@link angular.scope.$watch watch} properties and fire events.
|
||||
*
|
||||
* # Basic Operations
|
||||
* Scopes can be created by calling {@link angular.scope() angular.scope()} or by compiling HTML.
|
||||
*
|
||||
* {@link angular.widget Widgets} and data bindings register listeners on the current scope to get
|
||||
* notified of changes to the scope state. When notified, these listeners push the updated state
|
||||
* through to the DOM.
|
||||
*
|
||||
* Here is a simple scope snippet to show how you can interact with the scope.
|
||||
* <pre>
|
||||
var scope = angular.scope();
|
||||
scope.salutation = 'Hello';
|
||||
scope.name = 'World';
|
||||
|
||||
expect(scope.greeting).toEqual(undefined);
|
||||
|
||||
scope.$watch('name', function(){
|
||||
this.greeting = this.salutation + ' ' + this.name + '!';
|
||||
});
|
||||
|
||||
expect(scope.greeting).toEqual('Hello World!');
|
||||
scope.name = 'Misko';
|
||||
// scope.$eval() will propagate the change to listeners
|
||||
expect(scope.greeting).toEqual('Hello World!');
|
||||
|
||||
scope.$eval();
|
||||
expect(scope.greeting).toEqual('Hello Misko!');
|
||||
* </pre>
|
||||
*
|
||||
* # Inheritance
|
||||
* A scope can inherit from a parent scope, as in this example:
|
||||
* <pre>
|
||||
var parent = angular.scope();
|
||||
var child = angular.scope(parent);
|
||||
|
||||
parent.salutation = "Hello";
|
||||
child.name = "World";
|
||||
expect(child.salutation).toEqual('Hello');
|
||||
|
||||
child.salutation = "Welcome";
|
||||
expect(child.salutation).toEqual('Welcome');
|
||||
expect(parent.salutation).toEqual('Hello');
|
||||
* </pre>
|
||||
*
|
||||
* # Dependency Injection
|
||||
* Scope also acts as a simple dependency injection framework.
|
||||
*
|
||||
* **TODO**: more info needed
|
||||
*
|
||||
* # When scopes are evaluated
|
||||
* Anyone can update a scope by calling its {@link angular.scope.$eval $eval()} method. By default
|
||||
* angular widgets listen to user change events (e.g. the user enters text into text field), copy
|
||||
* the data from the widget to the scope (the MVC model), and then call the `$eval()` method on the
|
||||
* root scope to update dependents. This creates a spreadsheet-like behavior: the bound views update
|
||||
* immediately as the user types into the text field.
|
||||
*
|
||||
* Similarly, when a request to fetch data from a server is made and the response comes back, the
|
||||
* data is written into the model and then $eval() is called to push updates through to the view and
|
||||
* any other dependents.
|
||||
*
|
||||
* Because a change in the model that's triggered either by user input or by server response calls
|
||||
* `$eval()`, it is unnecessary to call `$eval()` from within your controller. The only time when
|
||||
* calling `$eval()` is needed, is when implementing a custom widget or service.
|
||||
*
|
||||
* Because scopes are inherited, the child scope `$eval()` overrides the parent `$eval()` method.
|
||||
* So to update the whole page you need to call `$eval()` on the root scope as `$root.$eval()`.
|
||||
*
|
||||
* Note: A widget that creates scopes (i.e. {@link angular.widget.@ng:repeat ng:repeat}) is
|
||||
* responsible for forwarding `$eval()` calls from the parent to those child scopes. That way,
|
||||
* calling $eval() on the root scope will update the whole page.
|
||||
*
|
||||
*
|
||||
* @TODO THESE PARAMS AND RETURNS ARE NOT RENDERED IN THE TEMPLATE!! FIX THAT!
|
||||
* @param {Object} parent The scope that should become the parent for the newly created scope.
|
||||
* @param {Object.<string, function()>=} providers Map of service factory which need to be provided
|
||||
* for the current scope. Usually {@link angular.service}.
|
||||
* @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
|
||||
* append/override services provided by `providers`.
|
||||
* @returns {Object} Newly created scope.
|
||||
*
|
||||
*
|
||||
* @exampleDescription
|
||||
* This example demonstrates scope inheritance and property overriding.
|
||||
*
|
||||
* In this example, the root scope encompasses the whole HTML DOM tree. This scope has `salutation`,
|
||||
* `name`, and `names` properties. The {@link angular.widget@ng:repeat ng:repeat} creates a child
|
||||
* scope, one for each element in the names array. The repeater also assigns $index and name into
|
||||
* the child scope.
|
||||
*
|
||||
* Notice that:
|
||||
*
|
||||
* - While the name is set in the child scope it does not change the name defined in the root scope.
|
||||
* - The child scope inherits the salutation property from the root scope.
|
||||
* - The $index property does not leak from the child scope to the root scope.
|
||||
*
|
||||
* @example
|
||||
<ul ng:init="salutation='Hello'; name='Misko'; names=['World', 'Earth']">
|
||||
<li ng:repeat="name in names">
|
||||
{{$index}}: {{salutation}} {{name}}!
|
||||
</li>
|
||||
</ul>
|
||||
<pre>
|
||||
$index={{$index}}
|
||||
salutation={{salutation}}
|
||||
name={{name}}</pre>
|
||||
|
||||
@scenario
|
||||
it('should inherit the salutation property and override the name property', function() {
|
||||
expect(using('.doc-example-live').repeater('li').row(0)).
|
||||
toEqual(['0', 'Hello', 'World']);
|
||||
expect(using('.doc-example-live').repeater('li').row(1)).
|
||||
toEqual(['1', 'Hello', 'Earth']);
|
||||
expect(using('.doc-example-live').element('pre').text()).
|
||||
toBe('$index=\nsalutation=Hello\nname=Misko');
|
||||
});
|
||||
|
||||
*/
|
||||
function createScope(parent, providers, instanceCache) {
|
||||
function Parent(){}
|
||||
parent = Parent.prototype = (parent || {});
|
||||
var instance = new Parent();
|
||||
var evalLists = {sorted:[]};
|
||||
var postList = [], postHash = {}, postId = 0;
|
||||
var $log, $exceptionHandler;
|
||||
|
||||
extend(instance, {
|
||||
'this': instance,
|
||||
$id: (scopeId++),
|
||||
$parent: parent,
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$bind
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Binds a function `fn` to the current scope. See: {@link angular.bind}.
|
||||
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
var fn = scope.$bind(function(){
|
||||
return this;
|
||||
});
|
||||
expect(fn()).toEqual(scope);
|
||||
</pre>
|
||||
*
|
||||
* @param {function()} fn Function to be bound.
|
||||
*/
|
||||
$bind: bind(instance, bind, instance),
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$get
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Returns the value for `property_chain` on the current scope. Unlike in JavaScript, if there
|
||||
* are any `undefined` intermediary properties, `undefined` is returned instead of throwing an
|
||||
* exception.
|
||||
*
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
expect(scope.$get('person.name')).toEqual(undefined);
|
||||
scope.person = {};
|
||||
expect(scope.$get('person.name')).toEqual(undefined);
|
||||
scope.person.name = 'misko';
|
||||
expect(scope.$get('person.name')).toEqual('misko');
|
||||
</pre>
|
||||
*
|
||||
* @param {string} property_chain String representing name of a scope property. Optionally
|
||||
* properties can be chained with `.` (dot), e.g. `'person.name.first'`
|
||||
* @returns {*} Value for the (nested) property.
|
||||
*/
|
||||
$get: bind(instance, getter, instance),
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$set
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Assigns a value to a property of the current scope specified via `property_chain`. Unlike in
|
||||
* JavaScript, if there are any `undefined` intermediary properties, empty objects are created
|
||||
* and assigned in to them instead of throwing an exception.
|
||||
*
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
expect(scope.person).toEqual(undefined);
|
||||
scope.$set('person.name', 'misko');
|
||||
expect(scope.person).toEqual({name:'misko'});
|
||||
expect(scope.person.name).toEqual('misko');
|
||||
</pre>
|
||||
*
|
||||
* @param {string} property_chain String representing name of a scope property. Optionally
|
||||
* properties can be chained with `.` (dot), e.g. `'person.name.first'`
|
||||
* @param {*} value Value to assign to the scope property.
|
||||
*/
|
||||
$set: bind(instance, setter, instance),
|
||||
|
||||
$eval: function $eval(exp) {
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$eval
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Without the `exp` parameter triggers an eval cycle for this scope and its child scopes.
|
||||
*
|
||||
* With the `exp` parameter, compiles the expression to a function and calls it with `this` set
|
||||
* to the current scope and returns the result. In other words, evaluates `exp` as angular
|
||||
* expression in the context of the current scope.
|
||||
*
|
||||
* # Example
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
scope.a = 1;
|
||||
scope.b = 2;
|
||||
|
||||
expect(scope.$eval('a+b')).toEqual(3);
|
||||
expect(scope.$eval(function(){ return this.a + this.b; })).toEqual(3);
|
||||
|
||||
scope.$onEval('sum = a+b');
|
||||
expect(scope.sum).toEqual(undefined);
|
||||
scope.$eval();
|
||||
expect(scope.sum).toEqual(3);
|
||||
</pre>
|
||||
*
|
||||
* @param {(string|function())=} exp An angular expression to be compiled to a function or a js
|
||||
* function.
|
||||
*
|
||||
* @returns {*} The result of calling compiled `exp` with `this` set to the current scope.
|
||||
*/
|
||||
$eval: function(exp) {
|
||||
var type = typeof exp;
|
||||
var i, iSize;
|
||||
var j, jSize;
|
||||
@@ -133,11 +372,6 @@ function createScope(parent, providers, instanceCache) {
|
||||
instance.$tryEval(queue[j].fn, queue[j].handler);
|
||||
}
|
||||
}
|
||||
while(postList.length) {
|
||||
fn = postList.shift();
|
||||
delete postHash[fn.$postEvalId];
|
||||
instance.$tryEval(fn);
|
||||
}
|
||||
} else if (type === $function) {
|
||||
return exp.call(instance);
|
||||
} else if (type === 'string') {
|
||||
@@ -145,6 +379,44 @@ function createScope(parent, providers, instanceCache) {
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$tryEval
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Evaluates the expression in the context of the current scope just like
|
||||
* {@link angular.scope.$eval()} with expression parameter, but also wraps it in a try/catch
|
||||
* block.
|
||||
*
|
||||
* If exception is thrown then `exceptionHandler` is used to handle the exception.
|
||||
*
|
||||
* # Example
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
scope.error = function(){ throw 'myerror'; };
|
||||
scope.$exceptionHandler = function(e) {this.lastException = e; };
|
||||
|
||||
expect(scope.$eval('error()'));
|
||||
expect(scope.lastException).toEqual('myerror');
|
||||
this.lastException = null;
|
||||
|
||||
expect(scope.$eval('error()'), function(e) {this.lastException = e; });
|
||||
expect(scope.lastException).toEqual('myerror');
|
||||
|
||||
var body = angular.element(window.document.body);
|
||||
expect(scope.$eval('error()'), body);
|
||||
expect(body.attr('ng-exception')).toEqual('"myerror"');
|
||||
expect(body.hasClass('ng-exception')).toEqual(true);
|
||||
</pre>
|
||||
*
|
||||
* @param {string|function()} expression Angular expression to evaluate.
|
||||
* @param {function()|DOMElement} exceptionHandler Function to be called or DOMElement to be
|
||||
* decorated.
|
||||
* @returns {*} The result of `expression` evaluation.
|
||||
*/
|
||||
$tryEval: function (expression, exceptionHandler) {
|
||||
var type = typeof expression;
|
||||
try {
|
||||
@@ -154,25 +426,69 @@ function createScope(parent, providers, instanceCache) {
|
||||
return expressionCompile(expression).call(instance);
|
||||
}
|
||||
} catch (e) {
|
||||
(instance.$log || {error:error}).error(e);
|
||||
if ($log) $log.error(e);
|
||||
if (isFunction(exceptionHandler)) {
|
||||
exceptionHandler(e);
|
||||
} else if (exceptionHandler) {
|
||||
errorHandlerFor(exceptionHandler, e);
|
||||
} else if (isFunction(instance.$exceptionHandler)) {
|
||||
instance.$exceptionHandler(e);
|
||||
} else if (isFunction($exceptionHandler)) {
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
$watch: function(watchExp, listener, exceptionHandler) {
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$watch
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Registers `listener` as a callback to be executed every time the `watchExp` changes. Be aware
|
||||
* that callback gets, by default, called upon registration, this can be prevented via the
|
||||
* `initRun` parameter.
|
||||
*
|
||||
* # Example
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
scope.name = 'misko';
|
||||
scope.counter = 0;
|
||||
|
||||
expect(scope.counter).toEqual(0);
|
||||
scope.$watch('name', 'counter = counter + 1');
|
||||
expect(scope.counter).toEqual(1);
|
||||
|
||||
scope.$eval();
|
||||
expect(scope.counter).toEqual(1);
|
||||
|
||||
scope.name = 'adam';
|
||||
scope.$eval();
|
||||
expect(scope.counter).toEqual(2);
|
||||
</pre>
|
||||
*
|
||||
* @param {function()|string} watchExp Expression that should be evaluated and checked for
|
||||
* change during each eval cycle. Can be an angular string expression or a function.
|
||||
* @param {function()|string} listener Function (or angular string expression) that gets called
|
||||
* every time the value of the `watchExp` changes. The function will be called with two
|
||||
* parameters, `newValue` and `oldValue`.
|
||||
* @param {(function()|DOMElement)=} [exceptionHanlder=angular.service.$exceptionHandler] Handler
|
||||
* that gets called when `watchExp` or `listener` throws an exception. If a DOMElement is
|
||||
* specified as handler, the element gets decorated by angular with the information about the
|
||||
* exception.
|
||||
* @param {boolean=} [initRun=true] Flag that prevents the first execution of the listener upon
|
||||
* registration.
|
||||
*
|
||||
*/
|
||||
$watch: function(watchExp, listener, exceptionHandler, initRun) {
|
||||
var watch = expressionCompile(watchExp),
|
||||
last;
|
||||
last = watch.call(instance);
|
||||
listener = expressionCompile(listener);
|
||||
function watcher(){
|
||||
function watcher(firstRun){
|
||||
var value = watch.call(instance),
|
||||
// we have to save the value because listener can call ourselves => inf loop
|
||||
lastValue = last;
|
||||
if (last !== value) {
|
||||
if (firstRun || lastValue !== value) {
|
||||
last = value;
|
||||
instance.$tryEval(function(){
|
||||
return listener.call(instance, value, lastValue);
|
||||
@@ -180,9 +496,36 @@ function createScope(parent, providers, instanceCache) {
|
||||
}
|
||||
}
|
||||
instance.$onEval(PRIORITY_WATCH, watcher);
|
||||
watcher();
|
||||
if (isUndefined(initRun)) initRun = true;
|
||||
if (initRun) watcher(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$onEval
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Evaluates the `expr` expression in the context of the current scope during each
|
||||
* {@link angular.scope.$eval eval cycle}.
|
||||
*
|
||||
* # Example
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
scope.counter = 0;
|
||||
scope.$onEval('counter = counter + 1');
|
||||
expect(scope.counter).toEqual(0);
|
||||
scope.$eval();
|
||||
expect(scope.counter).toEqual(1);
|
||||
</pre>
|
||||
*
|
||||
* @param {number} [priority=0] Execution priority. Lower priority numbers get executed first.
|
||||
* @param {string|function()} expr Angular expression or function to be executed.
|
||||
* @param {(function()|DOMElement)=} [exceptionHandler=angular.service.$exceptionHandler] Handler
|
||||
* function to call or DOM element to decorate when an exception occurs.
|
||||
*
|
||||
*/
|
||||
$onEval: function(priority, expr, exceptionHandler){
|
||||
if (!isNumber(priority)) {
|
||||
exceptionHandler = expr;
|
||||
@@ -202,27 +545,42 @@ function createScope(parent, providers, instanceCache) {
|
||||
});
|
||||
},
|
||||
|
||||
$postEval: function(expr) {
|
||||
if (expr) {
|
||||
var fn = expressionCompile(expr);
|
||||
var id = fn.$postEvalId;
|
||||
if (!id) {
|
||||
id = '$' + instance.$id + "_" + (postId++);
|
||||
fn.$postEvalId = id;
|
||||
}
|
||||
if (!postHash[id]) {
|
||||
postList.push(postHash[id] = fn);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$become
|
||||
* @function
|
||||
* @deprecated This method will be removed before 1.0
|
||||
*
|
||||
* @description
|
||||
* Modifies the scope to act like an instance of the given class by:
|
||||
*
|
||||
* - copying the class's prototype methods
|
||||
* - applying the class's initialization function to the scope instance (without using the new
|
||||
* operator)
|
||||
*
|
||||
* That makes the scope be a `this` for the given class's methods — effectively an instance of
|
||||
* the given class with additional (scope) stuff. A scope can later `$become` another class.
|
||||
*
|
||||
* `$become` gets used to make the current scope act like an instance of a controller class.
|
||||
* This allows for use of a controller class in two ways.
|
||||
*
|
||||
* - as an ordinary JavaScript class for standalone testing, instantiated using the new
|
||||
* operator, with no attached view.
|
||||
* - as a controller for an angular model stored in a scope, "instantiated" by
|
||||
* `scope.$become(ControllerClass)`.
|
||||
*
|
||||
* Either way, the controller's methods refer to the model variables like `this.name`. When
|
||||
* stored in a scope, the model supports data binding. When bound to a view, {{name}} in the
|
||||
* HTML template refers to the same variable.
|
||||
*/
|
||||
$become: function(Class) {
|
||||
if (isFunction(Class)) {
|
||||
instance.constructor = Class;
|
||||
foreach(Class.prototype, function(fn, name){
|
||||
forEach(Class.prototype, function(fn, name){
|
||||
instance[name] = bind(instance, fn);
|
||||
});
|
||||
instance.$inject.apply(instance, concat([Class, instance], arguments, 1));
|
||||
instance.$service.apply(instance, concat([Class, instance], arguments, 1));
|
||||
|
||||
//TODO: backwards compatibility hack, remove when we don't depend on init methods
|
||||
if (isFunction(Class.prototype.init)) {
|
||||
@@ -231,9 +589,25 @@ function createScope(parent, providers, instanceCache) {
|
||||
}
|
||||
},
|
||||
|
||||
$new: function(Class) {
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$new
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Creates a new {@link angular.scope scope}, that:
|
||||
*
|
||||
* - is a child of the current scope
|
||||
* - will {@link angular.scope.$become $become} of type specified via `constructor`
|
||||
*
|
||||
* @param {function()} constructor Constructor function of the type the new scope should assume.
|
||||
* @returns {Object} The newly created child scope.
|
||||
*
|
||||
*/
|
||||
$new: function(constructor) {
|
||||
var child = createScope(instance);
|
||||
child.$become.apply(instance, concat([Class], arguments, 1));
|
||||
child.$become.apply(instance, concat([constructor], arguments, 1));
|
||||
instance.$onEval(child.$eval);
|
||||
return child;
|
||||
}
|
||||
@@ -243,8 +617,27 @@ function createScope(parent, providers, instanceCache) {
|
||||
if (!parent.$root) {
|
||||
instance.$root = instance;
|
||||
instance.$parent = instance;
|
||||
(instance.$inject = createInjector(instance, providers, instanceCache))();
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$service
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Provides access to angular's dependency injector and
|
||||
* {@link angular.service registered services}. In general the use of this api is discouraged,
|
||||
* except for tests and components that currently don't support dependency injection (widgets,
|
||||
* filters, etc).
|
||||
*
|
||||
* @param {string} serviceId String ID of the service to return.
|
||||
* @returns {*} Value, object or function returned by the service factory function if any.
|
||||
*/
|
||||
(instance.$service = createInjector(instance, providers, instanceCache))();
|
||||
}
|
||||
|
||||
$log = instance.$service('$log');
|
||||
$exceptionHandler = instance.$service('$exceptionHandler');
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user