From 3752564d62c39ea72dc9bcd98136d19b2b2ebdc6 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 8 Feb 2015 08:13:07 +0400 Subject: [PATCH 1/2] fix error in package.json files --- LICENSE | 226 +- README.md | 11 +- configs/cli.js | 8 +- configs/client-default-local.js | 158 + node_modules/ace_tree/LICENSE | 15 - node_modules/ace_tree/package.json | 2 + node_modules/ace_tree/todo.md | 12 + node_modules/kaefer/lib/client.js | 26 +- package.json | 7 +- plugins/c9.cli.publish/README.md | 2 - plugins/c9.cli.publish/publish.js | 16 +- plugins/c9.cli/auth.bootstrap.js | 2 +- plugins/c9.core/c9.js | 4 +- plugins/c9.core/ext.js | 8 +- plugins/c9.error/raygun.connect.js | 5 - plugins/c9.ide.ace/themes.js | 2 +- plugins/c9.ide.auth/auth.js | 3 - plugins/c9.ide.dialog.common/error.css | 37 +- plugins/c9.ide.dialog.common/error.js | 165 +- plugins/c9.ide.keys/editor.js | 7 +- plugins/c9.ide.layout.classic/layout.js | 46 +- .../themes/default-dark-gray.less | 6 - .../themes/default-dark.less | 6 - .../themes/default-flat-light.less | 6 - .../themes/default-light-gray.less | 6 - .../themes/default-light.less | 6 - .../themes/flat-light.less | 4 +- plugins/c9.ide.login/login.css | 36 + plugins/c9.ide.login/login.js | 194 + plugins/c9.ide.plugins/debug.js | 14 +- plugins/c9.ide.plugins/loader.js | 8 +- plugins/c9.ide.ui/forms.js | 2 +- plugins/c9.ide.ui/lib/core.js | 134 - plugins/c9.ide.ui/lib/dropdown.js | 8392 ----------------- plugins/c9.ide.ui/lib/flexbox.js | 994 -- plugins/c9.ide.ui/lib/menu/menu.js | 1830 ---- plugins/c9.ide.ui/lib/page.js | 2171 ----- plugins/c9.ide.ui/lib/splitbox.js | 1568 --- plugins/c9.ide.ui/lib_apf.js | 14 +- plugins/c9.login.client/bootstrap.js | 283 + plugins/c9.vfs.client/vfs_client.js | 24 +- plugins/c9.vfs.server/vfs.js | 13 +- scripts/install-sdk.sh | 80 - 43 files changed, 832 insertions(+), 15721 deletions(-) create mode 100644 configs/client-default-local.js delete mode 100644 node_modules/ace_tree/LICENSE create mode 100644 node_modules/ace_tree/todo.md create mode 100644 plugins/c9.ide.login/login.css create mode 100644 plugins/c9.ide.login/login.js delete mode 100644 plugins/c9.ide.ui/lib/core.js delete mode 100644 plugins/c9.ide.ui/lib/dropdown.js delete mode 100644 plugins/c9.ide.ui/lib/flexbox.js delete mode 100644 plugins/c9.ide.ui/lib/menu/menu.js delete mode 100644 plugins/c9.ide.ui/lib/page.js delete mode 100644 plugins/c9.ide.ui/lib/splitbox.js create mode 100644 plugins/c9.login.client/bootstrap.js delete mode 100755 scripts/install-sdk.sh diff --git a/LICENSE b/LICENSE index bf84a3d8..f5c8e4f3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,239 +1,99 @@ Cloud9 Software Development Kit Non-commercial License Agreement 1. Introduction -1.1 The Cloud9 Software Development Kit (referred to in this License Agreement -as the "SDK" and specifically including the Cloud9 system files, the Cloud9 -computer software source code, Cloud9’s Ace editor plugin and Cloud9’s -terminal plugin) is licensed to you subject to the terms of this Non-commercial -License Agreement. +1.1 The Cloud9 Software Development Kit (referred to in this License Agreement as the "SDK" and specifically including the Cloud9 system files, the Cloud9 computer software source code, Cloud9’s Ace editor plugin and Cloud9’s terminal plugin) is licensed to you subject to the terms of this Non-commercial License Agreement. -1.2 Any software provided along with the SDK (such as some of the files in the -node_modules directory) that is associated with a separate license agreement is -licensed to you under the terms of that separate license agreement. +1.2 Any software provided along with the SDK (such as some of the files in the node_modules directory) that is associated with a separate license agreement is licensed to you under the terms of that separate license agreement. -This License Agreement forms a legally binding contract between you and Cloud9 -in relation to your use of the SDK. +This License Agreement forms a legally binding contract between you and Cloud9 in relation to your use of the SDK. -1.2 “Cloud9” refers to Cloud9 IDE, Inc, registered Delaware corporation with -principal place of business 1663 McAllister Street #B, San Francisco, CA 94115, -United States. +1.2 “Cloud9” refers to Cloud9 IDE, Inc, registered Delaware corporation with principal place of business 1663 McAllister Street #B, San Francisco, CA 94115, United States. 2. Accepting this license agreement -2.1 In order to use the SDK, you must first agree to this License Agreement. You -may not use the SDK if you do not accept this License Agreement. +2.1 In order to use the SDK, you must first agree to this License Agreement. You may not use the SDK if you do not accept this License Agreement. -2.2 You agree to be bound by the terms of this SDK License by installing, -copying, downloading, accessing or otherwise using the SDK. +2.2 You agree to be bound by the terms of this SDK License by installing, copying, downloading, accessing or otherwise using the SDK. -2.3 You may not use the SDK and may not accept the License Agreement if you are -a person barred from receiving the SDK under the laws of the United States or -other countries including the country in which you are resident or from which -you use the SDK. +2.3 You may not use the SDK and may not accept the License Agreement if you are a person barred from receiving the SDK under the laws of the United States or other countries including the country in which you are resident or from which you use the SDK. -2.4 If you are agreeing to be bound by this License Agreement on behalf of your -employer or other entity, you represent and warrant that you have full legal -authority to bind your employer or such entity to this License Agreement. If you -do not have the requisite authority, you may not accept the License Agreement or -use the SDK on behalf of your employer or other entity. +2.4 If you are agreeing to be bound by this License Agreement on behalf of your employer or other entity, you represent and warrant that you have full legal authority to bind your employer or such entity to this License Agreement. If you do not have the requisite authority, you may not accept the License Agreement or use the SDK on behalf of your employer or other entity. 3. The Cloud9 SDK -3.1 Cloud9 grants to you a non-exclusive, non-commercial, non-transferable, -personal license to do the following, all subject to the terms of this license: +3.1 Cloud9 grants to you a non-exclusive, non-commercial, non-transferable, personal license to do the following, all subject to the terms of this license: -Copy and modify any SDK source code, and include such copies and modifications -in software that you create (“Derived Software”), and include SDK object -code in Derived Software; +Copy and modify any SDK source code, and include such copies and modifications in software that you create (“Derived Software”), and include SDK object code in Derived Software; -Use SDK source code, SDK object code, and Derived Software for your own -personal, noncommercial use; +Use SDK source code, SDK object code, and Derived Software for your own personal, noncommercial use; -Distribute Derived Software and SDK documentation to others, provided that any -distribution is for noncommercial purposes only, is without charge or fee or -consideration of any kind, and that every recipient of the distribution agree to -and is bound by the terms of this SDK License. +Distribute Derived Software and SDK documentation to others, provided that any distribution is for noncommercial purposes only, is without charge or fee or consideration of any kind, and that every recipient of the distribution agree to and is bound by the terms of this SDK License. -3.2 You agree that Cloud9 or third parties own all legal right, title and -interest in and to the SDK, including any Intellectual Property Rights that -subsist in the SDK. "Intellectual Property Rights" means any and all rights -under patent law, copyright law, trade secret law, trademark law, and any and -all other proprietary rights. Cloud9 reserves all rights not expressly granted -to you. +3.2 You agree that Cloud9 or third parties own all legal right, title and interest in and to the SDK, including any Intellectual Property Rights that subsist in the SDK. "Intellectual Property Rights" means any and all rights under patent law, copyright law, trade secret law, trademark law, and any and all other proprietary rights. Cloud9 reserves all rights not expressly granted to you. -3.3 You may not use the SDK for any purpose not expressly permitted by this -License Agreement. You do not have the right to: +3.3 You may not use the SDK for any purpose not expressly permitted by this License Agreement. You do not have the right to: -Reverse engineer or in any other way attempt to reproduce the operation of the -code in the SDK, or defeat or circumvent any security features of any code in -the SDK; -Use anything in the SDK or any Derived Software for any commercial purpose, -including without limitation for provision of programming, hosting, or any other -services for others; -Distribute anything in the SDK or any Derived Software to any other party that -is not bound by the terms of this SDK License; -Make any copies of anything in the SDK without including all copyright, license, -and other legal notices with the copy; -Transfer the SDK, this SDK License, or any part thereof, to any other party. Any -attempt at such a transfer is void. +Reverse engineer or in any other way attempt to reproduce the operation of the code in the SDK, or defeat or circumvent any security features of any code in the SDK; +Use anything in the SDK or any Derived Software for any commercial purpose, including without limitation for provision of programming, hosting, or any other services for others; +Distribute anything in the SDK or any Derived Software to any other party that is not bound by the terms of this SDK License; +Make any copies of anything in the SDK without including all copyright, license, and other legal notices with the copy; +Transfer the SDK, this SDK License, or any part thereof, to any other party. Any attempt at such a transfer is void. -3.4 Use, reproduction and distribution of components of the SDK licensed under a -separate license agreement are governed solely by the terms of that separate -license agreement and not this License Agreement. +3.4 Use, reproduction and distribution of components of the SDK licensed under a separate license agreement are governed solely by the terms of that separate license agreement and not this License Agreement. -3.5 You agree that the form and nature of the SDK that Cloud9 provides may -change without prior notice to you and that future versions of the SDK may be -incompatible with applications developed on previous versions of the SDK. You -agree that Cloud9 may stop (permanently or temporarily) providing the SDK (or -any features within the SDK) to you or to users generally at Cloud9's sole -discretion, without prior notice to you. +3.5 You agree that the form and nature of the SDK that Cloud9 provides may change without prior notice to you and that future versions of the SDK may be incompatible with applications developed on previous versions of the SDK. You agree that Cloud9 may stop (permanently or temporarily) providing the SDK (or any features within the SDK) to you or to users generally at Cloud9's sole discretion, without prior notice to you. -3.6 Nothing in this License Agreement gives you a right to use any of Cloud9's -trade names, trademarks, service marks, logos, domain names, or other -distinctive brand features. +3.6 Nothing in this License Agreement gives you a right to use any of Cloud9's trade names, trademarks, service marks, logos, domain names, or other distinctive brand features. -3.7 You agree that you will not remove, obscure, or alter any proprietary rights -notices (including copyright and trademark notices) that may be affixed to or -contained within the SDK. +3.7 You agree that you will not remove, obscure, or alter any proprietary rights notices (including copyright and trademark notices) that may be affixed to or contained within the SDK. 4. Privacy and Information -4.1 In order to continually innovate and improve the SDK, Cloud9 may collect -certain usage statistics from the software including but not limited to a unique -identifier, associated IP address, version number of the software, and -information on which tools and/or services in the SDK are being used and how -they are being used. Before any of this information is collected, the SDK will -notify you and seek your consent. If you withhold consent, the information will -not be collected. +4.1 In order to continually innovate and improve the SDK, Cloud9 may collect certain usage statistics from the software including but not limited to a unique identifier, associated IP address, version number of the software, and information on which tools and/or services in the SDK are being used and how they are being used. Before any of this information is collected, the SDK will notify you and seek your consent. If you withhold consent, the information will not be collected. -4.2 The data collected is examined in the aggregate to improve the SDK and is -maintained in accordance with Cloud9's Privacy Policy. +4.2 The data collected is examined in the aggregate to improve the SDK and is maintained in accordance with Cloud9's Privacy Policy. 5. Your developer credentials -5.1 You agree that you are responsible for maintaining the confidentiality of -any developer credentials that may be issued to you by Cloud9 or which you may -choose yourself and that you will be solely responsible for all applications -that are developed under your developer credentials. +5.1 You agree that you are responsible for maintaining the confidentiality of any developer credentials that may be issued to you by Cloud9 or which you may choose yourself and that you will be solely responsible for all applications that are developed under your developer credentials. 6. Third party applications -6.1 If you use the SDK to run applications developed by a third party or that -access data, content or resources provided by a third party, you agree that -Cloud9 is not responsible for those applications, data, content, or resources. -You understand that all data, content or resources which you may access through -such third party applications are the sole responsibility of the person from -which they originated and that Cloud9 is not liable for any loss or damage that -you may experience as a result of the use or access of any of those third party -applications, data, content, or resources. +6.1 If you use the SDK to run applications developed by a third party or that access data, content or resources provided by a third party, you agree that Cloud9 is not responsible for those applications, data, content, or resources. You understand that all data, content or resources which you may access through such third party applications are the sole responsibility of the person from which they originated and that Cloud9 is not liable for any loss or damage that you may experience as a result of the use or access of any of those third party applications, data, content, or resources. -6.2 You should be aware the data, content, and resources presented to you -through such a third party application may be protected by intellectual property -rights which are owned by the providers (or by other persons or companies on -their behalf). You may not modify, rent, lease, loan, sell, distribute or create -derivative works based on these data, content, or resources (either in whole or -in part) unless you have been specifically given permission to do so by the -relevant owners. +6.2 You should be aware the data, content, and resources presented to you through such a third party application may be protected by intellectual property rights which are owned by the providers (or by other persons or companies on their behalf). You may not modify, rent, lease, loan, sell, distribute or create derivative works based on these data, content, or resources (either in whole or in part) unless you have been specifically given permission to do so by the relevant owners. -6.3 You acknowledge that your use of such third party applications, data, -content, or resources may be subject to separate terms between you and the -relevant third party. In that case, this License Agreement does not affect your -legal relationship with these third parties. +6.3 You acknowledge that your use of such third party applications, data, content, or resources may be subject to separate terms between you and the relevant third party. In that case, this License Agreement does not affect your legal relationship with these third parties. 7. Termination of this license agreement -7.1 This License Agreement will continue to apply until terminated by either you -or Cloud9 as set out below. +7.1 This License Agreement will continue to apply until terminated by either you or Cloud9 as set out below. -7.2 If you want to terminate this License Agreement, you may do so by ceasing -your use of the SDK and any relevant developer credentials. +7.2 If you want to terminate this License Agreement, you may do so by ceasing your use of the SDK and any relevant developer credentials. 7.3 Cloud9 may at any time, terminate this License Agreement with you if: a. you do not abide by the provisions of this License Agreement; or b. at any time upon 72 hours notice to you; or -c. at any time upon post of a notice of termination to Cloud9’s website. In -such event, you must destroy all copies of the SDK and any Derived Software. -Termination of this SDK License shall terminate your rights and all obligations -of Cloud9 under this SDK License, but the remaining provisions shall survive -termination. +c. at any time upon post of a notice of termination to Cloud9’s website. In such event, you must destroy all copies of the SDK and any Derived Software. Termination of this SDK License shall terminate your rights and all obligations of Cloud9 under this SDK License, but the remaining provisions shall survive termination. 8. DISCLAIMER OF WARRANTIES -8.1 THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SDK AND ANY DERIVED -SOFTWARE REMAINS WITH YOU, THE USER. CLOUD9 EXPRESSLY DISCLAIMS (I) ANY WARRANTY -FOR THE SDK AND ANY DERIVED SOFTWARE, AND (II) ANY COMMON LAW DUTIES WITH REGARD -TO SDK AND ANY DERIVED SOFTWARE, INCLUDING DUTIES OF LACK OF NEGLIGENCE AND LACK -OF WORKMANLIKE EFFORT. +8.1 THE ENTIRE RISK ARISING OUT OF USE OR PERFORMANCE OF THE SDK AND ANY DERIVED SOFTWARE REMAINS WITH YOU, THE USER. CLOUD9 EXPRESSLY DISCLAIMS (I) ANY WARRANTY FOR THE SDK AND ANY DERIVED SOFTWARE, AND (II) ANY COMMON LAW DUTIES WITH REGARD TO SDK AND ANY DERIVED SOFTWARE, INCLUDING DUTIES OF LACK OF NEGLIGENCE AND LACK OF WORKMANLIKE EFFORT. -8.2 THE SDK AND ANY INFORMATION AVAILABLE IN CONNECTION THEREWITH ARE PROVIDED -ON AN "AS IS" AND "AS AVAILABLE" BASIS, "WITH ALL FAULTS" AND WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, THE -IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR -NON-INFRINGEMENT. +8.2 THE SDK AND ANY INFORMATION AVAILABLE IN CONNECTION THEREWITH ARE PROVIDED ON AN "AS IS" AND "AS AVAILABLE" BASIS, "WITH ALL FAULTS" AND WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. -8.3 ANY WARRANTY AGAINST INFRINGEMENT THAT MAY BE PROVIDED IN SECTION 2-312(3) -OF THE UNIFORM COMMERCIAL CODE AND/OR IN ANY OTHER COMPARABLE STATE STATUTE IS -EXPRESSLY DISCLAIMED. ALSO, THERE IS NO WARRANTY OF TITLE, INTERFERENCE WITH -YOUR ENJOYMENT, OR AUTHORITY IN CONNECTION WITH THE SDK OR ANY DERIVED SOFTWARE -OR INFORMATION AVAILABLE IN CONNECTION THEREWITH. THIS SECTION WILL APPLY TO THE -MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW. +8.3 ANY WARRANTY AGAINST INFRINGEMENT THAT MAY BE PROVIDED IN SECTION 2-312(3) OF THE UNIFORM COMMERCIAL CODE AND/OR IN ANY OTHER COMPARABLE STATE STATUTE IS EXPRESSLY DISCLAIMED. ALSO, THERE IS NO WARRANTY OF TITLE, INTERFERENCE WITH YOUR ENJOYMENT, OR AUTHORITY IN CONNECTION WITH THE SDK OR ANY DERIVED SOFTWARE OR INFORMATION AVAILABLE IN CONNECTION THEREWITH. THIS SECTION WILL APPLY TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW. 9. LIMITATION OF LIABILITY AND EXCLUSIVE REMEDY -9.1 NEITHER CLOUD9, ITS LICENSORS, NOR THEIR AFFILIATES SHALL BE LIABLE IN ANY -WAY FOR LOSS OR DAMAGE OF ANY KIND RESULTING FROM THE USE OR INABILITY TO USE -THE SDK OR ANY DERIVED SOFTWARE INCLUDING, BUT NOT LIMITED TO, LOSS OF GOODWILL, -WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL -DAMAGES OR LOSSES. +9.1 NEITHER CLOUD9, ITS LICENSORS, NOR THEIR AFFILIATES SHALL BE LIABLE IN ANY WAY FOR LOSS OR DAMAGE OF ANY KIND RESULTING FROM THE USE OR INABILITY TO USE THE SDK OR ANY DERIVED SOFTWARE INCLUDING, BUT NOT LIMITED TO, LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES. -9.2 IN NO EVENT WILL CLOUD9 BE LIABLE FOR ANY INDIRECT, INCIDENTAL, -CONSEQUENTIAL, SPECIAL, PUNITIVE, EXEMPLARY DAMAGES, OR ANY OTHER DAMAGES -ARISING OUT OF OR IN ANY WAY CONNECTED WITH THE SDK, ANY DERIVED SOFTWARE, ANY -INFORMATION AVAILABLE IN CONNECTION THEREWITH, OR THE DELAY OR INABILITY TO USE -THE SDK, ANY DERIVED SOFTWARE, OR ANY INFORMATION, EVEN IN THE EVENT OF FAULT, -TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY, BREACH OF CONTRACT, OR BREACH OF -ANY WARRANTY AND EVEN IF CLOUD9 HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. +9.2 IN NO EVENT WILL CLOUD9 BE LIABLE FOR ANY INDIRECT, INCIDENTAL, CONSEQUENTIAL, SPECIAL, PUNITIVE, EXEMPLARY DAMAGES, OR ANY OTHER DAMAGES ARISING OUT OF OR IN ANY WAY CONNECTED WITH THE SDK, ANY DERIVED SOFTWARE, ANY INFORMATION AVAILABLE IN CONNECTION THEREWITH, OR THE DELAY OR INABILITY TO USE THE SDK, ANY DERIVED SOFTWARE, OR ANY INFORMATION, EVEN IN THE EVENT OF FAULT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY, BREACH OF CONTRACT, OR BREACH OF ANY WARRANTY AND EVEN IF CLOUD9 HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. -THESE LIMITATIONS AND EXCLUSIONS REGARDING DAMAGES APPLY EVEN IF ANY REMEDY -FAILS. YOU ACKNOWLEDGE AND AGREE THAT YOUR SOLE AND EXCLUSIVE REMEDY FOR ANY -DISPUTE WITH CLOUD9 WITH REGARD TO THE SDK IS TO DISCONTINUE USE OF THE SDK AND -ANY DERIVED SOFTWARE. BECAUSE SOME STATES OR JURISDICTIONS DO NOT ALLOW THE -EXCLUSION OR THE LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL -DAMAGES, IN SUCH STATES OR JURISDICTIONS, CLOUD9, ITS LICENSORS, AND THEIR -AFFILIATES LIABILITY SHALL BE LIMITED TO THE FULL EXTENT PERMITTED BY LAW. +THESE LIMITATIONS AND EXCLUSIONS REGARDING DAMAGES APPLY EVEN IF ANY REMEDY FAILS. YOU ACKNOWLEDGE AND AGREE THAT YOUR SOLE AND EXCLUSIVE REMEDY FOR ANY DISPUTE WITH CLOUD9 WITH REGARD TO THE SDK IS TO DISCONTINUE USE OF THE SDK AND ANY DERIVED SOFTWARE. BECAUSE SOME STATES OR JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR THE LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, IN SUCH STATES OR JURISDICTIONS, CLOUD9, ITS LICENSORS, AND THEIR AFFILIATES LIABILITY SHALL BE LIMITED TO THE FULL EXTENT PERMITTED BY LAW. 10. Indemnification -10.1 You agree to defend, indemnify and hold harmless Cloud9, its licensors and -their affiliates from all liabilities, claims and expenses, including attorneys' -fees, that arise from or in connection with breach of this Agreement, use of the -SDK or any Derived Software, including, but not limited to, the creation, -distribution, promotion and use of any Derived Software. Cloud9 reserves the -right to assume the exclusive defense and control of any matter subject to -indemnification by you. +10.1 You agree to defend, indemnify and hold harmless Cloud9, its licensors and their affiliates from all liabilities, claims and expenses, including attorneys' fees, that arise from or in connection with breach of this Agreement, use of the SDK or any Derived Software, including, but not limited to, the creation, distribution, promotion and use of any Derived Software. Cloud9 reserves the right to assume the exclusive defense and control of any matter subject to indemnification by you. 11. Changes to the license agreement -11.1 Cloud9 may make changes to the License Agreement as it distributes new -versions of the SDK. When these changes are made, Cloud9 will make a new version -of the License Agreement available on the website where the SDK is made -available. +11.1 Cloud9 may make changes to the License Agreement as it distributes new versions of the SDK. When these changes are made, Cloud9 will make a new version of the License Agreement available on the website where the SDK is made available. 12. Applicable law, jurisdiction -12.1 This License Agreement, and your relationship with Cloud9 under this -License Agreement, shall be governed by the laws of the State of California -without regard to its conflict of laws provisions. You and Cloud9 agree to -submit to the exclusive jurisdiction of the courts located within the county of -Santa Clara, California to resolve any legal matter arising from this License -Agreement. Notwithstanding this, you agree that Cloud9 shall still be allowed to -apply for injunctive remedies. +12.1 This License Agreement, and your relationship with Cloud9 under this License Agreement, shall be governed by the laws of the State of California without regard to its conflict of laws provisions. You and Cloud9 agree to submit to the exclusive jurisdiction of the courts located within the county of Santa Clara, California to resolve any legal matter arising from this License Agreement. Notwithstanding this, you agree that Cloud9 shall still be allowed to apply for injunctive remedies. 13. General legal terms -13.1 This SDK License sets forth all rights for the user of the SDK and is the -entire agreement between the parties. This SDK License supersedes any other -communications with respect to the SDK. You agree that this SDK License is not -intended to confer and does not confer any rights or remedies upon any person -other than the parties to this SDK License. No provision hereof shall be deemed -waived unless such waiver shall be in writing and signed by Cloud9. If any -provision of this SDK License is held invalid or unenforceable, the remainder of -this SDK License shall continue in full force and effect, and such provision -shall be reformed only to the extent necessary to make it valid and enforceable. -The parties confirm that it is their wish that this Agreement has been written -in the English language only (or an equivalent type of urgent legal relief) in -any jurisdiction. +13.1 This SDK License sets forth all rights for the user of the SDK and is the entire agreement between the parties. This SDK License supersedes any other communications with respect to the SDK. You agree that this SDK License is not intended to confer and does not confer any rights or remedies upon any person other than the parties to this SDK License. No provision hereof shall be deemed waived unless such waiver shall be in writing and signed by Cloud9. If any provision of this SDK License is held invalid or unenforceable, the remainder of this SDK License shall continue in full force and effect, and such provision shall be reformed only to the extent necessary to make it valid and enforceable. The parties confirm that it is their wish that this Agreement has been written in the English language only (or an equivalent type of urgent legal relief) in any jurisdiction. -13.2 EXPORT RESTRICTIONS. YOU ACKNOWLEDGE THAT THE SDK IS OF U.S. ORIGIN. YOU -AGREE TO COMPLY WITH ALL APPLICABLE INTERNATIONAL AND NATIONAL LAWS THAT APPLY -TO THESE PRODUCTS, INCLUDING THE U.S. EXPORT ADMINISTRATION REGULATIONS, AS WELL -AS END-USER, END-USE AND DESTINATION RESTRICTIONS BY U.S. AND OTHER GOVERNMENTS. +13.2 EXPORT RESTRICTIONS. YOU ACKNOWLEDGE THAT THE SDK IS OF U.S. ORIGIN. YOU AGREE TO COMPLY WITH ALL APPLICABLE INTERNATIONAL AND NATIONAL LAWS THAT APPLY TO THESE PRODUCTS, INCLUDING THE U.S. EXPORT ADMINISTRATION REGULATIONS, AS WELL AS END-USER, END-USE AND DESTINATION RESTRICTIONS BY U.S. AND OTHER GOVERNMENTS. \ No newline at end of file diff --git a/README.md b/README.md index 95a2bf6f..ddb2650b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ Cloud9 3.0 SDK for Plugin Development -======================================= +======================================== This is the core repository for the Cloud9 v3 SDK. The SDK allows you to run a version of Cloud9 that allows you to develop plugins and create a custom IDE based on Cloud9. @@ -18,13 +18,14 @@ We also have a tutorial for how to get started working on the core plugins. [Che We have several documentation resources for you: - + +
SDK documentationhttp://cloud9-sdk.readme.io/v0.1/docs
SDK documentationhttps://TOBEFILLEDIN
API documentationhttp://docs.c9.io/api
User documentationhttp://docs.c9.io
User docs repohttps://github.com/c9/docs.c9.io
Please joing the mailinglist to get support or give support to the growing community of plugin developers: -https://groups.google.com/forum/#!forum/cloud9-sdk +https://groups.google.com/forum/#!forum/cloud9-plugin-development #### Installation #### @@ -75,6 +76,4 @@ To protect the interests of the Cloud9 contributors and users we require contrib 1. [The Individual CLA](https://docs.google.com/a/c9.io/forms/d/1MfmfrxqD_PNlNsuK0lC2KSelRLxGLGfh_wEcG0ijVvo/viewform): use this version if you're working on the Cloud9 SDK or open source plugins in your spare time, or can clearly claim ownership of copyright in what you'll be submitting. 2. [The Corporate CLA](https://docs.google.com/a/c9.io/forms/d/1vFejn4111GdnCNuQ6BfnJDaxdsUEMD4KCo1ayovAfu0/viewform): have your corporate lawyer review and submit this if your company is going to be contributing to the Cloud9 SDK and/or open source plugins. -If you want to contribute to the Cloud9 SDK and/or open source plugins please go to the online form, fill it out and submit it. - -Happy coding, Cloud9 \ No newline at end of file +If you want to contribute to the Cloud9 SDK and/or open source plugins please go to the online form, fill it out and submit it. \ No newline at end of file diff --git a/configs/cli.js b/configs/cli.js index fef801e9..94ff7df2 100644 --- a/configs/cli.js +++ b/configs/cli.js @@ -7,9 +7,6 @@ var APIHOST = process.env.C9_APIHOST || "api.c9.io"; // "api.c9.io"; var APIURL = APIHOST.indexOf("localhost") > -1 ? "http://" + APIHOST : "https://" + APIHOST; -var AUTHURL = APIHOST.indexOf("localhost") > -1 - ? "http://" + APIHOST - : "https://" + APIHOST.replace(/api\./, ""); return [ "./c9.core/ext", @@ -27,10 +24,7 @@ return [ }, "./c9.vfs.client/vfs.cli", "./c9.cli/cli", - { - packagePath: "./c9.cli/auth.bootstrap", - authUrl: AUTHURL - }, + "./c9.cli/auth.bootstrap", { packagePath: "./c9.cli.publish/publish", projectId: PID, diff --git a/configs/client-default-local.js b/configs/client-default-local.js new file mode 100644 index 00000000..b2ed60a1 --- /dev/null +++ b/configs/client-default-local.js @@ -0,0 +1,158 @@ +var join = require("path").join; + +module.exports = function(options) { + var config = require("./client-default")(options); + return module.exports.makeLocal(config, options); +}; + +module.exports.makeLocal = function(config, options) { + var c9Ws = options.remoteWorkspace; // true when opening c9 workspace as local + var root = options.workspaceDir; + + var nodeBin = options.nodeBin || ["node"]; + var settingDir = options.settingDir || options.installPath; + + if (!c9Ws) { + // Local version + options.local = true; + options.projectName = root.substr(root.lastIndexOf("/") + 1); + options.debug = 2; + options.env = "local"; + } + + for (var i = config.length - 1; i >= 0; i--) { + // if (config[i].packagePath == "plugins/c9.cli.bridge/bridge") + // config[i].port = 55556; + if (config[i].packagePath == "plugins/c9.ide.welcome/welcome" && !c9Ws) { + config[i].intro = + "Welcome to your brand new Cloud9. Use this welcome screen " + + "to tweak the look & feel of the Cloud9 user interface. " + + "If you prefer a more advanced IDE experience, you can choose " + + "to change the layout below. " + + "\n\n" + + "On the right you can find videos and documentation for Cloud9 " + + "IDE. Happy Coding!"; + } + // else if (config[i].packagePath == "plugins/c9.ide.login/login") { + // config.splice(i, 1); + // } + else if (config[i].packagePath == "plugins/c9.ide.run/run" && !c9Ws) { + config[i].runnerPath = join(settingDir, "/runners"); + } + else if (config[i].packagePath == "plugins/c9.ide.ui/menus") { + config[i].autoInit = false; + } + else if (config[i].packagePath == "plugins/c9.ide.save/autosave") { + config[i].slowChangeTimeout = 500; + } + else if (config[i].packagePath == "plugins/c9.ide.run.build/build" && !c9Ws) { + config[i].builderPath = join(settingDir, "/builders"); + } + else if (config[i].packagePath == "plugins/c9.ide.editors/metadata" && !c9Ws) { + config[i].path = join(settingDir, "/metadata"); + config[i].changeCheckInterval = 2000; + } + else if (config[i].packagePath == "plugins/c9.ide.feedback/feedback") { + config[i].screenshotSupport = false; + } + // else if (config[i].packagePath == "plugins/c9.ide.feedback/feedback") { + // config[i] = { + // packagePath : "plugins/c9.ide.help/help", + // staticPrefix : options.staticPrefix + "/plugins/c9.ide.help" + // }; + // } + + else if (config[i].packagePath == "plugins/c9.core/c9") { + config[i].local = true; + } + else if (config[i].packagePath == "plugins/c9.ide.clipboard/html5") + config[i].packagePath = "plugins/c9.ide.local/clipboard"; + else if (config[i].packagePath == "plugins/c9.ide.configuration/configure") + config[i].pathFromFavorite = true; + else if (config[i].packagePath == "plugins/c9.core/settings" && !c9Ws) { + // todo: Don't show console when opening a file? + // config[i].template = ; + config[i].projectConfigPath = join(settingDir, ""); + config[i].userConfigPath = join(settingDir, ""); + config[i].stateConfigPath = join(settingDir, ""); + } else if (config[i].packagePath == "plugins/c9.ide.log/log" && !c9Ws) { + config[i].source = "desktop"; + } else if (config[i].packagePath == "plugins/c9.ide.info/info" && c9Ws) { + config[i].packagePath = "plugins/c9.ide.local/info"; + } else if (config[i].packagePath == "plugins/c9.ide.ui/menus" && c9Ws) { + config[i].autoInit = false; + } else if (config[i].packagePath == "plugins/c9.ide.tree/tree") { + config[i].defaultExpanded = !config.hosted; + } + } + + // Add local modules + var includes = [{ + packagePath: "plugins/c9.ide.local/local", + options: options, + }, { + packagePath: "plugins/c9.ide.local/windowframe", + staticPrefix: options.staticPrefix + "/plugins/c9.ide.local" + }, { + packagePath: "plugins/c9.ide.local/update", + host: options.update && options.update.host || "localhost", // "update.c9.io", + port: options.update && options.update.port || "8888", // "443" + path: options.update && options.update.path, + protocol: options.update && options.update.protocol, + installPath: options.correctedInstallPath, + bashBin: options.bashBin, + nodeBin: nodeBin + }, { + packagePath: "plugins/c9.ide.local/projectmanager" + }, { + packagePath: "plugins/c9.ide.local/open" + }, { + packagePath: "plugins/c9.ide.local/nativemenus" + }, !c9Ws && { + packagePath: "plugins/c9.ide.local/info", + installPath: options.correctedInstallPath, + settingDir: settingDir, + cookie: options.user.cookie, + user: { + id: options.user.id, + name: options.user.name, + fullname: options.user.fullname, + email: options.user.email, + pubkey: options.user.pubkey + }, + project: { + id: options.project.id, + name: options.project.name, + contents: options.project.contents, + descr: options.project.descr + } + }].filter(Boolean); + + var excludes = c9Ws ? {} : { + "plugins/c9.ide.newresource/open": true, + "plugins/c9.ide.info/info": true, + // "plugins/c9.ide.login/login": true, + "plugins/c9.ide.collab/connect": true, + "plugins/c9.ide.collab/collab": true, + "plugins/c9.ide.collab/collabpanel": true, + "plugins/c9.ide.collab/workspace": true, + "plugins/c9.ide.collab/util": true, + "plugins/c9.ide.collab/ot/document": true, + "plugins/c9.ide.collab/cursor_layer": true, + "plugins/c9.ide.collab/author_layer": true, + "plugins/c9.ide.collab/timeslider/timeslider": true, + "plugins/c9.ide.notifications/notifications": true, + "plugins/c9.ide.collab/members/members_panel": true, + "plugins/c9.ide.collab/share/share": true, + "plugins/c9.ide.collab/members/members": true, + "plugins/c9.ide.collab/chat/chat": true, + "plugins/c9.ide.feedback/nps": true, + "plugins/c9.ide.download/download": true + }; + + config = config.concat(includes).filter(function (p) { + return !excludes[p] && !excludes[p.packagePath]; + }); + + return config; +}; diff --git a/node_modules/ace_tree/LICENSE b/node_modules/ace_tree/LICENSE deleted file mode 100644 index d188c3e4..00000000 --- a/node_modules/ace_tree/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ - -Copyright (C) 2015, Ajax.org B.V. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -See . - diff --git a/node_modules/ace_tree/package.json b/node_modules/ace_tree/package.json index f25821e7..7c475012 100644 --- a/node_modules/ace_tree/package.json +++ b/node_modules/ace_tree/package.json @@ -2,5 +2,7 @@ "name": "ace_tree", "version": "0.1.0", "devDependencies": { + "asyncjs": "0.0.x", + "amd-loader": "~0.0.4" } } \ No newline at end of file diff --git a/node_modules/ace_tree/todo.md b/node_modules/ace_tree/todo.md new file mode 100644 index 00000000..52d269a2 --- /dev/null +++ b/node_modules/ace_tree/todo.md @@ -0,0 +1,12 @@ +todo + +now +* [x] mouse handler +* [x] default commands + +future +* [ ] multiple selection +* [ ] variable line heights +* [ ] profile +* [ ] cleanup +* [ ] sort items? \ No newline at end of file diff --git a/node_modules/kaefer/lib/client.js b/node_modules/kaefer/lib/client.js index 3f6bd7f0..5586f63d 100644 --- a/node_modules/kaefer/lib/client.js +++ b/node_modules/kaefer/lib/client.js @@ -86,29 +86,27 @@ var connectClient = module.exports = function(connectEio, options) { }); } - var timer; - function reconnect(delay) { - if (isReconnecting && typeof delay !== "number") + function reconnect() { + if (isReconnecting) return; reconnectSocket.setSocket(null); connectAttempts += 1; - if (typeof delay !== "number") { - if (connectAttempts < 10) { - delay = 250; - } - else { - delay = Math.min(60000, 250 * Math.pow(2, connectAttempts - 10)); - } + var delay = 250; + if (connectAttempts > 10) { + delay = 10 * 1000; + } + else if (connectAttempts > 5) { + delay = 5 * 1000; + } + else if (connectAttempts > 3) { + delay = 1 * 1000; } isReconnecting = true; console.log("Schedule re-connect in: " + delay); - socket.emit("reconnectDelay", { delay: delay }); - - clearTimeout(timer); - timer = setTimeout(function() { + setTimeout(function() { isReconnecting = false; connect(); }, delay); diff --git a/package.json b/package.json index ebb512b7..57cec873 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "through": "2.2.0", "tmp": "~0.0.20", "uglify-js": "2.4.16", + "ui": "", "ws": "0.4.31" }, "optionalDependencies": { @@ -58,12 +59,12 @@ "c9.ide.language.javascript.tern": "#3d678a103a", "c9.ide.language.javascript.infer": "#1ae097af44", "c9.ide.language.jsonalyzer": "#45a20496be", - "c9.ide.collab": "#fabc22dda7", + "c9.ide.collab": "#08536cf0fe", "c9.ide.local": "#d5c324ee5b", "c9.ide.find": "#be3bca94b7", "c9.ide.find.infiles": "#462928475c", "c9.ide.find.replace": "#fe41fa768d", - "c9.ide.run.debug": "#9a05fadc55", + "c9.ide.run.debug": "#b734a2a47f", "c9.ide.ace.emmet": "#e5f1a92ac3", "c9.ide.ace.gotoline": "#4d1a93172c", "c9.ide.ace.keymaps": "#422e83553b", @@ -73,7 +74,7 @@ "c9.ide.ace.stripws": "#34426a03d1", "c9.ide.behaviors": "#f5aaf10aff", "c9.ide.closeconfirmation": "#a28bfd8272", - "c9.ide.configuration": "#b8470f4107", + "c9.ide.configuration": "#8627b7d37d", "c9.ide.dialog.wizard": "#a588b64050", "c9.ide.fontawesome": "#781602c5d8", "c9.ide.format": "#1ae38e60e6", diff --git a/plugins/c9.cli.publish/README.md b/plugins/c9.cli.publish/README.md index 11a1a58c..0a408385 100644 --- a/plugins/c9.cli.publish/README.md +++ b/plugins/c9.cli.publish/README.md @@ -20,5 +20,3 @@ curl -XPOST -k -v -u timjrobinson:password https://api.c9.dev/projects/90/instal Example install command: NODE_TLS_REJECT_UNAUTHORIZED=0 C9_APIHOST=api.c9.dev C9_PID=90 c9 install - -NODE_TLS_REJECT_UNAUTHORIZED=0 C9_APIHOST=api.cloud9beta.com c9 install diff --git a/plugins/c9.cli.publish/publish.js b/plugins/c9.cli.publish/publish.js index 880a669f..7eefabf2 100644 --- a/plugins/c9.cli.publish/publish.js +++ b/plugins/c9.cli.publish/publish.js @@ -539,11 +539,9 @@ define(function(require, exports, module) { form.append('options', JSON.stringify(json.plugins)); form.append('package', fs.createReadStream(zipFilePath)); - var path = "/packages/" + json.name - + "/versions?access_token=" - + encodeURIComponent(auth.accessToken); + var path = "/packages/" + json.name + "/versions?access_token=" + auth.accessToken; var host = APIHOST.split(":")[0] - var port = parseInt(APIHOST.split(":")[1]) || null; + var port = parseInt(APIHOST.split(":")[1] || 80); var request = http.request({ agent: false, @@ -652,11 +650,8 @@ define(function(require, exports, module) { var gzPath = join(os.tmpDir(), name + "@" + version + ".tar.gz"); var file = fs.createWriteStream(gzPath); - var path = "/packages/" + name + "/versions/" + version - + "/download?access_token=" - + encodeURIComponent(auth.accessToken); - var host = APIHOST.split(":")[0]; - var port = parseInt(APIHOST.split(":")[1]) || null; + var host = APIHOST.split(":")[0] + var port = parseInt(APIHOST.split(":")[1] || 80); var request = http.get({ agent: false, @@ -664,7 +659,8 @@ define(function(require, exports, module) { host: host, port: port, auth: BASICAUTH, - path: path + path: "/packages/" + name + "/versions/" + version + + "/download?access_token=" + auth.accessToken }, function(response){ response.pipe(file); }); diff --git a/plugins/c9.cli/auth.bootstrap.js b/plugins/c9.cli/auth.bootstrap.js index a24fd471..a9cb44af 100644 --- a/plugins/c9.cli/auth.bootstrap.js +++ b/plugins/c9.cli/auth.bootstrap.js @@ -15,7 +15,7 @@ define(function(require, exports, module) { var plugin = new Plugin("Ajax.org", main.consumes); // TODO read from options - var AUTHURL = options.authUrl; + var AUTHURL = "https://c9.dev"; var AUTHPATH = process.env.HOME + "/.c9/.auth"; var lastToken; diff --git a/plugins/c9.core/c9.js b/plugins/c9.core/c9.js index 6e7f9b1c..7f0947d2 100644 --- a/plugins/c9.core/c9.js +++ b/plugins/c9.core/c9.js @@ -57,7 +57,7 @@ define(function(require, module, exports) { }, plugin); vfs.on("disconnect", function(reason) { - setStatus(state & ~STORAGE & ~PROCESS & ~NETWORK); + setStatus(status & ~STORAGE & ~PROCESS & ~NETWORK); emit("disconnect"); }, plugin); @@ -67,7 +67,7 @@ define(function(require, module, exports) { }, plugin); vfs.on("error", function(message) { - setStatus(state & ~STORAGE & ~PROCESS); + setStatus(status & ~STORAGE & ~PROCESS); // TODO: Don't display all errors? if (emit("showerrormessage", message) !== false) { console.error( diff --git a/plugins/c9.core/ext.js b/plugins/c9.core/ext.js index 42f3bbd2..734d40da 100644 --- a/plugins/c9.core/ext.js +++ b/plugins/c9.core/ext.js @@ -888,8 +888,8 @@ define(function(require, exports, module) { * @fires newListener **/ on: function(eventName, callback, plugin){ - // if (!declaredEvents[eventName]) - // console.warn("Missing event description or unknown event '" + eventName + "' for plugin '" + name + "'", new Error().stack); + if (!declaredEvents[eventName]) + console.warn("Missing event description or unknown event '" + eventName + "' for plugin '" + name + "'", new Error().stack); event.on(eventName, callback, plugin); }, @@ -900,8 +900,8 @@ define(function(require, exports, module) { * @param {Function} callback the function called when the event is fired **/ once: function(eventName, callback){ - // if (!declaredEvents[eventName]) - // console.warn("Missing event description or unknown event '" + eventName + "' for plugin '" + name + "'"); + if (!declaredEvents[eventName]) + console.warn("Missing event description or unknown event '" + eventName + "' for plugin '" + name + "'"); event.once(eventName, callback); }, diff --git a/plugins/c9.error/raygun.connect.js b/plugins/c9.error/raygun.connect.js index 37848ec3..d2793f39 100644 --- a/plugins/c9.error/raygun.connect.js +++ b/plugins/c9.error/raygun.connect.js @@ -71,11 +71,6 @@ function plugin(options, imports, register) { email: req.user.email }; } - else if (req.session) { - customData.user = { - id: req.session.uid - }; - } raygunClient.send(err, customData, function() {}, { host: parsedUrl.hostname, diff --git a/plugins/c9.ide.ace/themes.js b/plugins/c9.ide.ace/themes.js index 802bd111..3e05b673 100644 --- a/plugins/c9.ide.ace/themes.js +++ b/plugins/c9.ide.ace/themes.js @@ -151,7 +151,7 @@ define(function(require, exports, module) { settings.set("user/general/@skin", e.value); }; var setTheme = function(e) { - [rb1, rb2, rb5].some(function(rb) { + [rb1, rb2, rb3, rb4, rb5].some(function(rb) { if (rb.value == e.value) { rb.select(); return true; diff --git a/plugins/c9.ide.auth/auth.js b/plugins/c9.ide.auth/auth.js index 8508ba0f..99cbf9db 100644 --- a/plugins/c9.ide.auth/auth.js +++ b/plugins/c9.ide.auth/auth.js @@ -168,9 +168,6 @@ define(function(require, exports, module) { get accessToken() { return accessToken; }, set accessToken(v) { accessToken = v; loggedIn = true;}, - /** - * - */ login: login, /** diff --git a/plugins/c9.ide.dialog.common/error.css b/plugins/c9.ide.dialog.common/error.css index 96f0a115..f3d14fb7 100644 --- a/plugins/c9.ide.dialog.common/error.css +++ b/plugins/c9.ide.dialog.common/error.css @@ -1,23 +1,18 @@ -.errorlabel, .disconnectlabel{ +.errorlabel{ position: absolute; left: 0; right: 0; top: 0; z-index: 10000000; - /*text-align: center;*/ + text-align: center; pointer-events: none; .font-smoothing(@error-font-smoothing); } -.errorlabel.anim, .disconnectlabel.anim{ +.errorlabel.anim{ transition: top 0.2s; -moz-transition: top 0.2s; /* Gecko */ -webkit-transition: top 0.2s; /* Safari */ } -.errorlabel.fade-in{ - transition: opacity 0.2s; - -moz-transition: opacity 0.2s; /* Gecko */ - -webkit-transition: opacity 0.2s; /* Safari */ -} .errorlabel div{ background: @error-background; padding: @error-padding; @@ -31,9 +26,6 @@ max-width: 100%; pointer-events: auto; } -.errorlabel.fade-in div{ - border-radius: 3px; -} .errorlabel u.close{ .image-2x("@{image-path}/@{error-close-image}", 42px, 28px); background-position: @error-close-idle-position; @@ -41,7 +33,7 @@ height: 14px; position: absolute; right: 8px; - top: 6px; + top: 8px; cursor: pointer; } .errorlabel u.close:hover{ @@ -53,25 +45,4 @@ .errorlabel div span{ border-bottom: 1px dotted rgb(255, 174, 174); cursor: help; -} - -.disconnectlabel{ - z-index: 10000001; -} -.disconnectlabel div{ - background: @disconnect-background; - padding: @disconnect-padding; - color: @disconnect-color; - border-radius: 0 0 3px 3px; - box-shadow: @disconnect-box-shadow; - line-height: 1.4; - display: inline-block; - position: relative; - word-wrap: break-word; - text-align: center; - width: 300px; - pointer-events: auto; -} -.disconnectlabel u{ - cursor: pointer; } \ No newline at end of file diff --git a/plugins/c9.ide.dialog.common/error.js b/plugins/c9.ide.dialog.common/error.js index 3d28bb20..3642818a 100644 --- a/plugins/c9.ide.dialog.common/error.js +++ b/plugins/c9.ide.dialog.common/error.js @@ -12,72 +12,22 @@ define(function(require, exports, module) { /***** Initialization *****/ var plugin = new Plugin("Ajax.org", main.consumes); - var emit = plugin.getEmitter(); var topPx = 0; + var error, hideTimeout; var lastCookie = 0; - var offset = 0; - var error, hideTimeout, disconnect; - - var DISCONNECTDELAY = 1000; + var loaded = false; function load() { + if (loaded) return false; + loaded = true; + ui.insertCss(require("text!./error.css"), options.staticPrefix, plugin); } - function initDisconnectEvents(vfs){ - var timer; - - vfs.once("connect", function(){ - vfs.connection.on("reconnectDelay", function(e){ - clearInterval(timer); - - var delay = e.delay; - if (delay > 999) { - timer = setInterval(function(){ - if (vfs.connected) - return clearInterval(timer); - - delay -= 1000; - showDisconnect({ delay: delay }); - - if (delay <= 0) - clearInterval(timer); - }, 1000); - } - - showDisconnect(e); - }); - }); - vfs.on("connect", function(){ - hideDisconnect(); - }); - vfs.on("disconnect", function(){ - // setTimeout(function(){ - // showDisconnect(); - // }, DISCONNECTDELAY); - }); - vfs.on("connecting", function(){ - showDisconnect({ connecting: true }); - }); - plugin.on("retryConnect", function(){ - vfs.connection.reconnect(0); - }); - } - /***** Methods *****/ - function getCenterX(){ - var bartools = document.querySelector(".bartools"); - if (!bartools) return 0; // For testing - - var b1 = bartools.getBoundingClientRect(); - var b2 = bartools.nextSibling.getBoundingClientRect(); - - return b1.left + b1.width + ((b2.left - b1.left - b1.width)/2); - } - function show(message, timeout) { // Error message container if (!error) { @@ -113,21 +63,18 @@ define(function(require, exports, module) { error.innerHTML = "
" + messageString + "
"; - error.style.opacity = 0; error.style.display = "block"; - error.style.top = (offset - (error.offsetHeight - 10 + topPx)) + "px"; - error.firstChild.style.marginLeft = Math.max(0, (getCenterX() - (error.firstChild.offsetWidth / 2))) + "px"; + error.style.top = (-1 * error.offsetHeight - 10 + topPx) + "px"; // Start anim setTimeout(function() { - error.className = "errorlabel anim " + (offset > 0 ? "fade-in" : ""); - error.style.top = (offset + topPx) + "px"; - error.style.opacity = 1; + error.className = "errorlabel anim"; + error.style.top = topPx + "px"; }, 10); clearTimeout(hideTimeout); if (!(timeout < 0)) - hideTimeout = setTimeout(hide, timeout || 15000); + setTimeout(hide, timeout || 15000); }); return ++lastCookie; @@ -142,96 +89,19 @@ define(function(require, exports, module) { if (!error || error.style.display === "none") return callback && callback(); - error.className = "errorlabel anim " + (offset > 0 ? "fade-in" : ""); - if (offset > 0) - error.style.opacity = 0; - else - error.style.top = (-1 * error.offsetHeight - 10 + topPx) + "px"; - + error.className = "errorlabel anim"; + error.style.top = (-1 * error.offsetHeight - 10 + topPx) + "px"; setTimeout(function() { error.style.display = "none"; callback && callback(); }, 220); } - function showDisconnect(options){ - // Error message container - if (!disconnect) { - disconnect = document.body.appendChild(document.createElement("div")); - disconnect.className = "disconnectlabel"; - disconnect.addEventListener("mouseup", function(e) { - if (e.target.tagName == "U") - emit("retryConnect"); - }); - } - - var message; - if (!options || options.delay < 1000 || options.connecting) - message = "Reconnecting..."; - else if (options.delay) - message = "Reconnecting in " + Math.ceil(options.delay/1000) - + " seconds." - + (options.delay < 2001 ? "" : " Retry Now."); - else - message = "Reconnecting..."; - - disconnect.innerHTML = "
" + message + "
"; - disconnect.firstChild.style.marginLeft - = Math.max(0, (getCenterX() - 150)) + "px"; - - if (disconnect.style.display == "block") - return; - - disconnect.style.display = "block"; - disconnect.style.top = (-1 * disconnect.offsetHeight - 10 + topPx) + "px"; - - // Start anim - setTimeout(function() { - disconnect.className = "disconnectlabel anim"; - disconnect.style.top = (topPx) + "px"; - }, 10); - - offset = 28; - - // document.querySelector(".c9-offline").addEventListener("click", function(){ - // alert("Offline Notication", "You are currently offline.", - // "This indicator notifies you that Cloud9 is unable to reach " - // + "the server. This usually happens because you are offline. " - // + "Some features will be disabled until the " - // + "network connection becomes available again. " - // + "This notication could also show when the server is " - // + "unreachable due to other reasons. Sometimes a refresh of " - // + "the tab will fix an issue. Please e-mail " - // + "support@c9.io for further problem resolution."); - // }, false); - } - - function hideDisconnect(cookie, callback) { - if (!disconnect || disconnect.style.display === "none") - return callback && callback(); - - disconnect.className = "disconnectlabel anim"; - disconnect.style.top = (-1 * disconnect.offsetHeight - 10 + topPx) + "px"; - setTimeout(function() { - disconnect.style.display = "none"; - callback && callback(); - }, 220); - - offset = 0; - } - /***** Lifecycle *****/ plugin.on("load", function() { load(); }); - plugin.on("unload", function() { - topPx = 0; - lastCookie = 0; - offset = 0; - error = null; - hideTimeout = null; - }); /***** Register and define API *****/ @@ -246,19 +116,6 @@ define(function(require, exports, module) { get top(){ return topPx; }, set top(value){ topPx = value; }, - get vfs(){ throw new Error("Permission Denied"); }, - set vfs(v){ initDisconnectEvents(v); }, - - /** - * - */ - showDisconnect: showDisconnect, - - /** - * - */ - hideDisconnect: hideDisconnect, - /** * Displays an error message in the main error reporting UI. * @param {String} message The message to display. diff --git a/plugins/c9.ide.keys/editor.js b/plugins/c9.ide.keys/editor.js index 3dd01111..824db379 100644 --- a/plugins/c9.ide.keys/editor.js +++ b/plugins/c9.ide.keys/editor.js @@ -301,7 +301,6 @@ define(function(require, exports, module) { setTimeout(function() { ace.treeEditor.endRename(false); }); - return disable; } } @@ -413,8 +412,6 @@ define(function(require, exports, module) { if (!item.name) return; var groupName = item.group || "General"; - if (groupName == "ignore") return; - var group = groups[groupName]; if (!group) root.push(group = groups[groupName] = { @@ -456,7 +453,7 @@ define(function(require, exports, module) { } function editUserKeys(tab) { - // preferences.hide(); + preferences.hide(); var keys = settings.getJson("user/key-bindings") || []; var value = "// Edit this keymap file and save to apply.\n[\n"; @@ -470,7 +467,7 @@ define(function(require, exports, module) { }).join(",\n"); if (!keys.length) - value += ' // { "command": "nexttab", "keys": ["Ctrl-Tab"] }'; + value += ' // { "command": "nexttab", "keys": ["Alt-`", "Ctrl-Tab"] }'; value += "\n]"; diff --git a/plugins/c9.ide.layout.classic/layout.js b/plugins/c9.ide.layout.classic/layout.js index 5d3aee91..c3124ceb 100644 --- a/plugins/c9.ide.layout.classic/layout.js +++ b/plugins/c9.ide.layout.classic/layout.js @@ -8,11 +8,11 @@ define(function(require, exports, module) { function main(options, imports, register) { var c9 = imports.c9; + var alert = imports["dialog.alert"].show; var Plugin = imports.Plugin; + var question = imports["dialog.question"]; var settings = imports.settings; var commands = imports.commands; - var alert = imports["dialog.alert"].show; - var question = imports["dialog.question"]; var preload = imports["layout.preload"]; var anims = imports.anims; var ui = imports.ui; @@ -126,6 +126,33 @@ define(function(require, exports, module) { img.src = options.staticPrefix + "/images/" + p; }); + var hideOffline; + c9.on("stateChange", function(e) { + // Online + if (e.state & c9.NETWORK && e.state & c9.STORAGE) { + hideOffline && hideOffline(); + } + // Offline + else if (!hideOffline || hideOffline.hasClosed()) { + hideOffline = notify("
No internet " + + "connection detected. Cloud9 will automatically try to " + + "reconnect when it detects an internet connection." + + "
", true, 1000); + + document.querySelector(".c9-offline").addEventListener("click", function(){ + alert("Offline Notication", "You are currently offline.", + "This indicator notifies you that Cloud9 is unable to reach " + + "the server. This usually happens because you are offline. " + + "Some features will be disabled until the " + + "network connection becomes available again. " + + "This notication could also show when the server is " + + "unreachable due to other reasons. Sometimes a refresh of " + + "the tab will fix an issue. Please e-mail " + + "support@c9.io for further problem resolution."); + }, false); + } + }); + window.addEventListener("resize", resize, false); window.addEventListener("focus", resize, false); @@ -515,21 +542,6 @@ define(function(require, exports, module) { window.removeEventListener("resize", resize); if (removeTheme) removeTheme(); - - logobar = null; - removeTheme = null; - theme = null; - c9console = null; - menus = null; - tabManager = null; - panels = null; - userLayout = null; - ignoreTheme = null; - notify = null; - hideFlagUpdate = null; - activeFindArea = null; - defaultFindArea = null; - activating = null; }); /***** Register and define API *****/ diff --git a/plugins/c9.ide.layout.classic/themes/default-dark-gray.less b/plugins/c9.ide.layout.classic/themes/default-dark-gray.less index f78fd8a0..6c78b067 100644 --- a/plugins/c9.ide.layout.classic/themes/default-dark-gray.less +++ b/plugins/c9.ide.layout.classic/themes/default-dark-gray.less @@ -632,12 +632,6 @@ @error-close-hover-position: -14px -14px; @error-close-active-position: -28px -14px; -// Disconnect Dialog -@disconnect-background: #D8B112; -@disconnect-color: @error-color; -@disconnect-padding: @error-padding; -@disconnect-box-shadow: @error-box-shadow; - // Offline Dialog @offline-gradient: linear-gradient(top, rgb(60, 112, 153) 0%, rgb(61, 97, 126) 100%); @offline-border-bottom: 1px solid rgba(0, 0, 0, 0.61); diff --git a/plugins/c9.ide.layout.classic/themes/default-dark.less b/plugins/c9.ide.layout.classic/themes/default-dark.less index 26707588..fd62d0f3 100644 --- a/plugins/c9.ide.layout.classic/themes/default-dark.less +++ b/plugins/c9.ide.layout.classic/themes/default-dark.less @@ -632,12 +632,6 @@ @error-close-hover-position: -14px -14px; @error-close-active-position: -28px -14px; -// Disconnect Dialog -@disconnect-background: #D8B112; -@disconnect-color: @error-color; -@disconnect-padding: @error-padding; -@disconnect-box-shadow: @error-box-shadow; - // Offline Dialog @offline-gradient: linear-gradient(top, rgb(60, 112, 153) 0%, rgb(61, 97, 126) 100%); @offline-border-bottom: 1px solid rgba(0, 0, 0, 0.61); diff --git a/plugins/c9.ide.layout.classic/themes/default-flat-light.less b/plugins/c9.ide.layout.classic/themes/default-flat-light.less index 6425061b..b23ca74b 100644 --- a/plugins/c9.ide.layout.classic/themes/default-flat-light.less +++ b/plugins/c9.ide.layout.classic/themes/default-flat-light.less @@ -632,12 +632,6 @@ @error-close-hover-position: -14px -14px; @error-close-active-position: -28px -14px; -// Disconnect Dialog -@disconnect-background: #D8B112; -@disconnect-color: @error-color; -@disconnect-padding: @error-padding; -@disconnect-box-shadow: @error-box-shadow; - // Offline Dialog @offline-gradient: linear-gradient(top, #3d9ac4 0%, #3d9ac4 100%); @offline-border-bottom: 0; diff --git a/plugins/c9.ide.layout.classic/themes/default-light-gray.less b/plugins/c9.ide.layout.classic/themes/default-light-gray.less index ebf4895d..39472337 100644 --- a/plugins/c9.ide.layout.classic/themes/default-light-gray.less +++ b/plugins/c9.ide.layout.classic/themes/default-light-gray.less @@ -632,12 +632,6 @@ @error-close-hover-position: -14px -14px; @error-close-active-position: -28px -14px; -// Disconnect Dialog -@disconnect-background: #D8B112; -@disconnect-color: @error-color; -@disconnect-padding: @error-padding; -@disconnect-box-shadow: @error-box-shadow; - // Offline Dialog @offline-gradient: linear-gradient(top, rgb(60, 112, 153) 0%, rgb(61, 97, 126) 100%); @offline-border-bottom: 1px solid rgba(0, 0, 0, 0.61); diff --git a/plugins/c9.ide.layout.classic/themes/default-light.less b/plugins/c9.ide.layout.classic/themes/default-light.less index 982b7177..48928aee 100644 --- a/plugins/c9.ide.layout.classic/themes/default-light.less +++ b/plugins/c9.ide.layout.classic/themes/default-light.less @@ -632,12 +632,6 @@ @error-close-hover-position: -14px -14px; @error-close-active-position: -28px -14px; -// Disconnect Dialog -@disconnect-background: #D8B112; -@disconnect-color: @error-color; -@disconnect-padding: @error-padding; -@disconnect-box-shadow: @error-box-shadow; - // Offline Dialog @offline-gradient: linear-gradient(top, rgb(60, 112, 153) 0%, rgb(61, 97, 126) 100%); @offline-border-bottom: 1px solid rgba(0, 0, 0, 0.61); diff --git a/plugins/c9.ide.layout.classic/themes/flat-light.less b/plugins/c9.ide.layout.classic/themes/flat-light.less index bcee40a5..ae95b3c6 100644 --- a/plugins/c9.ide.layout.classic/themes/flat-light.less +++ b/plugins/c9.ide.layout.classic/themes/flat-light.less @@ -156,14 +156,14 @@ .console>.hbox>.console_close_btn{ width: 26px !important; height: 30px !important; - margin: 11px -4px 0px 3px !important; + margin: 4px -4px 0px 3px !important; box-sizing: border-box; } .console>.hbox>.divider_console{ display: none; } .console>.hbox>.btn_console{ - margin: 7px 0px 0px 0px !important; + margin: 0px 0px 0px 0px !important; border-radius: 0; width: 26px !important; height: 31px !important; diff --git a/plugins/c9.ide.login/login.css b/plugins/c9.ide.login/login.css new file mode 100644 index 00000000..3a5d245e --- /dev/null +++ b/plugins/c9.ide.login/login.css @@ -0,0 +1,36 @@ +.c9-menu-btn.titlebar{ + position: absolute; + z-index: 100000000; + right: 24px; + top: 1px; + height: 19px; +} + +.c9-menu-btn.titlebar:not(.c9-menu-btnDown){ + height: 18px; + border-bottom: 1px solid black; +} + +.fullscreen>.btnName{ + display: none; +} + +.btnName.c9-menu-btnIcon{ + text-indent: -2000px; + overflow: hidden; +} +.btnName{ + padding: @menu-name-button-padding; + margin-left: @menu-name-button-diff !important; +} +.btnName.c9-menu-btnmenuDown{ + margin-left: 0 !important; +} +.btnName .icon{ + background-size: @menu-name-button-icon-width @menu-name-button-icon-height !important; + top: @menu-name-button-icon-top !important; + left: @menu-name-button-icon-left !important; + width: @menu-name-button-icon-width; + height: @menu-name-button-icon-height; + border-radius: @menu-name-button-icon-border-radius; +} \ No newline at end of file diff --git a/plugins/c9.ide.login/login.js b/plugins/c9.ide.login/login.js new file mode 100644 index 00000000..817480a5 --- /dev/null +++ b/plugins/c9.ide.login/login.js @@ -0,0 +1,194 @@ +define(function(require, exports, module) { + main.consumes = [ + "Plugin", "ui", "menus", "info", "layout", "http", "util", + "vfs.endpoint", "auth", "dialog.alert", "c9" + ]; + main.provides = ["login"]; + return main; + + function main(options, imports, register) { + var Plugin = imports.Plugin; + var ui = imports.ui; + var c9 = imports.c9; + var menus = imports.menus; + var layout = imports.layout; + var http = imports.http; + var util = imports.util; + var info = imports.info; + var auth = imports.auth; + var alert = imports["dialog.alert"].show; + + var vfsEndpoint = imports["vfs.endpoint"]; + + /***** Initialization *****/ + + var ideBaseUrl = options.ideBaseUrl; + var dashboardUrl = options.dashboardUrl; + var accountUrl = options.accountUrl; + var lastUser, mnuUser; + + var plugin = new Plugin("Ajax.org", main.consumes); + var emit = plugin.getEmitter(); + + var loaded = false; + function load() { + if (loaded) return false; + loaded = true; + + info.getUser(function(err, user) { + updateButton({user: user}); + }); + + auth.on("relogin", onReLogin); + } + + /***** Methods *****/ + + function updateButton(e) { + var user = e.user; + if (lastUser && lastUser.id == user.id) + return; + plugin.cleanUp(); + info.on("change", updateButton, plugin); + createButton(user); + lastUser = user; + + emit.sticky("ready", { name: user.fullname, id: user.id }, plugin); + } + + function createButton(user) { + var name = "user_" + user.id; + + // todo cleanup seems to not work well + // without this menu is empty after logging out and back in + if (lastUser) + menus.remove("user_" + lastUser.id); + menus.remove(name); + + var parent = layout.findParent(plugin); + + // Insert CSS + ui.insertCss(require("text!./login.css"), plugin); + + // Create Menu + mnuUser = new ui.menu(); + plugin.addElement(mnuUser); + + // Add named button + var icon = util.getGravatarUrl(user.email, 32, ""); + menus.addItemByPath(name + "/", mnuUser, 110000, plugin); + + // Add Divider + ui.insertByIndex(parent, new ui.divider({ + skin: "c9-divider-double", + "class" : "extrasdivider" + }), 870, plugin); + + // Add sub menu items + var c = 500; + menus.addItemByPath(name + "/Dashboard", new ui.item({ + onclick: function() { window.open(dashboardUrl); } + }), c += 100, plugin); + menus.addItemByPath(name + "/Account", new ui.item({ + onclick: function() { window.open(accountUrl); } + }), c += 100, plugin); + menus.addItemByPath(name + "/Home", new ui.item({ + onclick: function() { window.open(ideBaseUrl); } + }), c += 100, plugin); + + if (!options.noLogout) { + menus.addItemByPath(name + "/~", new ui.divider(), c += 100, plugin); + menus.addItemByPath(name + "/Log out", new ui.item({ + onclick: function() { + if (!c9.local) + return signout(); + auth.logout(function() { + info.login(true); + }); + } + }), c += 100, plugin); + } + + var button = menus.get(name).item; + button.setAttribute("class", "btnName"); + button.setAttribute("icon", icon); + button.setAttribute("iconsize", "16px 16px"); + button.setAttribute("tooltip", user.fullname); + button.setAttribute("caption", user.fullname); + ui.insertByIndex(parent, button, 600, plugin); + + if (c9.local) { + function minimize(){ + apf.document.documentElement.appendChild(button); + ui.setStyleClass(button.$ext, "titlebar"); + } + function restore(){ + ui.insertByIndex(parent, button, 870, plugin); + ui.setStyleClass(button.$ext, "", ["titlebar"]); + } + + menus.on("minimize", minimize, plugin); + menus.on("restore", restore, plugin); + + if (menus.minimized) + minimize(); + } + } + + function signout() { + vfsEndpoint.clearCache(); + auth.logout(function() { location.href = ideBaseUrl; }); + } + + function onReLogin() { + if (!c9.local) { + alert("Logged out", + "You have been logged in as a different user", + "Please hit OK to reload the IDE.", + function() { + vfsEndpoint.clearCache(); + auth.logout(function() { + document.location.reload(); + }); + }); + } + } + + /***** Lifecycle *****/ + + plugin.on("load", function() { + load(); + }); + plugin.on("enable", function() { + + }); + plugin.on("disable", function() { + + }); + plugin.on("unload", function() { + loaded = false; + }); + + /***** Register and define API *****/ + + /** + * + **/ + plugin.freezePublicAPI({ + get menu(){ return mnuUser; }, + + _events: [ + /** + * @event ready + */ + "ready" + ], + createButton: createButton, + updateButton: updateButton + }); + + register(null, { + login: plugin + }); + } +}); \ No newline at end of file diff --git a/plugins/c9.ide.plugins/debug.js b/plugins/c9.ide.plugins/debug.js index bf7dd44b..17c2c97e 100644 --- a/plugins/c9.ide.plugins/debug.js +++ b/plugins/c9.ide.plugins/debug.js @@ -46,9 +46,9 @@ define(function(require, exports, module) { menus.addItemByPath("Tools/Developer", null, 100100, plugin); menus.addItemByPath("Tools/Developer/Start in Debug Mode", new ui.item({ onclick: function(){ - var url = location.href + (location.href.indexOf("?") > -1 + var url = location.href + (location.href.indexOf("?") > -1) ? "&debug=2" - : "?debug=2"); + : "?debug=2"; window.open(url); } }), 100100, plugin); @@ -139,12 +139,10 @@ define(function(require, exports, module) { watch("~/.c9/plugins/" + pluginPath); var cfg = options.plugins[path]; - var host = vfs.baseUrl + "/"; - var base = join(String(c9.projectId), - "plugins", auth.accessToken); - - cfg.packagePath = host + join(base, pluginPath.replace(/^plugins\//, "")); - cfg.staticPrefix = host + join(base, name); + cfg.packagePath = join(vfs.baseUrl, c9.projectId, "plugins", + auth.accessToken, pluginPath.replace(/^plugins\//, "")); + cfg.staticPrefix = join(vfs.baseUrl, c9.projectId, "plugins", + auth.accessToken, name); cfg.apikey = "0000000000000000000000000000="; config.push(cfg); diff --git a/plugins/c9.ide.plugins/loader.js b/plugins/c9.ide.plugins/loader.js index 45c5a41b..d6320a8c 100644 --- a/plugins/c9.ide.plugins/loader.js +++ b/plugins/c9.ide.plugins/loader.js @@ -66,11 +66,9 @@ define(function(require, exports, module) { names.push(name); var path = options.packagePath + ".js"; - var host = vfs.baseUrl + "/"; - var base = join(String(c9.projectId), "plugins", auth.accessToken); - - options.packagePath = host + join(base, path.replace(/^plugins\//, "")); - options.staticPrefix = host + join(base, name); + var base = join(vfs.baseUrl, c9.projectId, "plugins", auth.accessToken); + options.packagePath = join(base, path.replace(/^plugins\//, "")); + options.staticPrefix = join(base, name); if (!options.setup) { wait++; diff --git a/plugins/c9.ide.ui/forms.js b/plugins/c9.ide.ui/forms.js index a8b39694..359189dc 100644 --- a/plugins/c9.ide.ui/forms.js +++ b/plugins/c9.ide.ui/forms.js @@ -171,7 +171,7 @@ define(function(require, exports, module) { var position = options.position; var node, childNodes; - if (options.setting && !options.path) + if (!options.setting && options.path) options.path = options.setting; if (debug) diff --git a/plugins/c9.ide.ui/lib/core.js b/plugins/c9.ide.ui/lib/core.js deleted file mode 100644 index 44185e9b..00000000 --- a/plugins/c9.ide.ui/lib/core.js +++ /dev/null @@ -1,134 +0,0 @@ -define(function(require, exports, module) { - - -function parseXml(xmlStr) { - return (new DOMParser()).parseFromString(xmlStr, "text/xml"); -} -function attributes(list) { - var map = {}; - for (var i = 0; i < list.length; i++) { - var attr = list[i]; - map[attr.name] = attr.value; - } - return list.length && map; -} -function node2Json(node) { - var children; - if (node.nodeType == node.ELEMENT_NODE) { - children = []; - var list = node.childNodes; - for (var i = 0; i < list.length; i++) { - var ch = node2Json(list[i]); - ch && children.push(ch); - } - } else if (node.nodeType == node.TEXT_NODE) { - return node.data.trim(); - } else if (node.nodeType == node.DOCUMENT_NODE) { - return node2Json(node.documentElement); - } else { - return; - } - - var json = {name: node.nodeName}; // node.localName} - var props = attributes(node.attributes); - if (props) json.props = props; - if (children.length) json.children = children; - - return json; -} -function xml2Json(node) { - if (typeof node == "string") - node = parseXml(node); - return node2Json(node); -} - -var oop = require("ace/lib/oop"); -var lang = require("ace/lib/lang"); -var useragent = require("ace/lib/useragent"); -var KeyBinding = require("ace/keyboard/keybinding").KeyBinding; -var EventEmitter = require("ace/lib/event_emitter").EventEmitter; -var CommandManager = require("ace/commands/command_manager").CommandManager; - - -var Node = function() { - this.children = []; - this.childNodes = this.children; - this.firstChild = - this.lastChild = - this.parentNode = - this.nextSibling = - this.previousSibling = null; -}; - -(function() { - oop.implement(this, EventEmitter); - - this.appendChild = function(node) { - return this.insertBefore(node); - }; - this.insertBefore = function(node, beforeNode) { - if (beforeNode == node) - return node; - if (!this || this == node) - throw new Error("Invalid insertBefore call"); - - var children = this.childNodes; - // if (node.parentNode == this) - // children[index] - if (node.parentNode) - node.removeNode(); - - var index = beforeNode ? children.indexOf(beforeNode) : children.length; - node.parentNode = this; - - if (beforeNode) { - children.splice(index, 0, node); - } else { - children.push(node); - } - - node.previousSibling = children[index - 1]; - node.nextSibling = children[index + 1]; - if (node.previousSibling) - node.previousSibling.nextSibling = node; - else - this.firstChild = children[0]; - - if (node.nextSibling) - node.nextSibling.previousSibling = node; - else - this.lastChild = children[this.childNodes.length - 1]; - - return node; - }; - this.removeChild = function(node) { - var children = this.childNodes; - var index = children.indexOf(node); - if (index == -1) return; - - children.splice(index, 1); - - var prev = node.previousSibling; - var next = node.nextSibling; - if (prev) - prev.nextSibling = next; - if (next) - next.previousSibling = prev; - - node.parentNode = - node.nextSibling = - node.previousSibling = null; - }; - this.remove = function() { - if (this.parentNode) - this.parentNode.removeChild(this); - }; - - - -}).call(Node.prototype); - - - - -}); \ No newline at end of file diff --git a/plugins/c9.ide.ui/lib/dropdown.js b/plugins/c9.ide.ui/lib/dropdown.js deleted file mode 100644 index 79f7945a..00000000 --- a/plugins/c9.ide.ui/lib/dropdown.js +++ /dev/null @@ -1,8392 +0,0 @@ -define(function(require, module, exports) { -return function(apf) { -var $setTimeout = setTimeout; -var $setInterval = setInterval; - - - - - -/** - * This element functions as the central access point for XML data. Data can be - * retrieved from any data source using data instructions. Data can also be - * submitted using data instructions in a similar way to HTML form posts. - * - * The modal can be reset to its original state. It has support for offline use and - * synchronization between multiple clients. - * - * #### Example: Loading A Model - * - * - * - * - * - * - * - * - * - * - * - * List component: - * - * - * - * - * Datagrid component: - * - * - * - * - * - * - * - * - * - * #### Example - * - * A small form where the bound data is submitted to a server using a model: - * - * ```xml, demo - * - * - * - * - * - * - * - * Name - * - * Address - * - * Submit - * - * - * - * ``` - * - * @class apf.model - * @inherits apf.AmlElement - * @define model - * @logic - * @allowchild [cdata], instance, load, submission - * - * - * - * @author Ruben Daniels (ruben AT ajax DOT org) - * @version %I%, %G% - * @since 0.8 - * - */ -/** - * @attribute {String} src Sets or gets the data instruction on how to load data from the data source into this model. - */ -/** - * @attribute {String} submission Sets or gets the data instruction on how to record the data from the data source from this model. - */ -/** - * @attribute {String} session Sets or gets the data instruction on how to store the session data from this model. - */ -/** - * @attribute {Boolean} autoinit Sets or gets whether to initialize the model immediately. If set to false you are expected to call init() when needed. This is useful when the system has to log in first. - */ -/** - * @attribute {Boolean} enablereset Sets or gets whether to save the original state of the data. This enables the use of the reset() call. - */ -/** - * @attribute {String} remote Sets or gets the id of the remote element to use for data synchronization between multiple clients. - */ -/** - * @event beforeretrieve Fires before a request is made to retrieve data. - * @cancelable Prevents the data from being retrieved. - */ -/** - * @event afterretrieve Fires when the request to retrieve data returns both on success and failure. - */ -/** - * @event receive Fires when data is successfully retrieved - * @param {Object} e The standard event object. It contains the following property: - * - `data` (([String])): the retrieved data - */ -/** - * @event beforeload Fires before data is loaded into the model. - * @cancelable - */ -/** - * @event afterload Fires after data is loaded into the model. - */ -/** - * @event beforesubmit Fires before data is submitted. - * @cancelable Prevents the submit. - * @param {Object} e The standard event object. It contains the following property: - * - `instruction` ([[String]]): the data instruction used to store the data. - */ -/** - * @event submiterror Fires when submitting data has failed. - */ -/** - * @event submitsuccess Fires when submitting data was successfull. - */ -/** - * @event aftersubmit Fires after submitting data. - */ -/** - * @event error Fires when a communication error has occured while making a request for this element. - * @cancelable Prevents the error from being thrown. - * @bubbles - * @param {Object} e The standard event object. It contains the following properties: - * - `error` ([[Error]]): the error object that is thrown when the event callback doesn't return false. - * - `state` ([[Number]]): the state of the call. Possible values include: - * - `apf.SUCCESS`: the request was successfull - * - `apf.TIMEOUT`: the request has timed out. - * - `apf.ERROR`: an error has occurred while making the request. - * - `apf.OFFLINE`: the request was made while the application was offline. - * - `userdata` (`Mixed`): data that the caller wanted to be available in the callback of the HTTP request. - * - `http` ([[XMLHttpRequest]]): The object that executed the actual HTTP request. - * - `url` ([[String]]): the URL that was requested. - * - `tpModule` ([[apf.http]]): the teleport module that is making the request. - * - `id` ([[Number]]): the id of the request. - * - `message` ([[String]]): the error message. - * - */ -apf.model = function(struct, tagName) { - this.$init(tagName || "model", apf.NODE_HIDDEN, struct); - - this.$amlNodes = {}; - this.$propBinds = {}; - - this.$listeners = {}; - this.$proplisteners = {}; -}; - -(function(){ - this.$parsePrio = "020"; - this.$isModel = true; - this.$createModel = true; - - this.canHaveChildren = false; - this.enablereset = false; - - this.$state = 0;//1 = loading - - //1 = force no bind rule, 2 = force bind rule - this.$attrExcludePropBind = apf.extend({ - submission: 1, - src: 1, - session: 1 - }, this.$attrExcludePropBind); - - this.$booleanProperties["whitespace"] = true; - this.$booleanProperties["create-model"] = true; - this.$booleanProperties["autoinit"] = true; - this.$booleanProperties.enablereset = true; - this.$supportedProperties = ["submission", "src", "session", "autoinit", - "enablereset", "remote", "whitespace", "create-model"]; - - this.$propHandlers["src"] = - this.$propHandlers["get"] = function(value, prop) { - if (this.$amlLoaded) - this.$loadFrom(value); - }; - - this.$propHandlers["create-model"] = function(value, prop) { - this.$createModel = value; - }; - - - //Connect to a remote databinding - this.$propHandlers["remote"] = function(value, prop) { - if (value) { - if (this.src && this.src.indexOf("rdb://") === 0) { - var _self = this; - apf.queue.add("rdb_load_" + this.$uniqueId, function(){ - _self.unshare(); - _self.share(); - }); - } - } - else - this.unshare(); - }; - - this.share = function(xpath) { - this.rdb = typeof this.remote == "string" - ? - - apf.nameserver.get("remote", this.remote) - - : this.remote; - - - - this.rdb.createSession(this.src, this, xpath); - }; - - this.unshare = function(xpath) { - if (!this.rdb) return; - this.rdb.endSession(this.src); - this.rdb = null; - }; - - - /** - * Registers an AML element to this model in order for the AML element to - * receive data loaded in this model. - * - * @param {apf.AmlElement} amlNode The AML element to be registered. - * @param {String} [xpath] The XPath query which is executed on the - * data of the model to select the node to be - * loaded in the `amlNode`. - * @return {apf.model} This model - * @private - */ - this.register = function(amlNode, xpath) { - if (!amlNode || !amlNode.load) //hasFeature(apf.__DATABINDING__)) - return this; - - var isReloading = amlNode.$model == this; - - //Remove previous model - if (amlNode.$model && !isReloading) - amlNode.$model.unregister(amlNode); - - //Register the AML node - var item = this.$amlNodes[amlNode.$uniqueId] = { - amlNode: amlNode, - xpath: xpath - }; - amlNode.$model = this; - - if (typeof amlNode.noloading == "undefined" - && amlNode.$setInheritedAttribute - && !amlNode.$setInheritedAttribute("noloading")) - amlNode.noloading = false; - - //amlNode.$model = this; - if (this.$state == 1) { - if (amlNode.clear && !amlNode.noloading) - amlNode.clear("loading");//@todo apf3.0 - } - else if (this.data) { - this.$loadInAmlNode(item); - //this.$loadInAmlProp(amlNode); - } - else { //@experimental - if (amlNode.hasFeature(apf.__CACHE__)) // amlNode.clear - amlNode.clear("empty"); - } - - var p, node, list = amlNode.$propsUsingMainModel, id = amlNode.$uniqueId; - for (var prop in list) { - this.$unbindXmlProperty(amlNode, prop); - p = this.$bindXmlProperty(amlNode, prop, - list[prop].xpath, list[prop].optimize); - - if (this.data) { - //if (node = p.root || p.listen ? this.data.selectSingleNode(p.root || p.listen) : this.data) { - if (node = p.listen ? this.data.selectSingleNode(p.listen) : this.data) { - amlNode.$execProperty(prop, node); - } - else - this.$waitForXml(amlNode, prop); - } - } - - return this; - }; - - this.$register = function(amlNode, xpath) { - //@todo apf3.0 update this.$propBinds; - - this.$amlNodes[amlNode.$uniqueId].xpath = xpath; - }; - - /* - * Removes an AML element from the group of registered AML elements. - * The AML element will not receive any updates from this model, however - * the data loaded in the AML element is not unloaded. - * - * @param {apf.AmlElement} amlNode The AML element to be unregistered. - * @private - */ - this.unregister = function(amlNode) { - delete this.$amlNodes[amlNode.$uniqueId]; - - var list = amlNode.$propsUsingMainModel; - for (var prop in list) - this.$unbindXmlProperty(amlNode, prop); - - amlNode.dispatchEvent("unloadmodel"); - }; - - /* - * @private - */ - this.getXpathByAmlNode = function(amlNode) { - var n = this.$amlNodes[amlNode.$uniqueId]; - if (!n) - return false; - - return n.xpath; - }; - - /* - * @private - */ - this.$loadInAmlNode = function(item) { - var xmlNode; - var xpath = item.xpath; - var amlNode = item.amlNode; - - if (this.data && xpath) { - xmlNode = this.data.selectSingleNode(xpath); - } - else - xmlNode = this.data || null; - - if (xmlNode) { - delete this.$listeners[amlNode.$uniqueId]; - if (amlNode.xmlRoot != xmlNode) - amlNode.load(xmlNode); - } - else - this.$waitForXml(amlNode); - }; - - this.$loadInAmlProp = function(id, xmlNode) { - var prop, node, p = this.$propBinds[id], amlNode = apf.all[id]; - if (!amlNode) { - delete this.$propBinds[id]; - return; - } - - - for (prop in p) { - if (xmlNode && (node = p[prop].listen - ? xmlNode.selectSingleNode(p[prop].listen) - : xmlNode)) { - apf.xmldb.addNodeListener(xmlNode, amlNode, - "p|" + id + "|" + prop + "|" + this.$uniqueId); - - delete this.$proplisteners[id + prop]; - amlNode.$execProperty(prop, node); - } - else - this.$waitForXml(amlNode, prop); - } - }; - - /* - We don't want to connect to the root, that would create a rush - of unnecessary update messages, so we'll find the element that's - closest to the node that is going to feed us the value - - mdlBlah: :bli/persons - mdlBlah: :bli/persons/person - - $attrBindings - //split / join, pop, indexOf - - - */ - this.$bindXmlProperty = function(amlNode, prop, xpath, optimize, listenRoot) { - var q ,p, id = amlNode.$uniqueId; - if (!this.$propBinds[id]) - this.$propBinds[id] = {}; - - /* - Store - 0 - Original xpath - 1 - Store point of change listener - 2 - Xpath to determine data node passed into load - */ - p = this.$propBinds[id][prop] = { - bind: xpath - }; - - //@todo apf3.0 - //Optimize root point, doesnt work right now because it doesnt change the original rule - if (optimize && false) { - //Find xpath for bind on this model of the amlNode - if ((q = this.$amlNodes[id]) && q.xpath) - xpath = (p.root = q.xpath) + "/" + xpath; - - var l = xpath.split("/"), z = l.pop(); - if (z.indexOf("@") == 0 - || z.indexOf("text()") > -1 - || z.indexOf("node()") > -1) { - p.listen = l.join("/"); - } - else p.listen = xpath; - } - else { - if ((q = this.$amlNodes[id]) && q.xpath) - p.listen = q.xpath; - } - - if (listenRoot) - p.listen = "."; - - if (this.data) { - var xmlNode = - - (p.listen ? this.data.selectSingleNode(p.listen) : this.data); - - if (xmlNode) { - apf.xmldb.addNodeListener(xmlNode, amlNode, - "p|" + amlNode.$uniqueId + "|" + prop + "|" + this.$uniqueId); - - return p; - } - } - - this.$waitForXml(amlNode, prop); - - return p; - }; - - this.$unbindXmlProperty = function(amlNode, prop) { - var id = amlNode.$uniqueId; - - //@todo apf3.0 - var p = this.$propBinds[id] && this.$propBinds[id][prop]; - if (!p) return; - - if (this.data) { - var xmlNode = p.listen ? this.data.selectSingleNode(p.listen) : this.data; - if (xmlNode) { - apf.xmldb.removeNodeListener(xmlNode, amlNode, - "p|" + id + "|" + prop + "|" + this.$uniqueId); - } - } - - delete this.$proplisteners[id + prop]; - delete this.$propBinds[id][prop]; - return p; - }; - - /** - * Gets a copy of current state of the XML of this model. - * - * @return {XMLNode} The context of this model, or `false` if there's no data - * - */ - this.getXml = function(){ - return this.data - ? apf.xmldb.cleanNode(this.data.cloneNode(true)) - : false; - }; - - /** - * Sets a value of an XMLNode based on an XPath statement executed on the data of this model. - * - * @param {String} xpath The xpath used to select a XMLNode. - * @param {String} value The value to set. - * @return {XMLNode} The changed XMLNode - */ - this.setQueryValue = function(xpath, value) { - if (!this.data) - return false; - - var node = apf.createNodeFromXpath(this.data, xpath); - if (!node) - return null; - - apf.setNodeValue(node, value, true); - //apf.xmldb.setTextNode(node, value); - return node; - }; - - /** - * Sets a value of a set of XML nodes based on an XPath statement executed on the data of this model. - * - * @param {String} xpath The xpath used to select a the nodeset. - * @param {String} value The value to set. - * @return {NodeList} The changed XMLNodeSet - */ - this.setQueryValues = function(xpath, value) { - if (!this.data) - return []; - - var nodes = this.data.selectNodes(xpath); - for (var i = 0, l = nodes.length; i < l; i++) - apf.setNodeValue(node, value, true); - - return nodes; - }; - - /** - * Gets the value of an XMLNode based on a XPath statement executed on the data of this model. - * - * @param {String} xpath The XPath used to select a XMLNode. - * @return {String} The value of the XMLNode - */ - this.queryValue = function(xpath) { - if (!this.data) - return false; - - return apf.queryValue(this.data, xpath); - }; - - /** - * Gets the values of an XMLNode based on a XPath statement executed on the data of this model. - * - * @param {String} xpath The xpath used to select a XMLNode. - * @return {[String]} The values of the XMLNode - */ - this.queryValues = function(xpath) { - if (!this.data) - return []; - - return apf.queryValue(this.data, xpath); - }; - - /** - * Executes an XPath statement on the data of this model - * - * @param {String} xpath The XPath used to select the XMLNode(s). - * @return {Mixed} The result of the selection, either an [[XMLNode]] or a [[NodeList]] - */ - this.queryNode = function(xpath) { - if (!this.data) - return null; - - return this.data.selectSingleNode(xpath) - }; - - /** - * Executes XPath statements on the data of this model - * - * @param {String} xpath The XPath used to select the XMLNode(s). - * @return {Mixed} The result of the selection, either an [[XMLNode]] or a [[NodeList]] - */ - this.queryNodes = function(xpath) { - if (!this.data) - return []; - - return this.data.selectNodes(xpath); - }; - - /** - * Appends a copy of the `xmlNode` or model to this model as a child - * of its root node - * @param {XMLNode} xmlNode The XML node to append - * @param {String} [xpath] The path to a node to append to - * @returns {XMLNode} The appended node - */ - this.appendXml = function(xmlNode, xpath) { - var insertNode = xpath - ? apf.createNodeFromXpath(this.data, xpath) - : this.data; - if (!insertNode) - return null; - - if (typeof xmlNode == "string") - xmlNode = apf.getXml(xmlNode); - else if (xmlNode.nodeFunc) - xmlNode = xmlNode.getXml(); - - if (!xmlNode) return; - - xmlNode = apf.xmldb.appendChild(insertNode, xmlNode); - - this.dispatchEvent("update", {xmlNode: xmlNode}); - return xmlNode; - }; - - /** - * Removes an XML node from this model. - */ - this.removeXml = function(xmlNode) { - if (!this.data) return; - - var xmlNodes; - if (typeof xmlNode === "string") { - xmlNodes = this.data.selectNodes(xmlNode); - } - else if (!xmlNode.length) { - xmlNodes = [xmlNode]; - } - - if (xmlNodes.length) { - apf.xmldb.removeNodeList(xmlNodes); - } - }; - - /** - * Clears the loaded data from this model. - */ - this.clear = function(){ - this.load(null); - doc = null; //Fix for safari refcount issue; - }; - - /** - * Resets data in this model to the last saved point. - * - */ - this.reset = function(){ - var doc = this.data.ownerDocument; - //doc.removeChild(this.data); - //var node = doc.appendChild(apf.isWebkit ? doc.importNode(this.$copy, true) : this.$copy); - this.data.parentNode.replaceChild(this.$copy, this.data); - this.load(this.$copy); - }; - - /** - * Sets a new saved point based on the current state of the data in this - * model. The `reset()` method returns the model to this point. - */ - this.savePoint = function(){ - this.$copy = apf.xmldb.getCleanCopy(this.data); - }; - - /** - * @private - */ - this.reloadAmlNode = function(uniqueId) { - if (!this.data) - return; - - var item = this.$amlNodes[uniqueId]; - var xmlNode = item.xpath - ? this.data.selectSingleNode(item.xpath) - : this.data; - item.amlNode.load(xmlNode); - }; - - //@todo refactor this to use .blah instead of getAttribute - //@todo move this to propHandlers - /* - * @private - */ - this.addEventListener("DOMNodeInsertedIntoDocument", function(e) { - var x = this.$aml; - if (this.parentNode && this.parentNode.hasFeature(apf.__DATABINDING__)) { - if (!this.name) - this.setProperty("id", "model" + this.parentNode.$uniqueId); - //this.parentNode.$aml.setAttribute("model", this.name); //@todo don't think this is necesary anymore... - this.register(this.parentNode); - } - - //Load literal model - if (!this.src) { - var strXml, xmlNode = x; - if (xmlNode && xmlNode.childNodes.length) { - if (apf.getNode(xmlNode, [0])) { - if ((strXml = xmlNode.xml || xmlNode.serialize()).match(/^[\s\S]*?>([\s\S]*)<[\s\S]*?$/)) { - strXml = RegExp.$1; //@todo apf3.0 test this with json - if (!apf.supportNamespaces) - strXml = strXml.replace(/xmlns=\"[^"]*\"/g, ""); - } - - if (this.whitespace === false) - strXml = strXml.replace(/>[\s\n\r]*<"); - - return this.load(apf.getXmlDom(strXml).documentElement); - } - // we also support JSON data loading in a model CDATA section - else if (apf.isJson(xmlNode.childNodes[0].nodeValue)) { - return this.load(apf.getXmlDom(xmlNode.childNodes[0].nodeValue).documentElement); - } - } - - //Default data for XForms models without an instance but with a submission node - if (this.submission) - this.load(""); - } - - //Load data into model if allowed - if (!apf.isFalse(this.autoinit)) - this.init(); - - //@todo actions apf3.0 - - return this; - }); - - //callback here is private - /** - * Loads the initial data into this model. - * @see apf.model.init - */ - this.init = function(callback) { - if (this.session) { - this.$loadFrom(this.session, {isSession: true}); - } - else { - - - if (this.src) - this.$loadFrom(this.src, {callback: callback}); - } - }; - - /* *********** LOADING ****************/ - - /* - * Loads data into this model using a data instruction. - * @param {String} instruction The data instrution how to retrieve the data. - * @param {Object} options - * Properties: - * {XMLElement} xmlNode the {@link term.datanode data node} that provides context to the data instruction. - * {Function} callback the code executed when the data request returns. - * {Mixed} [] Custom properties available in the data instruction. - */ - this.$loadFrom = function(instruction, options) { - - if (instruction.indexOf("rdb://") === 0) { - this.src = instruction; //@todo - return this.$propHandlers["remote"].call(this, this.remote); - } - - var data = instruction.split(":"); - - if (!options) - options = {}; - - if (!options.isSession) { - this.src = instruction; - this.$srcOptions = [instruction, options]; - } - - //Loading data in non-literal model - this.dispatchEvent("beforeretrieve"); - - //Set all components on loading... - var uniqueId, item; - for (uniqueId in this.$amlNodes) { - if (!(item = this.$amlNodes[uniqueId]) || !item.amlNode) - continue; - - //@todo apf3.0 - if (!item.amlNode.noloading) - item.amlNode.clear("loading"); - } - - this.$state = 1; - if (!this.$callCount) - this.$callCount = 1; - else - this.$callCount++; - - var _self = this, - callCount = this.$callCount, - callback = options.callback; - options.callback = function(data, state, extra) { - if (callCount != _self.$callCount) - return; //another call has invalidated this one - - _self.dispatchEvent("afterretrieve"); - - - - if (state != apf.SUCCESS) { - var oError; - - oError = new Error(apf.formatErrorString(1032, - _self, "Loading xml data", "Could not load data\n" - + "Instruction: " + instruction + "\n" - + "Url: " + extra.url + "\n" - + "Info: " + extra.message + "\n\n" + data)); - - if (callback && callback.apply(this, arguments) === true) - return true; - - if (extra.tpModule && extra.tpModule.retryTimeout(extra, state, _self, oError) === true) - return true; - - _self.$state = 0; - - throw oError; - } - - if (options && options.isSession && !data) { - if (this.src) - return _self.$loadFrom(this.src); - } - else { - if (options && options.cancel) - return; - - _self.load(data); - _self.dispatchEvent("receive", { - data: data - }); - - if (callback) - callback.apply(this, arguments); - } - }; - - return apf.getData(instruction, options); - }; - - /** - * Loads the data from the datasource specified for [[apf.model.init]]. - */ - this.reload = function(){ - if (!this.data) - return; - - if (this.$srcOptions) - this.$loadFrom.apply(this, this.$srcOptions); - else if (this.src) - this.$loadFrom(this.src); - }; - - /** - * Loads data into this model. - * - * @param {Mixed} [xmlNode] The data to load in this model. A string specifies the data instruction how to retrieve the data, which can be an XML string. `null` will clear the data from this model. - * @param {Object} [options] Additional options to pass. This can contain the following properties: - * - * - `xmlNode` ([[XMLElement]]): the {@link term.datanode data node} that provides context to the data instruction. - * - `callback` ([[Function]]): the code executed when the data request returns. - * - `[]` (`Mixed`): custom properties available in the data instruction. - * - `[nocopy]` ([[Boolean]]): specifies whether the data loaded will not overwrite the reset point. - */ - this.load = function(xmlNode, options) { - if (typeof xmlNode == "string") { - if (xmlNode.charAt(0) == "<") { //xml - if (xmlNode.substr(0, 5).toUpperCase() == "")+1); - if (!apf.supportNamespaces) - xmlNode = xmlNode.replace(/xmlns\=\"[^"]*\"/g, ""); - xmlNode = apf.getXmlDom(xmlNode, null, true).documentElement; //@todo apf3.0 whitespace issue - } - - else - return this.$loadFrom(xmlNode, options); - } - - if (this.ownerDocument && this.ownerDocument.$domParser.$isPaused(this)) { - //if (!this.$queueLoading) { - var _self = this; - this.data = xmlNode; //@todo expirement //this.$copy = - apf.xmldb.getXmlDocId(xmlNode, this); //@todo experiment - - this.$queueLoading = true; - apf.queue.add("modelload" + this.$uniqueId, function(){ - if (_self.ownerDocument && _self.ownerDocument.$domParser.$isPaused(_self)) - apf.queue.add("modelload" + _self.$uniqueId, arguments.callee); - else { - _self.load(xmlNode, options); - _self.$queueLoading = false; - } - }); - //} - return; - } - else if (this.$queueLoading) - apf.queue.remove("modelload" + this.$uniqueId); - - this.$state = 0; - - if (this.dispatchEvent("beforeload", {xmlNode: xmlNode}) === false) - return false; - - var doc = xmlNode ? xmlNode.ownerDocument : null; //Fix for safari refcount issue; - - if (xmlNode) { - if (!apf.supportNamespaces) { - /* && (xmlNode.prefix || xmlNode.scopeName)) { - doc.setProperty("SelectionNamespaces", "xmlns:" - + (xmlNode.prefix || xmlNode.scopeName) + "='" - + xmlNode.namespaceURI + "'");*/ - var xmlns = [], attr = xmlNode.attributes; - for (var i = 0, l = attr.length; i < l; i++) { - if (attr[i].nodeName.substr(0, 5) == "xmlns") { - xmlns.push(attr[i].xml); - } - } - if (xmlns.length) - doc.setProperty("SelectionNamespaces", xmlns.join(" ")); - } - - apf.xmldb.addNodeListener(xmlNode, this); //@todo this one can be added for this.$listeners and when there are none removed - apf.xmldb.nodeConnect( - apf.xmldb.getXmlDocId(xmlNode, this), xmlNode, null, this); - - if ((!options || !options.nocopy) && this.enablereset) - this.$copy = apf.xmldb.getCleanCopy(xmlNode); - } - - this.data = xmlNode; - - this.dispatchEvent("afterload", {xmlNode: xmlNode}); - this.dispatchEvent("update", {xmlNode: xmlNode}); - - for (var id in this.$amlNodes) - this.$loadInAmlNode(this.$amlNodes[id]); - - for (id in this.$propBinds) - this.$loadInAmlProp(id, xmlNode); - - return this; - }; - - //Listening nodes should be removed in unregister - this.$waitForXml = function(amlNode, prop) { - if (prop) - this.$proplisteners[amlNode.$uniqueId + prop] = { - id: amlNode.$uniqueId, - amlNode: amlNode, - prop: prop - }; - else - this.$listeners[amlNode.$uniqueId] = amlNode; - - //When data is not available at model load but element had already data - //loaded, it is cleared here. - if (amlNode.xmlRoot) - amlNode.clear(); - }; - - this.$xmlUpdate = function(action, xmlNode, listenNode, UndoObj) { - //@todo optimize by only doing this for add, sync etc actions - - if (action == "replacenode" && xmlNode == this.data.ownerDocument.documentElement) { - var _self = this; - $setTimeout(function(){ - _self.load(xmlNode); - }); - return; - } - - - if (this.rdb && !this.$at && UndoObj) - this.$at = UndoObj.at; - - - - - var p, b; - for (var id in this.$listeners) { - if (xmlNode = this.data.selectSingleNode(this.$amlNodes[id].xpath || ".")) { - this.$listeners[id].load(xmlNode); - delete this.$listeners[id]; - } - } - - for (id in this.$proplisteners) { - p = this.$proplisteners[id]; - b = this.$propBinds[p.id][p.prop]; - if (xmlNode = b.listen ? this.data.selectSingleNode(b.listen) : this.data) { - delete this.$proplisteners[id]; - - apf.xmldb.addNodeListener(xmlNode, p.amlNode, - "p|" + p.id + "|" + p.prop + "|" + this.$uniqueId); - - p.amlNode.$execProperty(p.prop, b.root - ? this.data.selectSingleNode(b.root) - : this.data); - } - } - - this.dispatchEvent("update", {xmlNode: xmlNode, action: action, undoObj: UndoObj}); - }; - - // *** INSERT *** // - - /* - * Inserts data into the data of this model using a data instruction. - * @param {String} instruction The data instrution indicating how to retrieve the data. - * @param {Object} options Additional options to pass. This can contain the following properties: - * - * - `insertPoint` ([[XMLElement]]): the parent element for the inserted data. - * - `clearContents` ([[Boolean]]): whether the contents of the insertPoint should be cleared before inserting the new children. - * - `copyAttributes` ([[Boolean]]): whether the attributes of the merged element are copied. - * - `callback` ([[Function]]): the code executed when the data request returns. - * - `[]` (`Mixed`): custom properties available in the data instruction. - */ - this.$insertFrom = function(instruction, options) { - if (!instruction) return false; - - this.dispatchEvent("beforeretrieve"); - - - - var callback = options.callback, _self = this; - options.callback = function(data, state, extra) { - _self.dispatchEvent("afterretrieve"); - - if (!extra) - extra = {}; - - if (state != apf.SUCCESS) { - var oError; - - - - if (extra.tpModule.retryTimeout(extra, state, - options.amlNode || _self, oError) === true) - return true; - - if (callback - && callback.call(this, extra.data, state, extra) === false) - return; - - throw oError; - } - - //Checking for xpath - if (typeof options.insertPoint == "string") - options.insertPoint = _self.data.selectSingleNode(options.insertPoint); - - if (typeof options.clearContents == "undefined" && extra.userdata) - options.clearContents = apf.isTrue(extra.userdata[1]); //@todo is this still used? - - if (options.whitespace == undefined) - options.whitespace = _self.whitespace; - - //Call insert function - (options.amlNode || _self).insert(data, options); - - if (callback) - callback.call(this, extra.data, state, extra); - }; - - apf.getData(instruction, options); - }; - - /** - * Inserts data in this model as a child of the currently loaded data. - * - * @param {XMLElement} XMLRoot The {@link term.datanode data node} to insert into this model. - * @param {Object} options Additional options to pass. This can contain the following properties: - * - * - `insertPoint` ([[XMLElement]]): the parent element for the inserted data. - * - `clearContents` ([[Boolean]]): specifies whether the contents of the `insertPoint` should be cleared before inserting the new children. - * - `copyAttributes` ([[Boolean]]): specifies whether the attributes of the merged element are copied. - * - `callback` ([[Function]]): the code executed when the data request returns. - * - `[]` (`Mixed`): Custom properties available in the data instruction. - */ - this.insert = function(xmlNode, options) { - if (typeof xmlNode == "string") { - if (xmlNode.charAt(0) == "<") { - if (xmlNode.substr(0, 5).toUpperCase() == "")+1); - if (!apf.supportNamespaces) - xmlNode = xmlNode.replace(/xmlns\=\"[^"]*\"/g, ""); - - if (this.whitespace === false) - xmlNode = xmlNode.replace(/>[\s\n\r]*<"); - - xmlNode = apf.getXmlDom(xmlNode).documentElement; - } - - else - return this.$insertFrom(xmlNode, options); - } - - if (!options.insertPoint) - options.insertPoint = this.data; - - - - //if(this.dispatchEvent("beforeinsert", parentXMLNode) === false) return false; - - //Integrate XMLTree with parentNode - if (typeof options.copyAttributes == "undefined") - options.copyAttributes = true; - - var newNode = apf.mergeXml(xmlNode, options.insertPoint, options); - - //Call __XMLUpdate on all this.$listeners - apf.xmldb.applyChanges("insert", options.insertPoint);//parentXMLNode); - - //this.dispatchEvent("afterinsert"); - - return xmlNode; - }; - - - this.$destroy = function(){ - if (this.session && this.data) - apf.saveData(this.session, {xmlNode: this.getXml()}); - }; -}).call(apf.model.prototype = new apf.AmlElement()); - -apf.aml.setElement("model", apf.model); - - - - - - -apf.__DATABINDING__ = 1 << 1; - - - -/** - * This is a baseclass that adds data binding features to this element. - * Databinding takes care of automatically going from data to representation and establishing a - * permanent link between the two. In this way data that is changed will - * change the representation as well. Furthermore, actions that are executed on - * the representation will change the underlying data. - * - * #### Example - * - * ```xml - * - * - * - * Item 1 - * Item 2 - * - * - * - * - * - * - * - * - * ``` - * - * @class apf.DataBinding - * @inherits apf.Presentation - * @baseclass - * @author Ruben Daniels (ruben AT ajax DOT org) - * @version %I%, %G% - * @since 0.4 - * @default_private - */ -/** - * @event error Fires when a communication error has occured while - * making a request for this element. - * @cancelable Prevents the error from being thrown. - * @bubbles - * @param {Object} e The standard event object. It contains the following properties: - * - error ([[Error]]): the error object that is thrown when the event callback doesn't return false. - * - state ([[Number]]): the state of the call - * - `apf.SUCCESS`: The request was successfull - * - `apf.TIMEOUT`: The request has timed out. - * - `apf.ERROR `: An error has occurred while making the request. - * - `apf.OFFLINE`: The request was made while the application was offline. - * - userdata (`Mixed`): Data that the caller wanted to be available in the callback of the http request. - * - http ([[XMLHttpRequest]]): The object that executed the actual http request. - * - url ([[String]]): The url that was requested. - * - tpModule ([[apf.http]]): The teleport module that is making the request. - * - id ([[Number]]): The ID of the request. - * - message ([[String]]): The error message. - */ -/** - * @event beforeretrieve Fires before a request is made to retrieve data. - * @cancelable Prevents the data from being retrieved. - */ -/** - * @event afterretrieve Fires when the request to retrieve data returns both - * on success and failure. - */ -/** - * @event receive Fires when data is successfully retrieved - * @param {Object} e The standard event object. It contains the following properties: - * - data ([[String]]): the retrieved data - * - */ -apf.DataBinding = function(){ - this.$init(true); - - this.$loadqueue = - this.$dbTimer = null; - this.$regbase = this.$regbase | apf.__DATABINDING__; - this.$mainBind = "value"; - - this.$bindings = - this.$cbindings = - this.$attrBindings = false; - - //1 = force no bind rule, 2 = force bind rule - this.$attrExcludePropBind = apf.extend({ - model: 1, - each: 1 - //eachvalue : 1 //disabled because of line 1743 valueRule = in multiselect.js - }, this.$attrExcludePropBind); - - // *** Public Methods *** // - - /** - * Sets a value of an XMLNode based on an xpath statement executed on the data of this model. - * - * @param {String} xpath The xpath used to select a XMLNode - * @param {String} value The value to set - * @return {XMLNode} The changed XMLNode - */ - this.setQueryValue = function(xpath, value, type) { - var node = apf.createNodeFromXpath(this[type || 'xmlRoot'], xpath); - if (!node) - return null; - - apf.setNodeValue(node, value, true); - //apf.xmldb.setTextNode(node, value); - return node; - }; - - /** - * Queries the bound data for a string value - * - * @param {String} xpath The XPath statement which queries on the data this element is bound on. - * @param {String} type The node that is used as the context node for the query. It can be one of the following possible values: - * - `"selected"`: The selected data anode of this element. - * - `"xmlRoot"`: The root data node that this element is bound on. - * - `"indicator"`: The data node that is highlighted for keyboard navigation. - * @return {String} The value of the selected XML Node - * - */ - this.queryValue = function(xpath, type) { - /* @todo - * lstRev.query('revision/text()', 'selected'); - * lstRev.query('revision/text()', 'xmlRoot'); - * lstRev.query('revision/text()', 'indicator'); - */ - return apf.queryValue(this[type || 'xmlRoot'], xpath ); - }; - /** - * Queries the bound data for an array of string values - * - * @param {String} xpath The XPath statement which queries on the data this element is bound on. - * @param {String} type The node that is used as the context node for the query. It can be one of the following possible values: - * - `"selected"`: The selected data anode of this element. - * - `"xmlRoot"`: The root data node that this element is bound on. - * - `"indicator"`: The data node that is highlighted for keyboard navigation. - * @return {String} The value of the selected XML Node - */ - this.queryValues = function(xpath, type) { - return apf.queryValues(this[type || 'xmlRoot'], xpath ); - }; - - /** - * Executes an XPath statement on the data of this model - * - * @param {String} xpath The XPath statement which queries on the data this element is bound on. - * @param {String} type The node that is used as the context node for the query. It can be one of the following possible values: - * - `"selected"`: The selected data anode of this element. - * - `"xmlRoot"`: The root data node that this element is bound on. - * - `"indicator"`: The data node that is highlighted for keyboard navigation. - * @return {Mixed} An [[XMLNode]] or [[NodeList]] with the result of the selection - */ - this.queryNode = function(xpath, type) { - var n = this[type||'xmlRoot']; - return n ? n.selectSingleNode(xpath) : null; - }; - - /** - * Executes an XPath statement on the data of this model - * - * @param {String} xpath The XPath used to select the XMLNode(s) - * @param {String} type The node that is used as the context node for the query. It can be one of the following possible values: - * - `"selected"`: The selected data anode of this element. - * - `"xmlRoot"`: The root data node that this element is bound on. - * - `"indicator"`: The data node that is highlighted for keyboard navigation. - * @return {Mixed} An [[XMLNode]] or [[NodeList]] with the result of the selection - */ - this.queryNodes = function(xpath, type) { - var n = this[type||'xmlRoot']; - return n ? n.selectNodes(xpath) : []; - }; - - this.$checkLoadQueue = function(){ - // Load from queued load request - if (this.$loadqueue) { - if (!this.caching) - this.xmlRoot = null; - var q = this.load(this.$loadqueue[0], {cacheId: this.$loadqueue[1]}); - if (!q || q.dataType != apf.ARRAY || q != this.$loadqueue) - this.$loadqueue = null; - } - else return false; - }; - - //setProp - this.$execProperty = function(prop, xmlNode, undoObj) { - var attr = this.$attrBindings[prop]; - - //@todo this is a hacky solution for replaceNode support - Have to rethink this. - if (this.nodeType == 7) { - if (xmlNode != this.xmlRoot) - this.xmlRoot = xmlNode; - } - - - - - - - try { - - if (attr.cvalue.asyncs) { //if async - var _self = this; - return attr.cvalue.call(this, xmlNode, function(value) { - _self.setProperty(prop, value, true); - - - - }); - } - else { - var value = attr.cvalue.call(this, xmlNode); - } - - } - catch (e) { - apf.console.warn("[400] Could not execute binding for property " - + prop + "\n\n" + e.message); - return; - } - - - this.setProperty(prop, undoObj && undoObj.extra.range || value, true); //@todo apf3.0 range - - - }; - - //@todo apf3.0 contentEditable support - this.$applyBindRule = function(name, xmlNode, defaultValue, callback, oHtml) { - var handler = this.$attrBindings[name] - && this.$attrBindings[name].cvalue || this.$cbindings[name]; - - return handler ? handler.call(this, xmlNode, callback) : defaultValue || ""; - }; - - - - this.$hasBindRule = function(name) { - return this.$attrBindings[name] || this.$bindings - && this.$bindings[name]; - }; - - this.$getBindRule = function(name, xmlNode) { - return this.$attrBindings[name] || this.$bindings - && this.$bindings.getRule(name, xmlNode); - }; - - var ruleIsMatch = {"drag":1,"drop":1,"dragcopy":1} - this.$getDataNode = function(name, xmlNode, createNode, ruleList, multiple) { - var node, rule = this.$attrBindings[name]; - if (rule) { //@todo apf3.0 find out why drag and drop rules are already compiled here - if (rule.cvalue.type != 3) //@todo warn here? - return false; - - var func = rule.cvalue2 || rule.compile("value", { - xpathmode: multiple ? 4 : 3, - parsecode: 1, - injectself: ruleIsMatch[name] - }); - if (func && (node = func(xmlNode, createNode))) { - if (ruleList) - ruleList.push(rule); - - return node; - } - - return false; - } - - return this.$bindings - && this.$bindings.getDataNode(name, xmlNode, createNode, ruleList, multiple); - }; - - - /** - * Sets the model of the specified element. - * The model acts as a datasource for this element. - * - * @param {apf.model} The model this element is going to connect to. - * - */ - this.setModel = function(model) { - this.setAttribute("model", model, false, true); - }; - - - /** - * Gets the model which this element is connected to. - * The model acts as a datasource for this element. - * - * @param {Boolean} doRecur Specifies whether the model should be searched recursively up the data tree. - * @returns {apf.model} The model this element is connected to. - * @see apf.smartbinding - */ - this.getModel = function(doRecur) { - if (doRecur && !this.$model) - return this.dataParent ? this.dataParent.parent.getModel(true) : null; - - return this.$model; - }; - - /** - * Reloads the data in this element. - * @method - */ - this.reload = this.reload || function(){ - this.load(this.xmlRoot, {cacheId: this.cacheId, force: true}); - }; - - /** - * @event beforeload Fires before loading data in this element. - * @cancelable Prevents the data from being loaded. - * @param {XMLElement} xmlNode The node that is loaded as the root {@link term.datanode data node}. - * - */ - /** - * @event afterload Fires after loading data in this element. - * @param {XMLElement} xmlNode The node that is loaded as the root {@link term.datanode data node}. - */ - /** - * Loads data into this element using binding rules to transform the - * data into a presentation. - * - * #### Example - * - * ```xml - * - * - * - * - * - * - * - * - * - * ``` - * - * @param {XMLElement | String} [xmlNode] The content to load into this element. It can be one of the following values: - * - {XMLElement}: An XML element that's loaded into this element - * - {String}: Either an XML string, or, an instruction to load the data from a remote source - * - `null`: Clears this element from its data - * @param {Object} [options] Set of additional options to pass. Properties include: - * - [xmlNode] ([[XMLElement]]): The {@link term.datanode data node} that provides - * context to the data instruction. - * - [callback] ([[Function]]): The code executed when the data request returns - * - [properties] (`Mixed`): Custom properties available in the data instruction - * - [cacheId] ([[String]]): The xml element to which the binding rules are applied - * - [force] ([[Boolean]]): Specifies whether cache is checked before loading the data - * - [noClearMsg] ([[Boolean]]): Specifies whether a message is set when clear is called - */ - this.load = function(xmlNode, options) { - if (options) { - var cacheId = options.cacheId, - forceNoCache = options.force, - noClearMsg = options.noClearMsg; - } - if (cacheId && cacheId == this.cacheId && !forceNoCache) - return; - - - if (apf.popup.isShowing(this.$uniqueId)) - apf.popup.forceHide(); //This should be put in a more general position - - - // Convert first argument to an xmlNode we can use; - if (xmlNode) { - if (typeof xmlNode == "string") { - if (xmlNode.charAt(0) == "<") - xmlNode = apf.getXmlDom(xmlNode).documentElement; - else { - return apf.model.prototype.$loadFrom.call(this, xmlNode, options); - } - } - else if (xmlNode.nodeType == 9) { - xmlNode = xmlNode.documentElement; - } - else if (xmlNode.nodeType == 3 || xmlNode.nodeType == 4) { - xmlNode = xmlNode.parentNode; - } - else if (xmlNode.nodeType == 2) { - xmlNode = xmlNode.ownerElement - || xmlNode.parentNode - || xmlNode.selectSingleNode(".."); - } - } - - // If control hasn't loaded databinding yet, queue the call - if (this.$preventDataLoad || !this.$canLoadData - && ((!this.$bindings && (!this.$canLoadDataAttr || !this.each)) || !this.$amlLoaded) - && (!this.hasFeature(apf.__MULTISELECT__) || !this.each) - || this.$canLoadData && !this.$canLoadData()) { - - if (!this.caching || !this.hasFeature(apf.__CACHE__)) { - - //@todo this is wrong. It is never updated when there are only - //Property binds and then it just leaks xml nodes - this.xmlRoot = xmlNode; - - - this.setProperty("root", this.xmlRoot); - - } - - - - return this.$loadqueue = [xmlNode, cacheId]; - } - this.$loadqueue = null; - - // If no xmlNode is given we clear the control, disable it and return - if (this.dataParent && this.dataParent.xpath) - this.dataParent.parent.signalXmlUpdate[this.$uniqueId] = !xmlNode; - - if (!xmlNode && (!cacheId || !this.$isCached || !this.$isCached(cacheId))) { - - - this.clear(noClearMsg); - - - if (apf.config.autoDisable && this.$createModel === false) - this.setProperty("disabled", true); - - //@todo apf3.0 remove , true in clear above - //this.setProperty("selected", null); - - return; - } - - // If reloading current document, and caching is disabled, exit - if (!this.caching && !forceNoCache && xmlNode - && !this.$loadqueue && xmlNode == this.xmlRoot) - return; - - var disabled = this.disabled; - this.disabled = false; - - //Run onload event - if (this.dispatchEvent("beforeload", {xmlNode : xmlNode}) === false) - return false; - - - - this.clear(true, true); - - this.cacheId = cacheId; - - if (this.dispatchEvent("$load", { - forceNoCache: forceNoCache, - xmlNode: xmlNode - }) === false) { - //delete this.cacheId; - return; - } - - //Set usefull vars - this.documentId = apf.xmldb.getXmlDocId(xmlNode); - this.xmlRoot = xmlNode; - - - this.setProperty("root", this.xmlRoot); - - - - - // Draw Content - this.$load(xmlNode); - - - - // Check if subtree should be loaded - this.$loadSubData(xmlNode); - - if (this.$createModel === false) { - this.disabled = true; - this.setProperty("disabled", false); - } - else - this.disabled = disabled; - - // Run onafteronload event - this.dispatchEvent('afterload', {xmlNode : xmlNode}); - }; - - // @todo Doc - /* - * @binding load Determines how new data is loaded data is loaded into this - * element. Usually this is only the root node containing no children. - * - * #### Example - * - * This example shows a load rule in a text element. It gets its data from - * a list. When a selection is made on the list the data is loaded into the - * text element. - * - * ```xml - * - * - * - * - * - * - * - * - * message 1 - * message 2 - * - * - * - * - * - * - * - * - * - * - * ``` - * - */ - /** - * @attribute {String} get Sets or gets the {@link term.datainstruction data instruction} - * that is used to load data into the XML root of this component. - */ - this.$loadSubData = function(xmlRootNode) { - if (this.$hasLoadStatus(xmlRootNode) && !this.$hasLoadStatus(xmlRootNode, "potential")) - return; - - //var loadNode = this.$applyBindRule("load", xmlRootNode); - var rule = this.$getBindRule("load", xmlRootNode); - if (rule && (!rule[1] || rule[1](xmlRootNode))) { - - - this.$setLoadStatus(xmlRootNode, "loading"); - - if (this.$setClearMessage) - this.$setClearMessage(this["loading-message"], "loading"); - - //||apf.xmldb.findModel(xmlRootNode) - var mdl = this.getModel(true); - - - var amlNode = this; - if (mdl.$insertFrom(rule.getAttribute("get"), { - xmlNode: xmlRootNode, //@todo apf3.0 - insertPoint: xmlRootNode, //this.xmlRoot, - amlNode: this, - callback: function(){ - - amlNode.setProperty(amlNode.hasFeature(apf.__MULTISELECT__) - ? "selected" - : "root", xmlRootNode); - - } - }) === false - ) { - this.clear(true); - - if (apf.config.autoDisable) - this.setProperty("disabled", true); - - //amlNode.setProperty("selected", null); //@todo is this not already done in clear? - - } - } - }; - - //@todo this function is called way too much for a single load of a tree - //@todo should clear listener - /* - * Unloads data from this element and resets state displaying an empty message. - * The empty message is set on the {@link apf.GuiElement.msg}. - * - * @param {Boolean} [nomsg] Specifies whether to display the empty message. - * @param {Boolean} [doEvent] Specifies whether to send select events. - * @see baseclass.databinding.method.load - * @private - */ - this.clear = function(nomsg, doEvent, fakeClear) { - if (!this.$container) - return;//@todo apf3.0 - - if (this.clearSelection) - this.clearSelection(true);//!doEvent);//@todo move this to the $clear event in multiselect.js - - var lastHeight = this.$container.offsetHeight; - - if (this.dispatchEvent("$clear") !== false) - this.$container.innerHTML = ""; //@todo apf3.0 - - if (typeof nomsg == "string") { - var msgType = nomsg; - nomsg = false; - - //@todo apf3.0 please use attr. inheritance - if (!this[msgType + "-message"]) { - this.$setInheritedAttribute(msgType + "-message"); - } - } - this.$lastClearType = msgType || null; - - if (!nomsg && this.$setClearMessage) { - this.$setClearMessage(msgType - ? this[msgType + "-message"] - : this["empty-message"], msgType || "empty", lastHeight); - - //this.setProperty("selected", null); //@todo apf3.0 get the children to show loading... as well (and for each selected, null - //c[i].o.clear(msgType, doEvent); - } - else if (this.$removeClearMessage) - this.$removeClearMessage(); - - if (!fakeClear) - this.documentId = this.xmlRoot = this.cacheId = null; - - - if (!nomsg) { - if (this.hasFeature(apf.__MULTISELECT__)) //@todo this is all wrong - this.setProperty("length", 0); - //else - //this.setProperty("value", ""); //@todo redo apf3.0 - } - - }; - - this.clearMessage = function(msg) { - this.customMsg = msg; - this.clear("custom"); - }; - - //@todo optimize this - /** - * @private - */ - this.$setLoadStatus = function(xmlNode, state, remove) { - var group = this.loadgroup || "default"; - var re = new RegExp("\\|(\\w+)\\:" + group + ":(\\d+)\\|"); - var loaded = xmlNode.getAttribute("a_loaded") || ""; - - var m; - if (!remove && (m = loaded.match(re)) && m[1] != "potential" && m[2] != this.$uniqueId) - return; - - //remove old status if any - var ostatus = loaded.replace(re, "") - if (!remove) - ostatus += "|" + state + ":" + group + ":" + this.$uniqueId + "|"; - - xmlNode.setAttribute("a_loaded", ostatus); - }; - - /** - * @private - */ - this.$removeLoadStatus = function(xmlNode) { - this.$setLoadStatus(xmlNode, null, true); - }; - - /** - * @private - */ - this.$hasLoadStatus = function(xmlNode, state, unique) { - if (!xmlNode) - return false; - var ostatus = xmlNode.getAttribute("a_loaded"); - if (!ostatus) - return false; - - var group = this.loadgroup || "default"; - var re = new RegExp("\\|" + (state || "\\w+") + ":" + group + ":" + (unique ? this.$uniqueId : "\\d+") + "\\|"); - return ostatus.match(re) ? true : false; - }; - - /* - * @event beforeinsert Fires before data is inserted. - * @cancelable Prevents the data from being inserted. - * @param {XMLElement} xmlParentNode The parent in which the new data is inserted - */ - /** - * @event afterinsert Fires after data is inserted. - */ - - /** - * @private - */ - this.insert = function(xmlNode, options) { - if (typeof xmlNode == "string") { - if (xmlNode.charAt(0) == "<") { - - if (options.whitespace === false) - xmlNode = xmlNode.replace(/>[\s\n\r]*<"); - - xmlNode = apf.getXmlDom(xmlNode).documentElement; - } - else { - if (!options.insertPoint) - options.insertPoint = this.xmlRoot; - return apf.model.prototype.$insertFrom.call(this, xmlNode, options); - } - } - - var insertPoint = options.insertPoint || this.xmlRoot; - - if (this.dispatchEvent("beforeinsert", { - xmlParentNode: insertPoint - }) === false) - return false; - - //Integrate XMLTree with parentNode - if (typeof options.copyAttributes == "undefined") - options.copyAttributes = true; - - if (this.filterUnique) - options.filter = this.filterUnique; - - var newNode = apf.mergeXml(xmlNode, insertPoint, options); - - this.$isLoading = true; //Optimization for simpledata - - //Call __XMLUpdate on all listeners - apf.xmldb.applyChanges("insert", insertPoint); - - this.$isLoading = false; - - //Select or propagate new data - if (this.selectable && this.autoselect) { - if (this.xmlNode == newNode) - this.$selectDefault(this.xmlNode); - } - - else if (this.xmlNode == newNode) { - this.setProperty("root", this.xmlNode); - } - - - if (this.$hasLoadStatus(insertPoint, "loading")) - this.$setLoadStatus(insertPoint, "loaded"); - - this.dispatchEvent("afterinsert"); - - //Check Connections - //this one shouldn't be called because they are listeners anyway...(else they will load twice) - //if(this.selected) this.setConnections(this.selected, "select"); - }; - - /** - * @attribute {Boolean} render-root Sets or gets whether the XML element loaded into this - * element is rendered as well. The default is false. - * - * #### Example - * - * This example shows a tree which also renders the root element. - * - * ```xml - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * ``` - */ - this.$booleanProperties["render-root"] = true; - this.$supportedProperties.push("empty-message", "loading-message", - "offline-message", "render-root", "smartbinding", - "bindings", "actions"); - - /** - * @attribute {Boolean} render-root Sets or gets whether the root node of the data loaded - * into this element is rendered as well. - * @see apf.tree - */ - this.$propHandlers["render-root"] = function(value) { - this.renderRoot = value; - }; - - /** - * @attribute {String} empty-message Sets or gets the message displayed by this element - * when it contains no data. This property is inherited from parent nodes. - * When none is found, it is looked for on the appsettings element. Otherwise - * it defaults to the string "No items". - */ - this.$propHandlers["empty-message"] = function(value) { - this["empty-message"] = value; - - if (this.$updateClearMessage) - this.$updateClearMessage(this["empty-message"], "empty"); - }; - - /** - * @attribute {String} loading-message Sets or gets the message displayed by this - * element when it's loading. This property is inherited from parent nodes. - * When none is found, it is looked for on the appsettings element. Otherwise - * it defaults to the string "Loading...". - * - * #### Example - * - * This example uses property bindings to update the loading message. The - * position of the progressbar should be updated by the script taking care - * of loading the data. - * - * ```xml - * - * - * ``` - * - * #### Remarks - * - * Usually, a static loading message is displayed for only 100 milliseconds - * or so, whilst loading the data from the server. For instance, this is done - * when the load binding rule is used. In the code example below, a list - * binds on the selection of a tree displaying folders. When the selection - * changes, the list loads new data by extending the model. During the load - * of this new data, the loading message is displayed. - * - * ```xml - * - * - * ... - * - * - * - * ``` - */ - this.$propHandlers["loading-message"] = function(value) { - this["loading-message"] = value; - - if (this.$updateClearMessage) - this.$updateClearMessage(this["loading-message"], "loading"); - }; - - /** - * @attribute {String} offline-message Sets or gets the message displayed by this - * element when it can't load data because the application is offline. - * This property is inherited from parent nodes. When none is found it is - * looked for on the appsettings element. Otherwise it defaults to the - * string "You are currently offline...". - */ - this.$propHandlers["offline-message"] = function(value) { - this["offline-message"] = value; - - if (this.$updateClearMessage) - this.$updateClearMessage(this["offline-message"], "offline"); - }; - - /** - * @attribute {String} smartbinding Sets or gets the name of the SmartBinding for this - * element. - * - * A smartbinding is a collection of rules which define how data - * is transformed into representation, how actions on the representation are - * propagated to the data and it's original source, how drag&drop actions - * change the data and where the data is loaded from. Each of these are - * optionally defined in the smartbinding set and can exist independently - * of the smartbinding object. - * - * #### Example - * - * This example shows a fully specified smartbinding. Usually, only parts - * are used. This example shows a tree with files and folders. - * - * ```xml - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * ``` - * - * #### Remarks - * - * The smartbinding parts can also be assigned to an element by adding them - * directly as a child in aml. - * - * ```xml - * - * - * ... - * - * - * - * - * - * ### See Also - * - * There are several ways to be less verbose in assigning certain rules. For more information, see: - * - * * [[apf.bindings]] - * * [[apf.actions]] - * * [[apf.DragDrop]] - * - */ - this.$propHandlers["smartbinding"] = - - /** - * @attribute {String} actions Sets or gets the id of the actions element which - * provides the action rules for this element. Action rules are used to - * send changes on the bound data to a server. - * - * #### Example - * - * ```xml - * - * - * - * - * - * - * - * - * - * - * - * - * - * Undo - * ``` - */ - this.$propHandlers["actions"] = - - /** - * @attribute {String} bindings Sets or gets the id of the bindings element which - * provides the binding rules for this element. - * - * #### Example - * - * This example shows a set of binding rules that transform data into the - * representation of a list. In this case it displays the names of - * several email accounts, with after each account name the number of unread - * mails in that account. It uses JSLT to transform the caption. - * - * ```xml - * - * - * Account 1 - * - * - * - * - * Account 2 - * - * - * - * - * - * [text()] (#[mail[@read != 'true']]) - * - * - * - * ``` - * - * #### Remarks - * - * Bindings can also be assigned directly by putting the bindings tag as a - * child of this element. - * - * If the rule only contains a select attribute, it can be written in a - * short way by adding an attribute with the name of the rule to the - * element itself: - * - * ```xml - * - * ``` - */ - this.$propHandlers["bindings"] = function(value, prop) { - var local = "$" + prop + "Element"; - if (this[local]) - this[local].unregister(this); - - if (!value) - return; - - - - apf.nameserver.get(prop, value).register(this); - - - if (prop != "actions" && - this.$checkLoadQueue() === false && this.$amlLoaded) - 1+1; //@todo add reload queue. - //this.reload(); - }; - - - var eachBinds = {"caption":1, "icon":1, "select":1, "css":1, "sort":1, - "drop":2, "drag":2, "dragcopy":2, "eachvalue":1}; //Similar to apf.Class - - this.$addAttrBind = function(prop, fParsed, expression) { - //Detect if it uses an external model - if (fParsed.models) { - - if (this.hasFeature(apf.__MULTISELECT__)) { - - } - - } - - //Set listener for all models - var i, xpath, modelId, model, - paths = fParsed.xpaths, - list = {}; - //@todo when there is no model in xpath modelId == null... - for (i = 0; i < paths.length; i+=2) { - if (!list[(modelId = paths[i])]) - list[modelId] = 1; - else list[modelId]++ - } - - if (!this.$propsUsingMainModel) - this.$propsUsingMainModel = {}; - - var rule = (this.$attrBindings || (this.$attrBindings = {}))[prop] = { - cvalue: fParsed, - value: expression, - compile: apf.BindingRule.prototype.$compile, - models: [] - }; - - delete this.$propsUsingMainModel[prop]; - for (xpath, i = 0; i < paths.length; i+=2) { - modelId = paths[i]; - if (list[modelId] == -1) - continue; - - xpath = paths[i + 1]; - - if (modelId == "#" || xpath == "#") { - var m = (rule.cvalue3 || (rule.cvalue3 = apf.lm.compile(rule.value, { - xpathmode: 5 - }))).call(this, this.xmlRoot); - - //@todo apf3 this needs to be fixed in live markup - if (typeof m != "string") { - model = m.model && m.model.$isModel && m.model; - if (model) - xpath = m.xpath; - else if (m.model) { - model = typeof m.model == "string" ? apf.xmldb.findModel(m.model) : m.model; - xpath = apf.xmlToXpath(m.model, model.data) + (m.xpath ? "/" + m.xpath : ""); //@todo make this better - } - else { - //wait until model becomes available - this.addEventListener("prop." + prop, function(e) { - var m = (rule.cvalue3 || (rule.cvalue3 = apf.lm.compile(rule.value, { - xpathmode: 5 - }))).call(this, this.xmlRoot); - - if (m.model) { - this.removeEventListener("prop." + prop, arguments.callee); - var _self = this; - $setTimeout(function(){ - _self.$clearDynamicProperty(prop); - _self.$setDynamicProperty(prop, expression); - }, 10); - } - }); - continue; - } - } - else model = null; - } - else model = null; - - if (!model) { - if (modelId) { - - //@todo apf3.0 how is this cleaned up??? - //Add change listener to the data of the model - model = apf.nameserver.get("model", modelId) //is model creation useful here? - || apf.setReference(modelId, apf.nameserver.register("model", modelId, new apf.model())); - - } - else { - if (!this.$model && !this.$initingModel) - initModel.call(this); - - model = this.$model; - - if (!this.hasFeature(apf.__MULTISELECT__) - && eachBinds[prop] != 2 || !eachBinds[prop]) //@experimental - should not set this because model will load these attributes - this.$propsUsingMainModel[prop] = { - xpath: xpath, - optimize: list[modelId] == 1 - }; - } - } - - //@todo warn here if no model?? - if (model && (!this.hasFeature(apf.__MULTISELECT__) - && eachBinds[prop] != 2 || !eachBinds[prop])) { - //Create the attribute binding - //@todo: remove listenRoot = expression.indexOf("*[") > -1 -> because it doesnt make sense in certain context. recheck selection handling - model.$bindXmlProperty(this, prop, xpath, list[modelId] == 1); - rule.models.push(model); - } - - list[modelId] = -1; - } - - rule.xpath = xpath; - - this.$canLoadDataAttr = eachBinds[prop] == 1; //@todo apf3.0 remove - this.$checkLoadQueue(); - } - - this.$removeAttrBind = function(prop) { - //@todo apf3.0 - //$model.$unbindXmlProperty - var rule = this.$attrBindings[prop] - if (!rule) - return; - - delete this.$attrBindings[prop]; - delete this.$propsUsingMainModel[prop] - - var models = rule.models; - if (models.length) - for (var i = 0; i < models.length; i++) { - models[i].$unbindXmlProperty(this, prop); - } - else if (this.$model) - this.$model.$unbindXmlProperty(this, prop); - }; - - this.$initingModel; - function initModel(){ - this.$initingModel = true; - this.$setInheritedAttribute("model"); - } - - this.addEventListener("DOMNodeInsertedIntoDocument", function(e) { - //Set empty message if there is no data - if (!this.model && this.$setClearMessage && !this.value) - this.$setClearMessage(this["empty-message"], "empty"); - - this.$amlLoaded = true; //@todo this can probably be removed - this.$checkLoadQueue(); - }); - - - - /** - * @attribute {String} model Sets or gets the name of the model to load data from, or a - * datainstruction to load data. - * - * #### Example - * - * ```xml - * - * - * - * - * - * - * - * ``` - * - * #### Example - * - * Here's an example loading from an XML source: - * - * ```xml - * - * - * - * - * - * - * ``` - * - * #### Example - * - * ```xml - * - * - * - * - * - * - * - * ``` - * - * #### Example - * - * This example shows a dropdown from which the user can select a country. - * The list of countries is loaded from a model. Usually this would be loaded - * from a separate url, but for clarity it's inlined. When the user selects - * a country in the dropdown the value of the item is stored in the second - * model (mdlForm) at the position specified by the ref attribute. In this - * case this is the country element. - * - * ```xml - * Name - * - * - * Country - * - * - * - * - * - * USA - * Great Britain - * The Netherlands - * - * - * - * - * - * - * - * - * - * ``` - * - * #### Remarks - * - * This attribute is inherited from a parent when not set. You can use this - * to tell sets of elements to use the same model. - * - * ```xml - * - * Name - * - * - * Happiness - * - * - * - * - * - * - * ``` - * - * When no model is specified the default model is chosen. The default - * model is the first model that is found without a name, or if all models - * have a name, the first model found. - * - * @see apf.DataBinding.model - */ - this.$propHandlers["model"] = function(value) { - //Unset model - if (!value && !this.$modelParsed) { - if (this.$model) { - this.clear(); - this.$model.unregister(this); - this.$model = null; - this.lastModelId = ""; - } - else if (this.dataParent) - this.dataParent.parent = null; //Should be autodisconnected by property binding - - return; - } - this.$initingModel = true; - - var fParsed; - //Special case for property binding - if ((fParsed = this.$modelParsed) && fParsed.type != 2) { - var found, pb = fParsed.props; - - if (this.dataParent) - this.dataParent = null; //Should be autodisconnected by property binding - - //Try to figure out who is the dataParent - for (var prop in pb) { - - - this.dataParent = { - parent: self[prop.split(".")[0]], - xpath: null, - model: this.$modelParsed.instruction - }; - - found = true; - break; // We currently only support one data parent - } - - if (found) { - //@todo this statement doesnt make sense - /*//Maybe a compound model is found - if (!this.dataParent && (pb = fParsed.xpaths && fParsed.xpaths[0])) { - this.dataParent = { - parent: self[pb.split(".")[0]], - xpath: fParsed.xpaths[1], - model: this.$modelParsed.instruction - }; - }*/ - - if (this.dataParent && !this.dataParent.signalXmlUpdate) - this.dataParent.signalXmlUpdate = {}; - } - - this.$modelParsed = null; - } - - //Analyze the data - var model; - if (typeof value == "object") { - if (value.dataType == apf.ARRAY) { //Optimization used for templating - - model = apf.nameserver.get("model", value[0]); - model.register(this, value[1]); - return; - - } - else if (value.$isModel) { // A model node is passed - //Convert model object to value; - model = value; - value = this.model = model.name; - if (!value) - model.setProperty("id", value = this.model = "model" + model.$uniqueId); - - //@todo why not set directly here? - } - else { //if (this.dataParent) { //Data came through data parent - if (this.dataParent) - this.model = this.dataParent.model; //reset this property - - model = apf.xmldb.findModel(value); - if (!model) //@todo very strange, this should never happen, but it does - return; - var xpath = apf.xmlToXpath(value, null, true) || "."; - - - - model.register(this, xpath); - return; - } - /*else { - //@todo Error ?? - }*/ - } - else if (value.indexOf("[::") > -1) { //@experimental - var model, pNode = this; - do { - pNode = pNode.parentNode - model = pNode.getAttribute("model"); - } - while (pNode.parentNode && pNode.parentNode.nodeType == 1 && (!model || model == value)); - - if (model && typeof model == "object") - model = model.id; - - this.$inheritProperties.model = 3; - if (model) { - value = value.replace(/\[\:\:/g, "[" + model + "::"); - } - else { - apf.console.warn("No found model on any of the parents for this element while trying to overload model: " + value); - return; - } - } - - //Optimize xmlroot position and set model async (unset the old one) - //@todo apf3.0 this could be optimized by using apf.queue and only when not all info is there... - clearTimeout(this.$dbTimer); - if (!this.$amlLoaded && this.nodeType == 1) { - var _self = this; - this.$dbTimer = $setTimeout(function(){ - if (!_self.$amlDestroyed) - apf.setModel(value, _self); - }); - } - else - apf.setModel(value, this); - }; - - - /** - * @attribute {String} viewport Sets or gets the way this element renders its data. - * - * The possible values include: - * - `"virtual"`: this element only renders data that it needs to display. - * - `"normal`"`: this element renders all data at startup. - * @experimental - */ - this.$propHandlers["viewport"] = function(value) { - if (value != "virtual") - return; - - this.implement(apf.VirtualViewport); - }; - -}; - - apf.DataBinding.prototype = new apf[apf.Presentation ? "Presentation" : "AmlElement"](); - - -apf.config.$inheritProperties["model"] = 1; -apf.config.$inheritProperties["empty-message"] = 1; -apf.config.$inheritProperties["loading-message"] = 1; -apf.config.$inheritProperties["offline-message"] = 1; -apf.config.$inheritProperties["noloading"] = 1; - -apf.Init.run("databinding"); - - - - - - - - - -/** - * All elements inheriting from this {@link term.baseclass baseclass} can bind to data - * which contains multiple nodes. - * - * - * - * @class apf.MultiselectBinding - * @inherits apf.DataBinding - * @baseclass - * @default_private - * @allowchild item, choices - */ - -/* - * @define choices Container for item nodes which receive presentation. - * This element is part of the XForms specification. It is not necesary for - * the Ajax.org Markup Language. - * - * #### Example - * - * ```xml - * - * - * red - * blue - * green - * - * - * ``` - * @allowchild item - */ -apf.MultiselectBinding = function(){ - if (!this.setQueryValue) - this.implement(apf.DataBinding); - - this.$regbase = this.$regbase|apf.__MULTISELECT__; //We're pretending to have multiselect even though we might not. - - this.$init(function(){ - this.$selectTimer = {}; - }); -}; - -(function(){ - this.length = 0; - - //1 = force no bind rule, 2 = force bind rule - this.$attrExcludePropBind = apf.extend({ - caption: 2, - icon: 2, - eachvalue: 2, - select: 2, - css: 2, - sort: 2, - drag: 2, - drop: 2, - dragcopy: 2, - selected: 3, - //caret : 2, - each: 1, - "selection" : 3, //only databound when has an xpath - "selection-unique" : 3, //only databound when has an xpath - "selection-constructor" : 3 //only databound when has an xpath - }, this.$attrExcludePropBind); - - - /** - * Change the sorting order of this element. - * - * @param {Object} options The new sort options. These are applied incrementally. - * Any property that is not set is maintained unless the clear - * parameter is set to `true`. The following properties are available: - * - order ([[String]]) - * - [xpath] ([[String]]) - * - [type] ([[String]]) - * - [method] ([[String]]) - * - [getNodes] ([[Function]]): A function that retrieves a list of nodes. - * - [dateFormat] ([[String]]) - * - [getValue] ([[Function]]): A function that determines the string content based - * on an XML node as it's first argument. - * @param {Boolean} clear Removes the current sort options. - * @param {Boolean} noReload Specifies whether to reload the data of this component. - */ - this.resort = function(options, clear, noReload) { - if (!this.$sort) - this.$sort = new apf.Sort(); - - this.$sort.set(options, clear); - - if (this.clearAllCache) - this.clearAllCache(); - - if (noReload) - return; - - - /*if(this.hasFeature(apf.__VIRTUALVIEWPORT__)){ - this.$clearVirtualDataset(this.xmlRoot); - this.reload(); - - return; - }*/ - - - var _self = this; - (function sortNodes(xmlNode, htmlParent) { - if (!xmlNode) - return; - var sNodes = _self.$sort.apply( - apf.getArrayFromNodelist(xmlNode.selectNodes(_self.each))); - - for (var i = 0; i < sNodes.length; i++) { - if (_self.$isTreeArch || _self.$withContainer) { - var htmlNode = apf.xmldb.findHtmlNode(sNodes[i], _self); - - - - var container = _self.$findContainer(htmlNode); - - htmlParent.appendChild(htmlNode); - if (!apf.isChildOf(htmlNode, container, true)) - htmlParent.appendChild(container); - - sortNodes(sNodes[i], container); - } - else - htmlParent.appendChild(apf.xmldb.findHtmlNode(sNodes[i], _self)); - } - })(this.xmlRoot, this.$container); - - return options; - }; - - /** - * Change sorting from ascending to descending, and vice versa! - */ - this.toggleSortOrder = function(){ - return this.resort({"ascending" : !this.$sort.get().ascending}).ascending; - }; - - /** - * Retrieves the current sort options. - * - * @returns {Object} The current sort options. The following properties are available: - * - order ([[String]]) - * - xpath ([[String]]) - * - type ([[String]]) - * - method ([[String]]) - * - getNodes ([[Function]]): A function that retrieves a list of nodes. - * - dateFormat ([[String]]) - * - getValue ([[Function]]): A function that determines the string content based on - * an XML node as it's first argument. - * - */ - this.getSortSettings = function(){ - return this.$sort.get(); - }; - - - /* - * Optimizes load time when the xml format is very simple. - */ - // @todo Doc - this.$propHandlers["simpledata"] = function(value) { - if (value) { - this.getTraverseNodes = function(xmlNode) { - - if (this.$sort && !this.$isLoading) { - var nodes = apf.getArrayFromNodelist((xmlNode || this.xmlRoot).childNodes); - return this.$sort.apply(nodes); - } - - - return (xmlNode || this.xmlRoot).childNodes; - }; - - this.getFirstTraverseNode = function(xmlNode) { - return this.getTraverseNodes(xmlNode)[0];//(xmlNode || this.xmlRoot).childNodes[0]; - }; - - this.getLastTraverseNode = function(xmlNode) { - var nodes = this.getTraverseNodes(xmlNode);//(xmlNode || this.xmlRoot).childNodes; - return nodes[nodes.length - 1]; - }; - - this.getTraverseParent = function(xmlNode) { - if (!xmlNode.parentNode || xmlNode == this.xmlRoot) - return false; - - return xmlNode.parentNode; - }; - } - else { - delete this.getTraverseNodes; - delete this.getFirstTraverseNode; - delete this.getLastTraverseNode; - delete this.getTraverseParent; - } - }; - - /** - * Retrieves a node list containing the {@link term.datanode data nodes} which - * are rendered by this element. - * - * @param {XMLElement} [xmlNode] The parent element on which each query is applied. - * @return {NodeList} The node list containing the data nodes - */ - this.getTraverseNodes = function(xmlNode) { - - - - if (this.$sort) { - var nodes = apf.getArrayFromNodelist((xmlNode || this.xmlRoot).selectNodes(this.each)); - return this.$sort.apply(nodes); - } - - - return (xmlNode || this.xmlRoot).selectNodes(this.each); - }; - - /** - * Retrieves the first {@link term.datanode data node} which gets representation - * in this element. - * - * @param {XMLElement} [xmlNode] The parent element on which the each query is executed. - * @return {apf.AmlNode} The first represented {@link term.datanode data node} - */ - this.getFirstTraverseNode = function(xmlNode) { - - if (this.$sort) { - var nodes = (xmlNode || this.xmlRoot).selectNodes(this.each); - return this.$sort.apply(nodes)[0]; - } - - - return (xmlNode || this.xmlRoot).selectSingleNode(this.each); - }; - - /** - * Retrieves the last {@link term.datanode data node} which gets representation - * in this element. - * - * @param {XMLElement} [xmlNode] the parent element on which the each query is executed. - * @return {XMLElement} The last represented {@link term.datanode data node} - * - */ - this.getLastTraverseNode = function(xmlNode) { - var nodes = this.getTraverseNodes(xmlNode || this.xmlRoot); - return nodes[nodes.length-1]; - }; - - /** - * Determines whether a {@link term.datanode data node} is an each node. - * - * @param {XMLElement} [xmlNode] The parent element on which the each query is executed. - * @return {Boolean} Identifies whether the XML element is a each node. - * - */ - this.isTraverseNode = function(xmlNode) { - /* - Added optimization, only when an object has a tree architecture is it - important to go up to the each parent of the xmlNode, else the node - should always be based on the xmlroot of this component - */ - //this.$isTreeArch - var nodes = this.getTraverseNodes( - this.getTraverseParent(xmlNode) || this.xmlRoot); - for (var i = 0; i < nodes.length; i++) - if (nodes[i] == xmlNode) - return true; - return false; - }; - - /** - * Retrieves the next `each` node to be selected from a given `each` node. - * - * The method can do this in either direction and also return the Nth node for this algorithm. - * - * @param {XMLElement} xmlNode The starting point for determining the next selection. - * @param {Boolean} [up=false] The direction of the selection. - * @param {Number} [count=1] The distance in number of nodes. - * @return {XMLElement} The {@link term.datanode data node} to be selected next. - */ - this.getNextTraverseSelected = function(xmlNode, up, count) { - if (!xmlNode) - xmlNode = this.selected; - if (!count) - count = 1; - - var i = 0; - var nodes = this.getTraverseNodes(this.getTraverseParent(xmlNode) || this.xmlRoot); - while (nodes[i] && nodes[i] != xmlNode) - i++; - - var node = (up == null) - ? nodes[i + count] || nodes[i - count] - : (up ? nodes[i + count] : nodes[i - count]); - - //arguments[2] - return node || count && (i < count || (i + 1) > Math.floor(nodes.length / count) * count) - ? node - : (up ? nodes[nodes.length-1] : nodes[0]); - }; - - /** - * Retrieves the next `each` node. - * - * The method can do this in either direction and also return the Nth next node. - * - * @param {XMLElement} xmlNode The starting point for determining the next selection. - * @param {Boolean} [up=false] The direction of the selection. - * @param {Number} [count=1] The distance in number of nodes. - * @return {XMLElement} The {@link term.datanode data node} to be selected next. - */ - this.getNextTraverse = function(xmlNode, up, count) { - if (!count) - count = 1; - if (!xmlNode) - xmlNode = this.selected; - - var i = 0; - var nodes = this.getTraverseNodes(this.getTraverseParent(xmlNode) || this.xmlRoot); - while (nodes[i] && nodes[i] != xmlNode) - i++; - - var ind = i + (up ? -1 * count : count); - return nodes[ind < 0 ? 0 : ind]; - }; - - /** - * Retrieves the parent each node. - * - * In some cases the each rules has a complex form like 'children/item'. In - * those cases, the generated tree has a different structure from that of the XML - * data. For these situations, the `xmlNode.parentNode` property won't return - * the each parent; instead, this method will give you the right parent. - * - * @param {XMLElement} xmlNode The node for which the parent element will be determined. - * @return {XMLElement} The parent node or `null` if none was found. - */ - this.getTraverseParent = function(xmlNode) { - if (!xmlNode.parentNode || xmlNode == this.xmlRoot) - return false; - - //@todo this can be removed when we have a new xpath implementation - if (xmlNode.$regbase) - return xmlNode.parentNode; - - var x, id = xmlNode.getAttribute(apf.xmldb.xmlIdTag); - if (!id) { - //return false; - xmlNode.setAttribute(apf.xmldb.xmlIdTag, "temp"); - id = "temp"; - } - - /* - do { - xmlNode = xmlNode.parentNode; - if (xmlNode == this.xmlRoot) - return false; - if (this.isTraverseNode(xmlNode)) - return xmlNode; - } while (xmlNode.parentNode); - */ - - //This is not 100% correct, but good enough for now - - x = xmlNode.selectSingleNode("ancestor::node()[((" - + this.each + ")/@" + apf.xmldb.xmlIdTag + ")='" - + id + "']"); - - if (id == "temp") - xmlNode.removeAttribute(apf.xmldb.xmlIdTag); - return x; - }; - - if (!this.$findHtmlNode) { //overwritten by apf.Cache - /** - * Finds HTML presentation node in cache by ID. - * - * @param {String} id The id of the HTMLElement which is looked up. - * @return {HTMLElement} The HTMLElement found. When no element is found, `null` is returned. - * @private - */ - this.$findHtmlNode = function(id) { - return this.$pHtmlDoc.getElementById(id); - }; - } - - this.$setClearMessage = function(msg, className, lastHeight) { - if (this.more && this.$addMoreItem) this.$addMoreItem(); - if (!this.$empty) { - if (!this.$hasLayoutNode("empty")) - return; - - this.$getNewContext("empty"); - - var xmlEmpty = this.$getLayoutNode("empty"); - if (!xmlEmpty) return; - - this.$empty = apf.insertHtmlNode(xmlEmpty, this.$container); - } - else { - this.$container.appendChild(this.$empty); - } - - var empty = this.$getLayoutNode("empty", "caption", this.$empty); - - if (empty) - apf.setNodeValue(empty, msg || ""); - - this.$empty.setAttribute("id", "empty" + this.$uniqueId); - apf.setStyleClass(this.$empty, className, ["loading", "empty", "offline"]); - - //@todo apf3.0 cleanup? - var extH = apf.getStyle(this.$ext, "height"); - this.$empty.style.height = (lastHeight && (!extH || extH == "auto") && className != "empty") - ? (Math.max(10, (lastHeight - - apf.getHeightDiff(this.$empty) - - apf.getHeightDiff(this.$ext))) + "px") - : ""; - }; - - this.$updateClearMessage = function(msg, className) { - if (!this.$empty || this.$empty.parentNode != this.$container - || this.$empty.className.indexOf(className) == -1) - return; - - var empty = this.$getLayoutNode("empty", "caption", this.$empty); - if (empty) - apf.setNodeValue(empty, msg || ""); - } - - this.$removeClearMessage = function(){ - if (!this.$empty) - this.$empty = document.getElementById("empty" + this.$uniqueId); - if (this.$empty && this.$empty.parentNode) - this.$empty.parentNode.removeChild(this.$empty); - }; - - /* - * Set listeners, calls HTML creation methods and - * initializes select and focus states of object. - */ - this.$load = function(XMLRoot) { - //Add listener to XMLRoot Node - apf.xmldb.addNodeListener(XMLRoot, this); - - this.$isLoading = true; - - var length = this.getTraverseNodes(XMLRoot).length; - if (!this.renderRoot && !length) - return this.clear(null, null, true); //@todo apf3.0 this should clear and set a listener - - - //Traverse through XMLTree - var nodes = this.$addNodes(XMLRoot, null, null, this.renderRoot, null, 0, "load"); - - //Build HTML - this.$fill(nodes); - - this.$isLoading = false; - - //Select First Child - if (this.selectable) { - - //@todo apf3.0 optimize to not set selection when .selection or .selected is set on initial load - if (this["default"]) - this.select(this["default"]); - else if (this.autoselect) { - if (!this.selected) { - if (this.renderRoot) - this.select(XMLRoot, null, null, null, true); - else if (nodes.length) - this.$selectDefault(XMLRoot); - //else @todo apf3.0 this one doesnt seem needed - //this.clearSelection(); - } - } - else { - this.clearSelection(true); - var xmlNode = this.renderRoot - ? this.xmlRoot - : this.getFirstTraverseNode(); //should this be moved to the clearSelection function? - if (xmlNode) - this.setCaret(xmlNode); - - if (this.selected) - this.setProperty("selected", null); - if (this.choosen) - this.setProperty("choosen", null); - - } - } - - if (this.focussable) - apf.document.activeElement == this ? this.$focus() : this.$blur(); - - - if (length != this.length) - this.setProperty("length", length); - - }; - - var actionFeature = { - "insert" : 127,//11111110 - "replacenode" : 127,//11111110 - "attribute" : 255,//11111111 - "add" : 251,//11110111 - "remove" : 110, //01011110 - "redo-remove" : 79, //10011110 - "synchronize" : 127,//11111110 - "move-away" : 297,//11010111 - "move" : 141 //10011111 - }; - - /** - * @event xmlupdate Fires when XML of this element is updated. - * @param {Object} e The standard event object. The following properties are available: - * - action ([[String]]): The action that was executed on the XML. The following values are possible: - * - `text` : A text node is set - * - `attribute` : An attribute is set - * - `update`: An XML node is updated - * - `insert` : xml nodes are inserted - * - `add` : An XML node is added - * - `remove` : An XML node is removed (parent still set) - * - `redo`-remove`: An XML node is removed (parent not set) - * - `synchronize`: An unknown update - * - `move-away` : An XML node is moved (parent not set) - * - `move` An XML node is moved (parent still set) - * - xmlNode ([[XMLElement]]): The node that is subject to the update - * - result (`Mixed`): The result - * - UndoObj ([[apf.UndoData]]): The undo information - */ - /* - * Loops through parents of a changed node to find the first - * connected node. Based on the action, it will change, remove, - * or update the representation of the data. - */ - this.$xmlUpdate = function(action, xmlNode, listenNode, UndoObj, lastParent) { - if (!this.xmlRoot) - return; //@todo think about purging cache when xmlroot is removed - - var result, length, pNode, htmlNode, - startNode = xmlNode; - if (!listenNode) - listenNode = this.xmlRoot; - - if (action == "redo-remove") { - var loc = [xmlNode.parentNode, xmlNode.nextSibling]; - lastParent.appendChild(xmlNode); //ahum, i'm not proud of this one - var eachNode = this.isTraverseNode(xmlNode); - if (loc[0]) - loc[0].insertBefore(xmlNode, loc[1]); - else - lastParent.removeChild(xmlNode); - - if (!eachNode) - xmlNode = lastParent; - } - - //Get First ParentNode connected - do { - if (action == "add" && this.isTraverseNode(xmlNode) - && startNode == xmlNode) - break; //@todo Might want to comment this out for adding nodes under a eachd node - - if (xmlNode.getAttribute(apf.xmldb.xmlIdTag)) { - htmlNode = this.$findHtmlNode( - xmlNode.getAttribute(apf.xmldb.xmlIdTag) - + "|" + this.$uniqueId); - - if (xmlNode == listenNode && !this.renderRoot) { - if (xmlNode == this.xmlRoot && action != "insert" && action != "replacenode") { - //@todo apf3.0 - fix this for binding on properties - this.dispatchEvent("xmlupdate", { - action: action, - xmlNode: xmlNode, - UndoObj: UndoObj - }); - return; - } - break; - } - - if (htmlNode && actionFeature[action] & 2 - && !this.isTraverseNode(xmlNode)) - action = "remove"; //@todo why not break here? - - else if (!htmlNode && actionFeature[action] & 4 - && this.isTraverseNode(xmlNode)){ - action = "add"; - break; - } - - else if (htmlNode - && (startNode != xmlNode || xmlNode == this.xmlRoot)) { - if (actionFeature[action] & 1) - action = "update"; - else if (action == "remove") - return; - } - - if (htmlNode || action == "move") - break; - } - else if (actionFeature[action] & 8 && this.isTraverseNode(xmlNode)){ - action = "add"; - break; - } - - if (xmlNode == listenNode) { - if (actionFeature[action] & 128) //The change is not for us. - return; - - break; - } - xmlNode = xmlNode.parentNode; - } - while (xmlNode && xmlNode.nodeType != 9); - - - - - - // @todo Think about not having this code here - if (this.hasFeature(apf.__VIRTUALVIEWPORT__)) { - if (!this.$isInViewport(xmlNode)) //xmlNode is a eachd node - return; - } - - - //if(xmlNode == listenNode && !action.match(/add|synchronize|insert/)) - // return; //deleting nodes in parentData of object - - var foundNode = xmlNode; - if (xmlNode && xmlNode.nodeType == 9) - xmlNode = startNode; - - if (action == "replacenode") { - //var tmpNode; - //Case for replacing the xmlroot or its direct parent - if (UndoObj ? UndoObj.args[1] == this.xmlRoot : !this.xmlRoot.parentNode) - return this.load(UndoObj ? UndoObj.xmlNode : listenNode, {force: true}); - - //Case for replacing a node between the xmlroot and the traverse nodes - var nodes = this.getTraverseNodes(); - for (var i = 0, l = nodes.length; i < l; i++) { - if (apf.isChildOf(startNode, nodes[i])) - return this.load(this.xmlRoot, {force: true}); //This can be more optimized by using addNodes - } - //if ((tmpNode = this.getFirstTraverseNode()) && apf.isChildOf(startNode, tmpNode)) - } - - //Action Tracker Support - && xmlNode correct here??? - UndoObj.xmlNode works but fishy.... - if (UndoObj && xmlNode && !UndoObj.xmlNode) - UndoObj.xmlNode = xmlNode; - - //Check Move -- if value node isn't the node that was moved then only perform a normal update - if (action == "move" && foundNode == startNode) { - //if(!htmlNode) alert(xmlNode.getAttribute("id")+"|"+this.$uniqueId); - var isInThis = apf.isChildOf(this.xmlRoot, xmlNode.parentNode, true); //@todo this.getTraverseParent(xmlNode) - var wasInThis = apf.isChildOf(this.xmlRoot, UndoObj.extra.parent, true); - - //Move if both previous and current position is within this object - if (isInThis && wasInThis) - this.$moveNode(xmlNode, htmlNode, UndoObj.extra.oldParent); - else if (isInThis) //Add if only current position is within this object - action = "add"; - else if (wasInThis) //Remove if only previous position is within this object - action = "remove"; - } - else if (action == "move-away") { - var goesToThis = apf.isChildOf(this.xmlRoot, UndoObj.extra.parent, true); - if (!goesToThis) - action = "remove"; - } - - //Remove loading message - if (this.$removeClearMessage && this.$setClearMessage) { - if (this.getFirstTraverseNode()) - this.$removeClearMessage(); - else - this.$setClearMessage(this["empty-message"], "empty") - } - - //Check Insert - if (action == "insert" && (this.$isTreeArch || xmlNode == this.xmlRoot)) { - if (!xmlNode) - return; - - if (this.$hasLoadStatus(xmlNode) && this.$removeLoading) - this.$removeLoading(xmlNode); - - if (this.$container.firstChild && !apf.xmldb.getNode(this.$container.firstChild)) { - //Appearantly the content was cleared - this.$container.innerHTML = ""; - - if (!this.renderRoot) { - length = this.getTraverseNodes().length; - if (!length) - this.clear(); - } - } - - result = this.$addNodes(xmlNode, null, true, false, null, null, "insert");//this.$isTreeArch?? - - this.$fillParentHtml = (this.$getParentNode - ? this.$getParentNode(htmlNode) - : htmlNode); - this.$fillParent = xmlNode; - this.$fill(result); - - - - if (this.selectable && (length === 0 || !this.xmlRoot.selectSingleNode(this.each))) - return; - } - else if (action == "add") {// || !htmlNode (Check Add) - var parentHTMLNode; - pNode = this.getTraverseParent(xmlNode) || this.xmlRoot; - - if (pNode == this.xmlRoot) - parentHTMLNode = this.$container; - - if (!parentHTMLNode && this.$isTreeArch) { - parentHTMLNode = this.$findHtmlNode( - pNode.getAttribute(apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); - } - - //This should be moved into a function (used in setCache as well) - - if (!parentHTMLNode && this.getCacheItem) - parentHTMLNode = this.getCacheItem(pNode.getAttribute(apf.xmldb.xmlIdTag) - || (pNode.getAttribute(apf.xmldb.xmlDocTag) - ? "doc" + pNode.getAttribute(apf.xmldb.xmlDocTag) - : false)); - - - //Only update if node is in current representation or in cache - if (parentHTMLNode || this.$isTreeArch - && pNode == this.xmlRoot) { //apf.isChildOf(this.xmlRoot, xmlNode) - parentHTMLNode = (this.$findContainer && parentHTMLNode && parentHTMLNode.nodeType == 1 - ? this.$findContainer(parentHTMLNode) - : parentHTMLNode) || this.$container; - - result = this.$addNodes(xmlNode, parentHTMLNode, true, true, - apf.xmldb.getHtmlNode(this.getNextTraverse(xmlNode), this)); - - if (parentHTMLNode) - this.$fill(result); - } - } - else if (action == "remove") { //Check Remove - //&& (!xmlNode || foundNode == xmlNode && xmlNode.parentNode - //if (!xmlNode || startNode != xmlNode) //@todo unsure if I can remove above commented out statement - //return; - //I've commented above code out, because it disabled removing a - //subnode of a node that through an each rule makes the traverse - //node no longer a traverse node. - - //Remove HTML Node - if (htmlNode) - this.$deInitNode(xmlNode, htmlNode); - else if (startNode == this.xmlRoot) { - return this.load(null, { - noClearMsg: !this.dataParent || !this.dataParent.autoselect - }); - } - } - else if (htmlNode) { - - if (this.$sort) - this.$moveNode(xmlNode, htmlNode); - - - this.$updateNode(xmlNode, htmlNode); - - //Transaction 'niceties' - if (action == "replacenode" && this.hasFeature(apf.__MULTISELECT__) - && this.selected && xmlNode.getAttribute(apf.xmldb.xmlIdTag) - == this.selected.getAttribute(apf.xmldb.xmlIdTag)) { - this.selected = xmlNode; - } - - //if(action == "synchronize" && this.autoselect) this.reselect(); - } - else if (action == "redo-remove") { //Check Remove of the data (some ancestor) that this component is bound on - var testNode = this.xmlRoot; - while (testNode && testNode.nodeType != 9) - testNode = testNode.parentNode; - - if (!testNode) { - //Set Component in listening state until data becomes available again. - var model = this.getModel(true); - - - - return model.$waitForXml(this); - } - } - - - - //For tree based nodes, update all the nodes up - pNode = xmlNode ? xmlNode.parentNode : lastParent; - if (this.$isTreeArch && !this.$preventRecursiveUpdate - && pNode && pNode.nodeType == 1) { - do { - htmlNode = this.$findHtmlNode(pNode.getAttribute( - apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); - - if (htmlNode) - this.$updateNode(pNode, htmlNode); - } - while ((pNode = this.getTraverseParent(pNode)) && pNode.nodeType == 1); - } - - //Make sure the selection doesn't become corrupted - if (actionFeature[action] & 32 && this.selectable - && startNode == xmlNode - && (action != "insert" || xmlNode == this.xmlRoot)) { - - clearTimeout(this.$selectTimer.timer); - // Determine next selection - if (action == "remove" && apf.isChildOf(xmlNode, this.selected, true) - || xmlNode == this.$selectTimer.nextNode) { - this.$selectTimer.nextNode = this.getDefaultNext(xmlNode, this.$isTreeArch); - if (this.$selectTimer.nextNode == this.xmlRoot && !this.renderRoot) - this.$selectTimer.nextNode = null; - } - - //@todo Fix this by putting it after xmlUpdate when its using a timer - var _self = this; - this.$selectTimer.timer = $setTimeout(function(){ - _self.$checkSelection(_self.$selectTimer.nextNode); - _self.$selectTimer.nextNode = null; - }); - } - - - //Set dynamic properties that relate to the changed content - if (actionFeature[action] & 64) { - if (!length) - length = this.xmlRoot.selectNodes(this.each).length; - if (action == "remove") - length--; - if (length != this.length) - this.setProperty("length", length); - } - - - //Let's signal components that are waiting for xml to appear (@todo what about clearing the signalXmlUpdate) - if (this.signalXmlUpdate && actionFeature[action] & 16) { - var uniqueId; - for (uniqueId in this.signalXmlUpdate) { - if (parseInt(uniqueId, 10) != uniqueId) continue; //safari_old stuff - - var o = apf.lookup(uniqueId); - if (!this.selected) continue; - - xmlNode = this.selected.selectSingleNode(o.dataParent.xpath); - if (!xmlNode) continue; - - o.load(xmlNode); - } - } - - this.dispatchEvent("xmlupdate", { - action: action, - xmlNode: startNode, - traverseNode: xmlNode, - result: result, - UndoObj: UndoObj - }); - }; - - /* - * Loop through NodeList of selected Traverse Nodes - * and check if it has representation. If it doesn't - * representation is created via $add(). - */ - this.$addNodes = function(xmlNode, parent, checkChildren, isChild, insertBefore, depth, action) { - - - var htmlNode, lastNode, loopNode; - isChild = (isChild && (this.renderRoot && xmlNode == this.xmlRoot - || this.isTraverseNode(xmlNode))); - var nodes = isChild ? [xmlNode] : this.getTraverseNodes(xmlNode); - /*var loadChildren = nodes.length && this.$bindings["insert"] - ? this.$applyBindRule("insert", xmlNode) - : false; << UNUSED */ - - - var cId, cItem; - if (this.$isTreeArch && this.caching - && (!this.$bindings || !this.$bindings.each || !this.$bindings.each.filter) - && (cItem = this.cache[(cId = xmlNode.getAttribute(apf.xmldb.xmlIdTag))])) { - if (this.$subTreeCacheContext || this.$needsDepth) { - //@todo - //We destroy the current items, because currently we - //don't support multiple treecachecontexts - //and because datagrid needs to redraw depth - this.clearCacheItem(cId); - } - else { - this.$subTreeCacheContext = { - oHtml: cItem, - container: parent, - parentNode: null, - beforeNode: null - }; - - var htmlNode; - while (cItem.childNodes.length) - (parent || this.$container).appendChild(htmlNode = cItem.childNodes[0]); - - return nodes; - } - } - - - if (this.$isTreeArch && depth === null && action == "insert") { - depth = 0, loopNode = xmlNode; - while (loopNode && loopNode != this.xmlRoot) { - depth++; - loopNode = this.getTraverseParent(loopNode); - } - } - - for (var i = 0; i < nodes.length; i++) { - if (nodes[i].nodeType != 1) { - - continue; - } - - if (checkChildren) { - htmlNode = this.$findHtmlNode(nodes[i] - .getAttribute(apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); - } - - if (!htmlNode) { - //Retrieve DataBind ID - var Lid = apf.xmldb.nodeConnect(this.documentId, nodes[i], null, this); - - //Add Children - var beforeNode = isChild - ? insertBefore - : (lastNode ? lastNode.nextSibling : null),//(parent || this.$container).firstChild); - parentNode = this.$add(nodes[i], Lid, isChild ? xmlNode.parentNode : xmlNode, - beforeNode ? parent || this.$container : parent, beforeNode, - (!beforeNode && i == nodes.length - 1), depth, nodes[i + 1], action);//Should use getTraverParent - - //Exit if component tells us its done with rendering - if (parentNode === false) { - //Tag all needed xmlNodes for future reference - // @todo apf3.0 code below looks harmful... hence commented out (Mike) - /*for (var j = i; j < nodes.length; j++) - apf.xmldb.nodeConnect(this.documentId, nodes[j], - null, this);*/ - break; - } - - //Parse Children Recursively -> optimize: don't check children that can't exist - //if(this.$isTreeArch) this.$addNodes(nodes[i], parentNode, checkChildren); - } - - if (checkChildren) - lastNode = htmlNode;// ? htmlNode.parentNode.parentNode : null; - } - - return nodes; - }; - - this.$handleBindingRule = function(value, prop) { - if (!value) - this[prop] = null; - - //@todo apf3.0 fix parsing - if (prop == "each") { - value = value.charAt(0) == "[" && value.charAt(value.length - 1) == "]" - ? value.replace(/^\[|\]$/g, "") - : value; - - if (value.match(/^\w+::/)) { - var model = value.split("::"); //@todo this is all very bad - if (!apf.xPathAxis[model[0]]) { - this.setProperty("model", model[0]); - this.each = model[1]; - } - else - this.each = value; - } - else - this.each = value; - - if (this.each == this.$lastEach) - return; - - this.$lastEach = value; - - if (!this.$model && !this.$initingModel) { - this.$initingModel = true; - this.$setInheritedAttribute("model"); - - return; //@experimental - } - - if (this.$checkLoadQueue() !== false) //@experimental - return; - } - - //@todo apf3.0 find a better heuristic (portal demo) - if (this.xmlRoot && !this.$bindRuleTimer && this.$amlLoaded) { - var _self = this; - apf.queue.add("reload" + this.$uniqueId, function(){ - - _self.reload(); - }); - } - }; - - this.$select = function(o) { - - if (this.renaming) - this.stopRename(null, true); - - - if (!o || !o.style) - return; - return this.$setStyleClass(o, "selected"); - }; - - this.$deselect = function(o) { - - if (this.renaming) { - this.stopRename(null, true); - - if (this.ctrlselect) - return false; - } - - - if (!o) - return; - return this.$setStyleClass(o, "", ["selected", "indicate"]); - }; - - this.$indicate = function(o) { - - if (this.renaming) - this.stopRename(null, true); - - - if (!o) - return; - return this.$setStyleClass(o, "indicate"); - }; - - this.$deindicate = function(o) { - - if (this.renaming) - this.stopRename(null, true); - - - if (!o) - return; - return this.$setStyleClass(o, "", ["indicate"]); - }; - - - /** - * @attribute {String} each Sets or gets the XPath statement that determines which - * {@link term.datanode data nodes} are rendered by this element (also known - * as {@link term.eachnode each nodes}. - * - * - * #### Example - * - * ```xml - * Country - * - * - * - * - * - * USA - * Great Brittain - * The Netherlands - * ... - * - * - * ``` - * - * - */ - this.$propHandlers["each"] = - - /** - * @attribute {String} caption Sets or gets the text displayed on the item. - * - * #### Example - * - * ```xml - * - * ``` - */ - this.$propHandlers["caption"] = - - /** - * @attribute {String} eachvalue Sets or gets the {@link term.expression} - * that determines the value for each data nodes in the dataset of the element. - * - * #### Example - * - * ```xml - * - * ``` - * - */ - this.$propHandlers["eachvalue"] = - - /** - * @attribute {String} icon Sets or gets the XPath statement that determines from - * which XML node the icon URL is retrieved. - * - * #### Example - * - * ```xml - * - * ``` - */ - this.$propHandlers["icon"] = - - /** - * @attribute {String} tooltip Sets or gets the XPath statement that determines from - * which XML node the tooltip text is retrieved. - * - * #### Example - * - * ```xml - * - * ``` - */ - this.$propHandlers["tooltip"] = this.$handleBindingRule; - - - /** - * @attribute {String} sort Sets or gets the XPath statement that selects the sortable value. - * - * #### Example - * - * ```xml - * - * ``` - * - */ - this.$propHandlers["sort"] = function(value) { - if (value) { - this.$sort = new apf.Sort() - this.$sort.set({ - getValue: apf.lm.compile(value) - }); - } - else { - this.$sort = null; - } - } - - - /** - * @attribute {String} match Sets or gets the XPath statement that determines whether - * this node is selectable. - * - * #### Example - * - * ```xml - * - * ``` - * - */ - //this.$propHandlers["select"] = - -}).call(apf.MultiselectBinding.prototype = new apf.DataBinding()); - - - - - - - - -/** - * The baseclass for all standard data binding rules. - * - * @class apf.StandardBinding - * @private - * @baseclass - * @inherits apf.DataBinding - */ -apf.StandardBinding = function(){ - this.$init(true); - - - if (apf.Validation) - this.implement(apf.Validation); - - - if (!this.setQueryValue) - this.implement(apf.DataBinding); - - if (!this.defaultValue) //@todo please use this in a sentence - this.defaultValue = ""; - - /** - * Load XML into this element - * @private - */ - this.$load = function(xmlNode) { - //Add listener to XMLRoot Node - apf.xmldb.addNodeListener(xmlNode, this); - //Set Properties - - - var b, lrule, rule, bRules, bRule, value; - if (b = this.$bindings) { - for (rule in b) { - lrule = rule.toLowerCase(); - if (this.$supportedProperties.indexOf(lrule) > -1) { - bRule = (bRules = b[lrule]).length == 1 - ? bRules[0] - : this.$getBindRule(lrule, xmlNode); - - value = bRule.value || bRule.match; - - - //Remove any bounds if relevant - this.$clearDynamicProperty(lrule); - - if (value.indexOf("{") > -1 || value.indexOf("[") > -1) - this.$setDynamicProperty(lrule, value); - else - - if (this.setProperty) - this.setProperty(lrule, value, true); - } - } - } - - - //Think should be set in the event by the Validation Class - if (this.errBox && this.isValid && this.isValid()) - this.clearError(); - }; - - /** - * Set xml based properties of this element - * @private - */ - this.$xmlUpdate = function(action, xmlNode, listenNode, UndoObj) { - //Clear this component if some ancestor has been detached - if (action == "redo-remove") { - var retreatToListenMode = false, model = this.getModel(true); - if (model) { - var xpath = model.getXpathByAmlNode(this); - if (xpath) { - xmlNode = model.data.selectSingleNode(xpath); - if (xmlNode != this.xmlRoot) - retreatToListenMode = true; - } - } - - if (retreatToListenMode || this.xmlRoot == xmlNode) { - - - //Set Component in listening state untill data becomes available again. - return model.$waitForXml(this); - } - } - - //Action Tracker Support - if (UndoObj && !UndoObj.xmlNode) - UndoObj.xmlNode = this.xmlRoot; - - //Set Properties - - - var b, lrule, rule, bRules, bRule, value; - if (b = this.$bindings) { - for (rule in b) { - lrule = rule.toLowerCase(); - if (this.$supportedProperties.indexOf(lrule) > -1) { - bRule = (bRules = b[lrule]).length == 1 - ? bRules[0] - : this.$getBindRule(lrule, xmlNode); - - value = bRule.value || bRule.match; - - - //Remove any bounds if relevant - this.$clearDynamicProperty(lrule); - - if (value.indexOf("{") > -1 || value.indexOf("[") > -1) - this.$setDynamicProperty(lrule, value); - else - - if (this.setProperty) - this.setProperty(lrule, value); - } - } - } - - - //@todo Think should be set in the event by the Validation Class - if (this.errBox && this.isValid && this.isValid()) - this.clearError(); - - this.dispatchEvent("xmlupdate", { - action: action, - xmlNode: xmlNode, - UndoObj: UndoObj - }); - }; - - //@todo apf3.0 this is wrong - /** - * @event $clear Clears the data loaded into this element resetting it's value. - */ - this.addEventListener("$clear", function(nomsg, do_event) { - if (this.$propHandlers && this.$propHandlers["value"]) { - this.value = -99999; //force resetting - this.$propHandlers["value"].call(this, ""); - } - }); -}; -apf.StandardBinding.prototype = new apf.DataBinding(); - -apf.Init.run("standardbinding"); - - - - - - -apf.__MULTISELECT__ = 1 << 8; - - - -/** - * All elements inheriting from this {@link term.baseclass baseclass} have selection features. This includes handling - * for multiselect and several keyboard based selection interaction. It also - * takes care of {@link term.caret caret} handling when multiselect is enabled. Furthermore features - * for dealing with multinode component are included like adding and removing - * {@link term.datanode data nodes}. - * - * #### Example - * - * In this example the tree contains nodes that have a disabled flag set. These nodes cannot be selected. - * - * ```xml - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * ``` - * - * #### Example - * - * ```xml - * - * - * - * - * - * - * - * - * red - * green - * blue - * - * - * - * ``` - * - * @class apf.MultiSelect - * @baseclass - * @author Ruben Daniels (ruben AT ajax DOT org) - * @version %I%, %G% - * @since 0.5 - * - * @inherits apf.MultiselectBinding - * - */ -/** - * - * @binding select Determines whether the {@link term.eachnode each node} can be selected. - * - */ - /** - * - * * @binding value Determines the way the value for the element is retrieved - * from the selected node. The `apf.MultiSelect.value` property contains this value. - * - */ -apf.MultiSelect = function(){ - this.$init(function(){ - this.$valueList = []; - this.$selectedList = []; - }); -}; - -//@todo investigate if selectedList can be deprecated -(function() { - this.$regbase = this.$regbase|apf.__MULTISELECT__; - - // *** Properties *** // - - // @todo Doc is that right? - /** - * The last selected item of this element. - * @type {XMLElement} - */ - this.sellength = 0; - this.selected = null; - this.$selected = null; - - /** - * The XML element that has the {@link term.caret caret}. - * @type {XMLElement} - */ - this.caret = null; - this.$caret = null; - - /** - * Specifies whether to use a {@link term.caret caret} in the interaction of this element. - * @type {Boolean} - */ - this.useindicator = true; - - - - /** - * Removes a {@link term.datanode data node} from the data of this element. - * - * #### Example - * - * A simple list showing products. This list is used in all the following examples. - * - * ```xml - * - * - * - * - * [@type].png - * - * - * - * - * - * - * - * - * - * - * - * - * ``` - * - * #### Example - * - * This example selects a product by its value and then removes the selection. - * - * ```xml - * - * ``` - * - * #### Example - * - * This example gets a product by its value and then removes it. - * - * ```xml - * - * var xmlNode = myList.findXmlNodeByValue("product20"); - * myList.remove(xmlNode); - * - * ``` - * - * #### Example - * - * This example retrieves all nodes from the list. All items with a length - * greater than 10 are singled out and removed. - * - * ```xml - * 10) - * removeList.push(list[i]); - * } - * myList.remove(removeList); - * } - * ]]> - * ``` - * - * #### Remarks - * - * Another way to trigger this method is by using the action attribute on a - * button. - * - * ```xml - * Remove item - * ``` - * - * Using the action methodology, you can let the original data source - * (usually the server) know that the user removed an item: - * - * ```xml - * - * - * - * - * ``` - * - * For undo, this action should be extended and the server should maintain a - * copy of the deleted item. - * - * ```xml - * - * - * - * - * Remove item - * - * ``` - * - * @action - * @param {NodeList | XMLElement} [nodeList] The {@link term.datanode data node}(s) to be removed. If none are specified, the current selection is removed. - * - * @return {Boolean} Indicates if the removal succeeded - */ - this.remove = function(nodeList) { - //Use the current selection if no xmlNode is defined - if (!nodeList) - nodeList = this.$valueList; - - //If we're an xml node let's convert - if (nodeList.nodeType) - nodeList = [nodeList]; - - //If there is no selection we'll exit, nothing to do - if (!nodeList || !nodeList.length) - return; - - - - var changes = []; - for (var i = 0; i < nodeList.length; i++) { - changes.push({ - action: "removeNode", - args: [nodeList[i]] - }); - } - - if (this.$actions["removegroup"]) - return this.$executeAction("multicall", changes, "removegroup", nodeList[0]); - else { - return this.$executeAction("multicall", changes, "remove", - nodeList[0], null, null, nodeList.length > 1 ? nodeList : null); - } - }; - - /** - * Adds a {@link term.datanode data node} to the data of this element. - * - * #### Example - * - * A simple list showing products. This list is used in all following examples. - * - * ```xml - * - * - * - * - * [@type].png - * - * - * - * - * - * - * - * - * - * - * - * - * ``` - * - * #### Example - * - * This example adds a product to this element selection. - * - * ```xml - * '); - * } - * ]]> - * ``` - * - * #### Example - * - * This example copys the selected product, changes its name, and then - * adds it. After selecting the new node, the user is offered a rename input - * box. - * - * ```xml - * - * ``` - * - * #### Remarks - * Another way to trigger this method is by using the action attribute on a - * button. - * - * ```xml - * - * - * - * - * - * - * - * - * - * Add new product - * ``` - * - * Using the action methodology you can let the original data source (usually the server) know that the user added an item. - * - * ```xml - * - * ``` - * - * For undo, this action should be extended as follows. - * - * ```xml - * - * - * - * - * - * - * - * - * - * Add new product - * - * ``` - * - * In some cases the server needs to create the new product before its - * added. This is done as follows. - * - * ```xml - * - * ``` - * Alternatively the template for the addition can be provided as a child of - * the action rule. - * ``` - * - * - * - * ``` - * - * @action - * @param {XMLElement} [xmlNode] The {@link term.datanode data node} which is added. If none is specified the action will use the action rule to try to retrieve a new node to add - * @param {XMLElement} [pNode] The parent node of the added {@link term.datanode data node} - * @param {XMLElement} [beforeNode] The position where the XML element should be inserted - * @return {XMLElement} The added {@link term.datanode data node} or false on failure - */ - this.add = function(xmlNode, pNode, beforeNode, userCallback) { - var rule; - - if (this.$actions) { - if (xmlNode && xmlNode.nodeType) - rule = this.$actions.getRule("add", xmlNode); - else if (typeof xmlNode == "string") { - if (xmlNode.trim().charAt(0) == "<") { - xmlNode = apf.getXml(xmlNode); - rule = this.$actions.getRule("add", xmlNode); - } - else { - var rules = this.$actions["add"]; - for (var i = 0, l = rules.length; i < l; i++) { - if (rules[i].getAttribute("type") == xmlNode) { - xmlNode = null; - rule = rules[i]; - break; - } - } - } - } - - if (!rule) - rule = (this.$actions["add"] || {})[0]; - } - else - rule = null; - - - - var refNode = this.$isTreeArch ? this.selected || this.xmlRoot : this.xmlRoot, - amlNode = this, - callback = function(addXmlNode, state, extra) { - if (state != apf.SUCCESS) { - var oError; - - oError = new Error(apf.formatErrorString(1032, amlNode, - "Loading xml data", - "Could not add data for control " + amlNode.name - + "[" + amlNode.tagName + "] \nUrl: " + extra.url - + "\nInfo: " + extra.message + "\n\n" + xmlNode)); - - if (extra.tpModule.retryTimeout(extra, state, amlNode, oError) === true) - return true; - - throw oError; - } - - /*if (apf.supportNamespaces && node.namespaceURI == apf.ns.xhtml) { - node = apf.getXml(node.xml.replace(/xmlns\=\"[^"]*\"/g, "")); - //@todo import here for webkit? - }*/ - - if (typeof addXmlNode != "object") - addXmlNode = apf.getXmlDom(addXmlNode).documentElement; - if (addXmlNode.getAttribute(apf.xmldb.xmlIdTag)) - addXmlNode.setAttribute(apf.xmldb.xmlIdTag, ""); - - var actionNode = amlNode.$actions && - amlNode.$actions.getRule("add", amlNode.$isTreeArch - ? amlNode.selected - : amlNode.xmlRoot); - if (!pNode) { - if (actionNode && actionNode.parent) { - pNode = (actionNode.cparent - || actionNode.compile("parent", { - xpathmode: 2, - injectself: true - }))(amlNode.$isTreeArch - ? amlNode.selected || amlNode.xmlRoot - : amlNode.xmlRoot); - } - else { - pNode = amlNode.$isTreeArch - ? amlNode.selected || amlNode.xmlRoot - : amlNode.xmlRoot - } - } - - if (!pNode) - pNode = amlNode.xmlRoot; - - //Safari issue not auto importing nodes: - if (apf.isWebkit && pNode.ownerDocument != addXmlNode.ownerDocument) - addXmlNode = pNode.ownerDocument.importNode(addXmlNode, true); - - - - if (amlNode.$executeAction("appendChild", - [pNode, addXmlNode, beforeNode], "add", addXmlNode) !== false - && amlNode.autoselect) - amlNode.select(addXmlNode); - - if (userCallback) - userCallback.call(amlNode, addXmlNode); - - return addXmlNode; - }; - - if (xmlNode) - return callback(xmlNode, apf.SUCCESS); - else { - if (rule.get) - return apf.getData(rule.get, {xmlNode: refNode, callback: callback}) - else { - - } - } - - return addXmlNode; - }; - - if (!this.setValue) { - /** - * Sets the value of this element. The value - * corresponds to an item in the list of loaded {@link term.datanode data nodes}. This - * element will receive the selection. If no {@link term.datanode data node} is found, the - * selection is cleared. - * - * @param {String} value The new value for this element. - * @see apf.MultiSelect.getValue - */ - this.setValue = function(value, disable_event) { - // @todo apf3.0 what does noEvent do? in this scope it's useless and - // doesn't improve codeflow with a global lookup and assignment - noEvent = disable_event; - this.setProperty("value", value, false, true); - noEvent = false; - }; - } - - /** - * Retrieves an {@link term.datanode data node} that has a value that corresponds to the - * string that is searched on. - * @param {String} value The value to match. - * @returns {XMLNode} The found node, or `false` - */ - this.findXmlNodeByValue = function(value) { - var nodes = this.getTraverseNodes(), - bindSet = this.$attrBindings["eachvalue"] - && "eachvalue" || this.$bindings["value"] - && "value" || this.$hasBindRule("caption") && "caption"; - - if (!bindSet) - return false; - - for (var i = 0; i < nodes.length; i++) { - if (this.$applyBindRule(bindSet, nodes[i]) == value) - return nodes[i]; - } - }; - - if (!this.getValue) { - /** - * Retrieves the value of this element. This is the value of the - * first selected {@link term.datanode data node}. - * - */ - this.getValue = function(xmlNode, noError) { - return this.value; - /* - if (!this.bindingRules && !this.caption) - return false; - - - - return this.$applyBindRule(this.$mainBind, xmlNode || this.selected, null, true) - || this.$applyBindRule("caption", xmlNode || this.selected, null, true); - */ - }; - } - - /** - * Select the current selection...again. - * - */ - this.reselect = function(){ // @todo Add support for multiselect - if (this.selected) this.select(this.selected, null, this.ctrlselect, - null, true);//no support for multiselect currently. - }; - - /** - * @event beforeselect Fires before a {@link apf.MultiSelect.select selection} is made - * @param {Object} e The standard event object. It contains the following properties: - * - `selected` ([[XMLElement]]): The {@link term.datanode data node} that will be selected - * - `selection` ([[Array]]): An array of {@link term.datanode data nodes} that will be selected - * - `htmlNode` ([[HTMLElement]]): The HTML element that visually represents the {@link term.datanode data node} - */ - /** - * @event afterselect Fires after a {@link apf.MultiSelect.select selection} is made - * @param {Object} e The standard event object. It contains the following properties: - * - `selected` ([[XMLElement]]): the {@link term.datanode data node} that was selected - * - `selection` ([[Array]](): an array of {@link term.datanode data node} that are selected - * - `htmlNode` ([[HTMLElement]](): the HTML element that visually represents the {@link term.datanode data node} - */ - /** - * Selects a single, or set, of {@link term.eachnode each nodes}. - * The selection can be visually represented in this element. - * - * @param {Mixed} xmlNode The identifier to determine the selection. It can be one of the following values: - * - ([[XMLElement]]): The {@link term.datanode data node} to be used in the selection as a start/end point or to toggle the selection on the node. - * - ([[HTMLElement]]): The HTML element node used as visual representation of {@link term.datanode data node}. - * Used to determine the {@link term.datanode data node} for selection. - * - ([[String]]): The value of the {@link term.datanode data node} to be selected. - * @param {Boolean} [ctrlKey] Indicates whether the [[keys: Ctrl]] key was pressed - * @param {Boolean} [shiftKey] Indicates whether the [[keys: Shift]] key was pressed - * @param {Boolean} [fakeselect] Indicates whether only visually a selection is made - * @param {Boolean} [force] Indicates whether reselect is forced - * @param {Boolean} [noEvent] Indicates whether to not call any event - * @return {Boolean} Indicates whether the selection could be made - * - */ - this.select = function(xmlNode, ctrlKey, shiftKey, fakeselect, force, noEvent, userAction) { - if (!this.selectable || this.disabled) - return; - - if (parseInt(fakeselect) == fakeselect) { - //Don't select on context menu - if (fakeselect == 2) { - fakeselect = true; - userAction = true; - } - else { - fakeselect = false; - userAction = true; - } - } - - if (this.$skipSelect) { - this.$skipSelect = false; - return; - } - - if (this.ctrlselect && !shiftKey) - ctrlKey = true; - - if (!this.multiselect) - ctrlKey = shiftKey = false; - - // Selection buffering (for async compatibility) - if (!this.xmlRoot) { - if (!this.$buffered) { - var f; - this.addEventListener("afterload", f = function(){ - this.select.apply(this, this.$buffered); - this.removeEventListener("afterload", f); - delete this.$buffered; - }); - } - - this.$buffered = Array.prototype.slice.call(arguments); - return; - } - - var htmlNode; - - // *** Type Detection *** // - if (!xmlNode) { - - - return false; - } - - if (typeof xmlNode != "object") { - var str = xmlNode; xmlNode = null; - if (typeof xmlNode == "string") - xmlNode = apf.xmldb.getNodeById(str); - - //Select based on the value of the xml node - if (!xmlNode) { - xmlNode = this.findXmlNodeByValue(str); - if (!xmlNode) { - this.clearSelection(noEvent); - return; - } - } - } - - if (!(typeof (xmlNode.style || "") == "object")) { - htmlNode = this.$findHtmlNode(xmlNode.getAttribute( - apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); - } - else { - var id = (htmlNode = xmlNode).getAttribute(apf.xmldb.htmlIdTag); - while (!id && htmlNode.parentNode) - id = (htmlNode = htmlNode.parentNode).getAttribute( - apf.xmldb.htmlIdTag); - - xmlNode = apf.xmldb.getNodeById(id);//, this.xmlRoot); - } - - if (!shiftKey && !ctrlKey && !force && !this.reselectable - && this.$valueList.length <= 1 && this.$valueList.indexOf(xmlNode) > -1) - return; - - if (this.dispatchEvent('beforeselect', { - selected: xmlNode, - htmlNode: htmlNode, - ctrlKey: ctrlKey, - shiftKey: shiftKey, - force: force, - captureOnly: noEvent - }) === false) - return false; - - // *** Selection *** // - - var lastIndicator = this.caret; - this.caret = xmlNode; - - //Multiselect with SHIFT Key. - if (shiftKey) { - var range = this.$calcSelectRange( - this.$valueList[0] || lastIndicator, xmlNode); - - if (this.$caret) - this.$deindicate(this.$caret); - - this.selectList(range); - - this.$selected = - this.$caret = this.$indicate(htmlNode); - } - else if (ctrlKey) { //Multiselect with CTRL Key. - //Node will be unselected - if (this.$valueList.contains(xmlNode)) { - if (this.selected == xmlNode) { - this.$deselect(this.$findHtmlNode(this.selected.getAttribute( - apf.xmldb.xmlIdTag) + "|" + this.$uniqueId)); - - this.$deindicate(this.$caret); - - if (this.$valueList.length && !fakeselect) { - //this.$selected = this.$selectedList[0]; - this.selected = this.$valueList[0]; - } - } - else - this.$deselect(htmlNode, xmlNode); - - if (!fakeselect) { - this.$selectedList.remove(htmlNode); - this.$valueList.remove(xmlNode); - } - - if (htmlNode != this.$caret) - this.$deindicate(this.$caret); - - this.$selected = - this.$caret = this.$indicate(htmlNode); - } - // Node will be selected - else { - if (this.$caret) - this.$deindicate(this.$caret); - this.$caret = this.$indicate(htmlNode, xmlNode); - - this.$selected = this.$select(htmlNode, xmlNode); - this.selected = xmlNode; - - if (!fakeselect) { - this.$selectedList.push(htmlNode); - this.$valueList.push(xmlNode); - } - } - } - else if (fakeselect && htmlNode && this.$selectedList.contains(htmlNode)) {//Return if selected Node is htmlNode during a fake select - return; - } - else { //Normal Selection - //htmlNode && this.$selected == htmlNode && this.$valueList.length <= 1 && this.$selectedList.contains(htmlNode) - if (this.$selected) - this.$deselect(this.$selected); - if (this.$caret) - this.$deindicate(this.$caret); - if (this.selected) - this.clearSelection(true); - - this.$caret = this.$indicate(htmlNode, xmlNode); - this.$selected = this.$select(htmlNode, xmlNode); - this.selected = xmlNode; - - this.$selectedList.push(htmlNode); - this.$valueList.push(xmlNode); - } - - if (this.delayedselect && (typeof ctrlKey == "boolean")){ - var _self = this; - $setTimeout(function(){ - if (_self.selected == xmlNode) - _self.dispatchEvent("afterselect", { - selection: _self.$valueList, - selected: xmlNode, - caret: _self.caret, - captureOnly: noEvent - }); - }, 10); - } - else { - this.dispatchEvent("afterselect", { - selection: this.$valueList, - selected: xmlNode, - caret: this.caret, - captureOnly: noEvent - }); - } - - return true; - }; - - /** - * @event beforechoose Fires before a choice is made. - * @param {Object} e The standard event object. It contains the following properties: - * - `xmlNode` ([[XMLElement]]): The {@link term.datanode data node} that was choosen - * - */ - /** - * @event afterchoose Fires after a choice is made. - * @param {Object} e The standard event object. It contains the following properties: - * - `xmlNode` ([[XMLElement]]): The {@link term.datanode data node} that was choosen - */ - /** - * Chooses a selected item. This is done by double clicking on the item or - * pressing the Enter key. - * - * @param {Mixed} xmlNode The identifier to determine the selection. It can be one of the following values: - * - [[XMLElement]]: The {@link term.datanode data node} to be choosen - * - [[HTMLElement]]: The HTML element node used as visual representation of {@link term.datanode data node} - * Used to determine the {@link term.datanode data node} - * - [[String]] : The value of the {@link term.datanode data node} to be choosen - * - */ - this.choose = function(xmlNode, userAction) { - if (!this.selectable || userAction && this.disabled) return; - - if (this.dispatchEvent("beforechoose", {xmlNode : xmlNode}) === false) - return false; - - if (xmlNode && !(typeof (xmlNode.style || "") == "object")) - this.select(xmlNode); - - - if (this.hasFeature(apf.__DATABINDING__) - && this.dispatchEvent("afterchoose", {xmlNode : this.selected}) !== false) - this.setProperty("chosen", this.selected); - - }; - - /* - * Removes the selection of one or more selected nodes. - * - * @param {Boolean} [noEvent] Indicates whether or not to call any events - */ - // @todo Doc - this.clearSelection = function(noEvent, userAction) { - if (!this.selectable || userAction && this.disabled || !this.$valueList.length) - return; - - if (!noEvent) { - if (this.dispatchEvent("beforeselect", { - selection: [], - selected: null, - caret: this.caret - }) === false) - return false; - } - - //Deselect html nodes - var htmlNode; - for (var i = this.$valueList.length - 1; i >= 0; i--) { - if (this.$valueList[i]) { - htmlNode = this.$findHtmlNode(this.$valueList[i].getAttribute( - apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); - this.$deselect(htmlNode); - } - } - - //Reset internal variables - this.$selectedList.length = 0; - this.$valueList.length = 0; - this.$selected = - this.selected = null; - - //Redraw indicator - if (this.caret) { - htmlNode = this.$findHtmlNode(this.caret.getAttribute( - apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); - - this.$caret = this.$indicate(htmlNode); - } - - if (!noEvent) { - this.dispatchEvent("afterselect", { - selection: this.$valueList, - selected: null, - caret: this.caret - }); - } - }; - - /* - * Selects a set of items - * - * @param {Array} xmlNodeList the {@link term.datanode data nodes} that will be selected. - */ - //@todo Doc I think there are missing events here? - this.selectList = function(xmlNodeList, noEvent, selected, userAction) { - if (!this.selectable || userAction && this.disabled) return; - - if (this.dispatchEvent("beforeselect", { - selection: xmlNodeList, - selected: selected || xmlNodeList[0], - caret: this.caret, - captureOnly: noEvent - }) === false) - return false; - - this.clearSelection(true); - - for (var sel, i = 0; i < xmlNodeList.length; i++) { - //@todo fix select state in unserialize after removing - if (!xmlNodeList[i] || xmlNodeList[i].nodeType != 1) continue; - var htmlNode, - xmlNode = xmlNodeList[i]; - - //Type Detection - if (typeof xmlNode != "object") - xmlNode = apf.xmldb.getNodeById(xmlNode); - if (!(typeof (xmlNode.style || "") == "object")) - htmlNode = this.$pHtmlDoc.getElementById(xmlNode.getAttribute( - apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); - else { - htmlNode = xmlNode; - xmlNode = apf.xmldb.getNodeById(htmlNode.getAttribute( - apf.xmldb.htmlIdTag)); - } - - if (!xmlNode) { - - continue; - } - - //Select Node - if (htmlNode) { - if (!sel && selected == htmlNode) - sel = htmlNode; - - this.$select(htmlNode, xmlNode); - this.$selectedList.push(htmlNode); - } - this.$valueList.push(xmlNode); - } - - this.$selected = sel || this.$selectedList[0]; - this.selected = selected || this.$valueList[0]; - - this.dispatchEvent("afterselect", { - selection: this.$valueList, - selected: this.selected, - caret: this.caret, - captureOnly: noEvent - }); - }; - - /** - * @event indicate Fires when an item becomes the indicator. - */ - - /** - * Sets the {@link term.caret caret} on an item to indicate to the user that the keyboard - * actions are done relevant to that item. Using the keyboard, - * a user can change the position of the indicator using the [[keys: Ctrl]] and arrow - * keys while not making a selection. When making a selection with the mouse - * or keyboard, the indicator is always set to the selected node. Unlike a - * selection there can be only one indicator item. - * - * @param {Mixed} xmlNode The identifier to determine the indicator. Its possible values include: - * - {XMLElement} The {@link term.datanode data node} to be set as indicator. - * - {HTMLElement} The HTML element node used as visual representation of - * {@link term.datanode data node}. Used to determine the {@link term.datanode data node}. - * - {String} The value of the {@link term.datanode data node} to be set as an indicator. - */ - this.setCaret = function(xmlNode) { - if (!xmlNode) { - if (this.$caret) - this.$deindicate(this.$caret); - this.caret = - this.$caret = null; - return; - } - - // *** Type Detection *** // - var htmlNode; - if (typeof xmlNode != "object") - xmlNode = apf.xmldb.getNodeById(xmlNode); - if (!(typeof (xmlNode.style || "") == "object")) { - htmlNode = this.$findHtmlNode(xmlNode.getAttribute( - apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); - } - else { - var id = (htmlNode = xmlNode).getAttribute(apf.xmldb.htmlIdTag); - while (!id && htmlNode.parentNode && htmlNode.parentNode.nodeType == 1) - id = (htmlNode = htmlNode.parentNode).getAttribute( - apf.xmldb.htmlIdTag); - if (!id) alert(this.$int.outerHTML); - - xmlNode = apf.xmldb.getNodeById(id); - } - - if (this.$caret) { - //this.$deindicate(this.$findHtmlNode(this.caret.getAttribute( - //apf.xmldb.xmlIdTag) + "|" + this.$uniqueId)); - this.$deindicate(this.$caret); - } - - this.$caret = this.$indicate(htmlNode); - this.setProperty("caret", this.caret = xmlNode); - }; - - /* - * @private - */ - this.$setTempSelected = function(xmlNode, ctrlKey, shiftKey, down) { - clearTimeout(this.timer); - - if (this.$bindings.selectable) { - while (xmlNode && !this.$getDataNode("selectable", xmlNode)) { - xmlNode = this.getNextTraverseSelected(xmlNode, !down); - } - if (!xmlNode) return; - } - - if (!this.multiselect) - ctrlKey = shiftKey = false; - - if (ctrlKey || this.ctrlselect) { - if (this.$tempsel) { - this.select(this.$tempsel); - this.$tempsel = null; - } - - this.setCaret(xmlNode); - } - else if (shiftKey) { - if (this.$tempsel) { - this.$selectTemp(); - this.$deselect(this.$tempsel); - this.$tempsel = null; - } - - this.select(xmlNode, null, shiftKey); - } - else if (!this.bufferselect || this.$valueList.length > 1) { - this.select(xmlNode); - } - else { - var id = apf.xmldb.getID(xmlNode, this); - - this.$deselect(this.$tempsel || this.$selected); - this.$deindicate(this.$tempsel || this.$caret); - this.$tempsel = this.$indicate(document.getElementById(id)); - this.$select(this.$tempsel); - - var _self = this; - this.timer = $setTimeout(function(){ - _self.$selectTemp(); - }, 400); - } - }; - - /* - * @private - */ - this.$selectTemp = function(){ - if (!this.$tempsel) - return; - - clearTimeout(this.timer); - this.select(this.$tempsel); - - this.$tempsel = null; - this.timer = null; - }; - - /** - * Selects all the {@link term.eachnode each nodes} of this element - * - */ - this.selectAll = function(userAction) { - if (!this.multiselect || !this.selectable - || userAction && this.disabled || !this.xmlRoot) - return; - - var nodes = this.$isTreeArch - ? this.xmlRoot.selectNodes(".//" - + this.each.split("|").join("|.//")) - : this.xmlRoot.selectNodes(this.each); - - this.selectList(nodes); - }; - - /** - * Retrieves an Array or a document fragment containing all the selected - * {@link term.datanode data nodes} from this element. - * - * @param {Boolean} [xmldoc] Specifies whether the method should return a document fragment. - * @return {Mixed} The selection of this element. - */ - this.getSelection = function(xmldoc) { - var i, r; - if (xmldoc) { - r = this.xmlRoot - ? this.xmlRoot.ownerDocument.createDocumentFragment() - : apf.getXmlDom().createDocumentFragment(); - for (i = 0; i < this.$valueList.length; i++) - apf.xmldb.cleanNode(r.appendChild( - this.$valueList[i].cloneNode(true))); - } - else { - for (r = [], i = 0; i < this.$valueList.length; i++) - r.push(this.$valueList[i]); - } - - return r; - }; - - this.$getSelection = function(htmlNodes) { - return htmlNodes ? this.$selectedList : this.$valueList; - }; - - /** - * Selects the next {@link term.datanode data node} to be selected. - * - * @param {XMLElement} xmlNode The context {@link term.datanode data node}. - * @param {Boolean} [isTree] If `true`, indicates that this node is a tree, and should select children - */ - this.defaultSelectNext = function(xmlNode, isTree) { - var next = this.getNextTraverseSelected(xmlNode); - //if(!next && xmlNode == this.xmlRoot) return; - - //@todo Why not use this.$isTreeArch ?? - if (next || !isTree) - this.select(next ? next : this.getTraverseParent(xmlNode)); - else - this.clearSelection(true); - }; - - /** - * Selects the next {@link term.datanode data node} when available. - */ - this.selectNext = function(){ - var xmlNode = this.getNextTraverse(); - if (xmlNode) - this.select(xmlNode); - }; - - /** - * Selects the previous {@link term.datanode data node} when available. - */ - this.selectPrevious = function(){ - var xmlNode = this.getNextTraverse(null, -1); - if (xmlNode) - this.select(xmlNode); - }; - - /* - * @private - */ - this.getDefaultNext = function(xmlNode, isTree){ //@todo why is isTree an argument - var next = this.getNextTraverseSelected(xmlNode); - //if(!next && xmlNode == this.xmlRoot) return; - - return (next && next != xmlNode) - ? next - : (isTree - ? this.getTraverseParent(xmlNode) - : null); //this.getFirstTraverseNode() - }; - - /** - * Determines whether a node is selected. - * - * @param {XMLElement} xmlNode The {@link term.datanode data node} to be checked - * @return {Boolean} Identifies if the element is selected - */ - this.isSelected = function(xmlNode) { - if (!xmlNode) return false; - - for (var i = 0; i < this.$valueList.length; i++) { - if (this.$valueList[i] == xmlNode) - return this.$valueList.length; - } - - return false; - }; - - /* - * This function checks whether the current selection is still correct. - * Selection can become invalid when updates to the underlying data - * happen. For instance when a selected node is removed. - */ - this.$checkSelection = function(nextNode) { - if (this.$valueList.length > 1) { - //Fix selection if needed - for (var lst = [], i = 0, l = this.$valueList.length; i < l; i++) { - if (apf.isChildOf(this.xmlRoot, this.$valueList[i])) - lst.push(this.$valueList[i]); - } - - if (lst.length > 1) { - this.selectList(lst); - if (this.caret - && !apf.isChildOf(this.xmlRoot, this.caret)) { - this.setCaret(nextNode || this.selected); - } - return; - } - else if (lst.length) { - //this.clearSelection(true); //@todo noEvents here?? - nextNode = lst[0]; - } - } - - if (!nextNode) { - if (this.selected - && !apf.isChildOf(this.xmlRoot, this.selected)) { - nextNode = this.getFirstTraverseNode(); - } - else if (this.selected && this.caret - && !apf.isChildOf(this.xmlRoot, this.caret)) { - this.setCaret(this.selected); - } - else if (!this.selected) { - nextNode = this.xmlRoot - ? this.getFirstTraverseNode() - : null; - } - else { - return; //Nothing to do - } - } - - if (nextNode) { - if (this.autoselect) { - this.select(nextNode); - } - else { - this.clearSelection(); - this.setCaret(nextNode); - } - } - else - this.clearSelection(); - - //if(action == "synchronize" && this.autoselect) this.reselect(); - }; - - /** - * @attribute {Boolean} [multiselect] Sets or gets whether the user may select multiple items. Default is `true, but `false` for dropdown. - */ - /** - * @attribute {Boolean} [autoselect] Sets or gets whether a selection is made after data is loaded. Default is `true`, but `false` for dropdown. When the string 'all' is set, all {@link term.datanode data nodes} are selected. - */ - /** - * @attribute {Boolean} [selectable] Sets or gets whether the {@link term.datanode data nodes} of this element can be selected. Default is `true`. - */ - /** - * @attribute {Boolean} [ctrlselect] Sets or gets whether a selection is made as if the user is holding the [[keys: Ctrl]] key. When set to `true` each mouse selection will add to the current selection. Selecting an already selected element will deselect it. - */ - /** - * @attribute {Boolean} [allowdeselect] Sets or gets whether the user can remove the selection of this element. When set to `true` it is possible for this element to have no selected {@link term.datanode data node}. - */ - /** - * @attribute {Boolean} [reselectable] Sets or gets whether selected nodes can be selected again, and the selection events are called again. Default is `false`. When set to `false` a selected {@link term.datanode data node} cannot be selected again. - */ - /** - * @attribute {String} [default] Sets or gets the value that this component has when no selection is made. - */ - /** - * @attribute {String} [eachvalue] Sets or gets the {@link term.expression expression} that determines the value for each {@link term.datanode data nodes} in the dataset of the element. - * - */ - this.selectable = true; - if (typeof this.ctrlselect == "undefined") - this.ctrlselect = false; - if (typeof this.multiselect == "undefined") - this.multiselect = true; - if (typeof this.autoselect == "undefined") - this.autoselect = true; - if (typeof this.delayedselect == "undefined") - this.delayedselect = true; - if (typeof this.allowdeselect == "undefined") - this.allowdeselect = true; - this.reselectable = false; - - this.$booleanProperties["selectable"] = true; - //this.$booleanProperties["ctrlselect"] = true; - this.$booleanProperties["multiselect"] = true; - this.$booleanProperties["autoselect"] = true; - this.$booleanProperties["delayedselect"] = true; - this.$booleanProperties["allowdeselect"] = true; - this.$booleanProperties["reselectable"] = true; - - this.$supportedProperties.push("selectable", "ctrlselect", "multiselect", - "autoselect", "delayedselect", "allowdeselect", "reselectable", - "selection", "selected", "default", "value", "caret"); - - /** - * @attribute {String} [value] Sets or gets the value of the element that is selected. - * - */ - //@todo add check here - this.$propHandlers["value"] = function(value) { - if (this.$lastValue == value) { - delete this.$lastValue; - return; - } - - if (!this.$attrBindings["eachvalue"] && !this.$amlLoaded - && this.getAttribute("eachvalue")) { - var _self = this; - return apf.queue.add("value" + this.$uniqueId, function(){ - _self.$propHandlers["value"].call(_self, value); - }); - } - - - - if (value || value === 0 || this["default"]) - this.select(String(value) || this["default"]); - else - this.clearSelection(); - } - - this.$propHandlers["default"] = function(value, prop) { - if (!this.value || !this.$amlLoaded && !(this.getAttribute("value") - || this.getAttribute("selected") || this.getAttribute("selection"))) { - this.$propHandlers["value"].call(this, ""); - } - } - - /** - * @attribute {String} [value] Sets or gets the caret value of the element. - */ - //@todo fill this in - this.$propHandlers["caret"] = function(value, prop) { - if (value) - this.setCaret(value); - } - - - - //@todo optimize this thing. Also implement virtual dataset support. - /** - * @attribute {String} [selection] Sets or gets the {@link term.expression expression} that determines the selection for this element. A reference to an XML nodelist can be passed as well. - * - */ - this.$propHandlers["selection"] = - - /** - * @attribute {String} [selected] Sets or gets the {@link term.expression expression} that determines the selected node for this element. A reference to an XML element can be passed as well. - * - */ - this.$propHandlers["selected"] = function(value, prop) { - if (!value) value = this[prop] = null; - - if (prop == "selected" && typeof value != "string") { // && value == this.selected - if (value && value.nodeType != 1) - value = value.nodeValue; - else - //this.selected = null; //I don't remember why this is here. It removes the selected property without setting it again. (dropdown test) - return; - } - - - - if (this.$isSelecting) { - this.selection = this.$valueList; - return false; - } - - var nodes, bindSet, getValue, i, j, c, d; - //Update the selection - if (prop == "selection") { - if (typeof value == "object" && value == this.$valueList) { - var pNode; - //We're using an external model. Need to update bound nodeset - if ((c = this.$attrBindings[prop]) && c.cvalue.models) { //added check, @todo whats up with above assumption? - this.$isSelecting = true; //Prevent reentrance (optimization) - - bindSet = this.$attrBindings["eachvalue"] - && "eachvalue" || this.$bindings["value"] - && "value" || this.$hasBindRule("caption") && "caption"; - - if (!bindSet) - throw new Error("Missing bind rule set: eachvalue, value or caption");//@todo apf3.0 make this into a proper error - - //@todo this may be optimized by keeping a copy of the selection - var selNodes = this.$getDataNode(prop, this.xmlRoot); - nodes = value; - getValue = (d = this.$attrBindings["selection-unique"]) && d.cvalue; - - if (selNodes.length) { - pNode = selNodes[0].parentNode; - } - else { - var model, path; - if (c.cvalue.xpaths[0] == "#" || c.cvalue.xpaths[1] == "#") { - var m = (c.cvalue3 || (c.cvalue3 = apf.lm.compile(c.value, { - xpathmode: 5 - })))(this.xmlRoot); - - model = m.model && m.model.$isModel && m.model; - if (model) - path = m.xpath; - else if (m.model) { - model = apf.xmldb.findModel(m.model); - path = apf.xmlToXpath(m.model, model.data) + (m.xpath ? "/" + m.xpath : ""); //@todo make this better - } - else { - //No selection - nothing to do - } - } - else { - - model = apf.nameserver.get("model", c.cvalue.xpaths[0]); - - path = c.cvalue.xpaths[1]; - } - - if (!model || !model.data) { - this.$isSelecting = false; - return false; - } - - pNode = model.queryNode(path.replace(/\/[^\/]+$|^[^\/]*$/, "") || "."); - - if (!pNode) - throw new Error("Missing parent node"); //@todo apf3.0 make this into a proper error - } - - //Nodes removed - remove_loop: - for (i = 0; i < selNodes.length; i++) { - //Value is either determined by special property or in the - //same way as the value for the bound node. - value = getValue - ? getValue(selNodes[i]) - : this.$applyBindRule(bindSet, selNodes[i]); - - //Compare the value with the traverse nodes - for (j = 0; j < nodes.length; j++) { - if (this.$applyBindRule(bindSet, nodes[j]) == value) //@todo this could be cached - continue remove_loop; - } - - //remove node - apf.xmldb.removeNode(selNodes[i]); - } - - //Nodes added - add_loop: - for (i = 0; i < nodes.length; i++) { - //Value is either determined by special property or in the - //same way as the value for the bound node. - value = this.$applyBindRule(bindSet, nodes[i]); - - //Compare the value with the traverse nodes - for (j = 0; j < selNodes.length; j++) { - if (getValue - ? getValue(selNodes[j]) - : this.$applyBindRule(bindSet, selNodes[j]) == value) //@todo this could be cached - continue add_loop; - } - - //add node - var node = this.$attrBindings["selection-constructor"] - && this.$getDataNode("selection-constructor", nodes[i]) - || apf.getCleanCopy(nodes[i]); - apf.xmldb.appendChild(pNode, node); - } - - //@todo above changes should be via the actiontracker - this.$isSelecting = false; - } - - return; - } - this.selection = this.$valueList; - } - else { - this.selected = null; - } - - if (!this.xmlRoot) { - if (!this.$buffered) { - var f; - this.addEventListener("afterload", f = function(){ - this.removeEventListener("afterload", f); - this.$propHandlers["selected"].call(this, value, prop); - delete this.$buffered; - }); - this.$buffered = true; - } - this[prop] = null; - return false; - } - - if (!value || typeof value != "object") { - //this[prop] = null; - - if (this.$attrBindings[prop]) { - //Execute the selection query - nodes = this.$getDataNode(prop, this.xmlRoot); - if (nodes && (nodes.length || nodes.nodeType == 1)) { - this.setProperty("selection", nodes); - return; - } - - if (!nodes || nodes.length === 0) - return; - - //Current model, it's an init selection, we'll clear the bind - /*if (typeof value == "string" - && !this.$attrBindings[prop].cvalue.xpaths[0]) { - this.$removeAttrBind(prop); - }*/ - } - - if (!value) { - this.clearSelection(); - } - else { - this.select(value); - } - - return false; //Disable signalling the listeners to this property - } - else if (typeof value.length == "number") { - nodes = value; - if (!nodes.length) { - this.selected = null; - if (this.$valueList.length) { //dont clear selection when no selection exists (at prop init) - this.clearSelection(); - return false; //Disable signalling the listeners to this property - } - else return; - } - - //For when nodes are traverse nodes of this element - if (this.isTraverseNode(nodes[0]) - && apf.isChildOf(this.xmlRoot, nodes[0])) { - if (!this.multiselect) { - this.select(nodes[0]); - } - else { - //this[prop] = null; //?? - this.selectList(nodes); - } - return false; //Disable signalling the listeners to this property - } - - //if external model defined, loop through items and find mate by value - if (this.$attrBindings[prop]) { //Can assume an external model is in place - bindSet = this.$attrBindings["eachvalue"] - && "eachvalue" || this.$bindings["value"] - && "value" || this.$hasBindRule("caption") && "caption"; - - if (!bindSet) - throw new Error("Missing bind rule set: eachvalue, value or caption");//@todo apf3.0 make this into a proper error - - var tNodes = !this.each - ? this.getTraverseNodes() - : this.xmlRoot.selectNodes("//" + this.each.split("|").join("|//")); - - getValue = (c = this.$attrBindings["selection-unique"]) && c.cvalue; - var selList = []; - for (i = 0; i < nodes.length; i++) { - //Value is either determined by special property or in the - //same way as the value for the bound node. - value = getValue - ? getValue(nodes[i]) - : this.$applyBindRule(bindSet, nodes[i]); - - //Compare the value with the traverse nodes - for (j = 0; j < tNodes.length; j++) { - if (this.$applyBindRule(bindSet, tNodes[j]) == value) //@todo this could be cached - selList.push(tNodes[j]); - } - } - - //this[prop] = null; //??? - this.selectList(selList, true); //@todo noEvent to distinguish between user actions and not user actions... need to rethink this - return false; - } - - throw new Error("Show me which case this is"); - } - else if (this.$valueList.indexOf(value) == -1) { - //this.selected = null; - this.select(value); - } - }; - - - - this.$propHandlers["allowdeselect"] = function(value) { - if (value) { - var _self = this; - this.$container.onmousedown = function(e) { - if (!e) - e = event; - if (e.ctrlKey || e.shiftKey) - return; - - var srcElement = e.srcElement || e.target; - if (_self.allowdeselect && (srcElement == this - || srcElement.getAttribute(apf.xmldb.htmlIdTag))) - _self.clearSelection(); //hacky - } - } - else { - this.$container.onmousedown = null; - } - }; - - this.$propHandlers["ctrlselect"] = function(value) { - if (value != "enter") - this.ctrlselect = apf.isTrue(value); - } - - function fAutoselect(){ - this.selectAll(); - } - - this.$propHandlers["autoselect"] = function(value) { - if (value == "all" && this.multiselect) - this.addEventListener("afterload", fAutoselect); - else - this.removeEventListener("afterload", fAutoselect); - }; - - this.$propHandlers["multiselect"] = function(value) { - if (!value && this.$valueList.length > 1) - this.select(this.selected); - - //if (value) - //this.bufferselect = false; //@todo doesn't return to original value - }; - - // Select Bind class - - this.addEventListener("beforeselect", function(e) { - if (this.$bindings.selectable && !this.$getDataNode("selectable", e.selected)) - return false; - }, true); - - - - this.addEventListener("afterselect", function (e) { - - var combinedvalue = null; - - - //@todo refactor below - /*if (this.caret == this.selected || e.list && e.list.length > 1 && hasConnections) { - //Multiselect databinding handling... [experimental] - if (e.list && e.list.length > 1 && this.$getConnections().length) { //@todo this no work no more apf3.0 - var oEl = this.xmlRoot.ownerDocument.createElement(this.selected.tagName); - var attr = {}; - - //Fill basic nodes - var nodes = e.list[0].attributes; - for (var j = 0; j < nodes.length; j++) - attr[nodes[j].nodeName] = nodes[j].nodeValue; - - //Remove nodes - for (var prop, i = 1; i < e.list.length; i++) { - for (prop in attr) { - if (typeof attr[prop] != "string") continue; - - if (!e.list[i].getAttributeNode(prop)) - attr[prop] = undefined; - else if (e.list[i].getAttribute(prop) != attr[prop]) - attr[prop] = ""; - } - } - - //Set attributes - for (prop in attr) { - if (typeof attr[prop] != "string") continue; - oEl.setAttribute(prop, attr[prop]); - } - - //missing is childnodes... will implement later when needed... - - oEl.setAttribute(apf.xmldb.xmlIdTag, this.$uniqueId); - apf.MultiSelectServer.register(oEl.getAttribute(apf.xmldb.xmlIdTag), - oEl, e.list, this); - apf.xmldb.addNodeListener(oEl, apf.MultiSelectServer); - - combinedvalue = oEl; - } - }*/ - - - //Set caret property - this.setProperty("caret", e.caret); - - //Set selection length - if (this.sellength != e.selection.length) - this.setProperty("sellength", e.selection.length); - - //Set selection property - delete this.selection; - this.setProperty("selection", e.selection); - if (!e.selection.length) { - //Set selected property - this.setProperty("selected", e.selected); - - //Set value property - if (this.value) - this.setProperty("value", ""); - } - else { - //Set selected property - this.$chained = true; - if (!e.force && (!this.dataParent || !this.dataParent.parent - || !this.dataParent.parent.$chained)) { - var _self = this; - $setTimeout(function(){ - - if (_self.selected == e.selected) { - delete _self.selected; - _self.setProperty("selected", combinedvalue || e.selected); - } - - delete _self.$chained; - }, 10); - } - else { - - this.setProperty("selected", combinedvalue || e.selected); - - delete this.$chained; - } - - //Set value property - var valueRule = this.$attrBindings["eachvalue"] && "eachvalue" - || this.$bindings["value"] && "value" - || this.$hasBindRule("caption") && "caption"; - - if (valueRule) { - //@todo this will call the handler again - should be optimized - - this.$lastValue = this.$applyBindRule(valueRule, e.selected) - //this.$attrBindings["value"] && - if (this.$lastValue != - (valueRule != "value" && (this.xmlRoot - && this.$applyBindRule("value", this.xmlRoot, null, true)) - || this.value)) { - if (valueRule == "eachvalue" || this.xmlRoot != this) - this.change(this.$lastValue); - else - this.setProperty("value", this.$lastValue); - } - /*else { - this.setProperty("value", this.$lastValue); - }*/ - delete this.$lastValue; - } - } - - - - - }, true); - - - - -}).call(apf.MultiSelect.prototype = new apf.MultiselectBinding()); - - - -//@todo refactor below -/* - * @private - */ -/* -apf.MultiSelectServer = { - objects: {}, - - register: function(xmlId, xmlNode, selList, jNode) { - if (!this.$uniqueId) - this.$uniqueId = apf.all.push(this) - 1; - - this.objects[xmlId] = { - xml: xmlNode, - list: selList, - jNode: jNode - }; - }, - - $xmlUpdate: function(action, xmlNode, listenNode, UndoObj) { - if (action != "attribute") return; - - var data = this.objects[xmlNode.getAttribute(apf.xmldb.xmlIdTag)]; - if (!data) return; - - var nodes = xmlNode.attributes; - - for (var j = 0; j < data.list.length; j++) { - //data[j].setAttribute(UndoObj.name, xmlNode.getAttribute(UndoObj.name)); - apf.xmldb.setAttribute(data.list[j], UndoObj.name, - xmlNode.getAttribute(UndoObj.name)); - } - - //apf.xmldb.synchronize(); - } -}; -*/ - - - - - - - - -apf.__CHILDVALUE__ = 1 << 27; - - -apf.ChildValue = function(){ - if (!this.$childProperty) - this.$childProperty = "value"; - - this.$regbase = this.$regbase | apf.__CHILDVALUE__; - - var f, re = /^[\s\S]*?>(<\?lm)?([\s\S]*?)(?:\?>)?<[^>]*?>$/; - this.addEventListener("DOMCharacterDataModified", f = function(e) { - if (e && (e.currentTarget == this - || e.currentTarget.nodeType == 2 && e.relatedNode == this) - || this.$amlDestroyed) - return; - - if (this.getAttribute(this.$childProperty)) - return; - - //Get value from xml (could also serialize children, but that is slower - var m = this.serialize().match(re), - v = m && m[2] || ""; - if (m && m[1]) - v = "{" + v + "}"; - - this.$norecur = true; - - - if (v.indexOf("{") > -1 || v.indexOf("[") > -1) - this.$setDynamicProperty(this.$childProperty, v); - else - - if (this[this.$childProperty] != v) - this.setProperty(this.$childProperty, v); - - this.$norecur = false; - }); - - //@todo Should be buffered - this.addEventListener("DOMAttrModified", f); - this.addEventListener("DOMNodeInserted", f); - this.addEventListener("DOMNodeRemoved", f); - - this.addEventListener("$skinchange", function(e) { - this.$propHandlers[this.$childProperty].call(this, this.caption || ""); - }); - - this.$init(function(){ - this.addEventListener("prop." + this.$childProperty, function(e) { - if (!this.$norecur && !e.value && !this.getAttributeNode(this.$childProperty)) - f.call(this); - }); - }); - - this.addEventListener("DOMNodeInsertedIntoDocument", function(e) { - var hasNoProp = typeof this[this.$childProperty] == "undefined"; - - //this.firstChild.nodeType != 7 && - if (hasNoProp - && !this.getElementsByTagNameNS(this.namespaceURI, "*", true).length - && (this.childNodes.length > 1 || this.firstChild - && (this.firstChild.nodeType == 1 - || this.firstChild.nodeValue.trim().length))) { - //Get value from xml (could also serialize children, but that is slower - var m = (this.$aml && this.$aml.xml || this.serialize()).match(re), - v = m && m[2] || ""; - if (m && m[1]) - v = "{" + v + "}"; - - - if (v.indexOf("{") > -1 || v.indexOf("[") > -1) - this.$setDynamicProperty(this.$childProperty, v); - else - - this.setProperty(this.$childProperty, apf.html_entity_decode(v)); //@todo should be xml entity decode - } - else if (hasNoProp) - this.$propHandlers[this.$childProperty].call(this, ""); - }); -}; - - - - - - - -apf.__DATAACTION__ = 1 << 25; - - -/** - * A [[term.baseclass baseclass]] that adds data action features to this element. - * @class apf.DataAction - */ -apf.DataAction = function(){ - this.$regbase = this.$regbase | apf.__DATAACTION__; - - // *** Public Methods *** // - - /** - * Gets the ActionTracker this element communicates with. - * - * @return {apf.actiontracker} - * @see apf.smartbinding - */ - this.getActionTracker = function(ignoreMe) { - if (!apf.AmlNode) - return apf.window.$at; - - var pNode = this, tracker = ignoreMe ? null : this.$at; - if (!tracker && this.dataParent && this.dataParent.parent) - tracker = this.dataParent.parent.$at; //@todo apf3.0 change this to be recursive?? - - while (!tracker) { - if (!pNode.parentNode && !pNode.$parentNode) { - var model; - return (model = this.getModel && this.getModel(true)) && model.$at || apf.window.$at; - } - - tracker = (pNode = pNode.parentNode || pNode.$parentNode).$at; - } - return tracker; - }; - - - - this.$actionsLog = {}; - this.$actions = false; - - /** - * @event locksuccess Fires when a lock request succeeds - * @bubbles - * @param {Object} e The standard event object, with the following properties: - * - state ([[Number]]): The return code of the lock request - * - */ - /** - * @event lockfailed Fires when a lock request failes - * @bubbles - * @param {Object} e The standard event object, with the following properties: - * - state ([[Number]]): The return code of the lock request - * - */ - /** - * @event unlocksuccess Fires when an unlock request succeeds - * @bubbles - * @param {Object} e The standard event object, with the following properties: - * - state ([[Number]]): The return code of the unlock request - * - */ - /** - * @event unlockfailed Fires when an unlock request fails - * @bubbles - * @param {Object} e The standard event object, with the following properties: - * - state ([[Number]]): The return code of the unlock request - * - */ - /* - * Starts the specified action, does optional locking and can be offline aware - * - or for optimistic locking it will record the timestamp (a setting - * ) - * - During offline work, optimistic locks will be handled by taking the - * timestamp of going offline - * - This method is always optional! The server should not expect locking to exist. - * - */ - this.$startAction = function(name, xmlContext, fRollback) { - if (this.disabled || this.liveedit && name != "edit") - return false; - - var actionRule = this.$actions && this.$actions.getRule(name, xmlContext); - if (!actionRule && apf.config.autoDisableActions && this.$actions) { - - - return false; - } - - var bHasOffline = typeof apf.offline != "undefined"; - - - if (this.dispatchEvent(name + "start", { - xmlContext: xmlContext - }) === false) - return false; - - - - this.$actionsLog[name] = xmlContext; - - return true; - }; - - - // @todo think about if this is only for rdb - this.addEventListener("xmlupdate", function(e) { - if (apf.xmldb.disableRDB != 2) - return; - - for (var name in this.$actionsLog) { - if (apf.isChildOf(this.$actionsLog[name], e.xmlNode, true)) { - //this.$stopAction(name, true); - this.$actionsLog[name].rollback.call(this, this.$actionsLog[name].xmlContext); - } - } - }); - - - this.$stopAction = function(name, isCancelled, curLock) { - delete this.$actionsLog[name]; - - - }; - - /* - * Executes an action using action rules set in the {@link apf.actions actions element}. - * - * @param {String} atAction The name of the action to be performed by the [[ActionTracker]]. Possible values include: - * - `"setTextNode"`: Sets the first text node of an XML element. For more information, see {@link core.xmldb.method.setTextNode} - * - `"setAttribute"`: Sets the attribute of an XML element. For more information, see {@link core.xmldb.method.setAttribute} - * - `"removeAttribute"`: Removes an attribute from an XML element. For more information, see {@link core.xmldb.method.removeAttribute} - * - `"setAttributes"`: Sets multiple attribute on an XML element. The arguments are in the form of `xmlNode, Array` - * - `"replaceNode"`: Replaces an XML child with another one. For more information, see {@link core.xmldb.method.replaceNode} - * - `"addChildNode"`: Adds a new XML node to a parent node. For more information, see {@link core.xmldb.method.addChildNode} - * - `"appendChild"`: Appends an XML node to a parent node. For more information, see {@link core.xmldb.method.appendChild} - * - `"moveNode"` : Moves an XML node from one parent to another. For more information, see {@link core.xmldb.method.moveNode} - * - `"removeNode"`: Removes a node from it's parent. For more information, see {@link core.xmldb.method.removeNode} - * - `" removeNodeList"`: Removes multiple nodes from their parent. For more information, see {@link core.xmldb.method.removeNodeList} - * - `"setValueByXpath"`: Sets the nodeValue of an XML node which is selected - * by an xpath statement. The arguments are in the form of `xmlNode, xpath, value` - * - `"multicall"`: Calls multiple of the above actions. The argument`s are an array - * of argument arrays for these actions each with a func` - * property, which is the name of the action. - * @param {Array} args the arguments to the function specified - * in atAction. - * @param {String} action the name of the action rule defined in - * actions for this element. - * @param {XMLElement} xmlNode the context for the action rules. - * @param {Boolean} [noevent] whether or not to call events. - * @param {XMLElement} [contextNode] the context node for action processing - * (such as RPC calls). Usually the same - * as xmlNode - * @return {Boolean} specifies success or failure - * @see apf.smartbinding - */ - this.$executeAction = function(atAction, args, action, xmlNode, noevent, contextNode, multiple) { - - - - - //Get Rules from Array - var rule = this.$actions && this.$actions.getRule(action, xmlNode); - if (!rule && this.$actions && apf.config.autoDisableActions - && "action|change".indexOf(action) == -1) { - apf.console.warn("Could not execute action '" + action + "'. \ - No valid action rule was found and auto-disable-actions is enabled"); - - return false; - } - - - - var newMultiple; - if (multiple) { - newMultiple = []; - for (var k = multiple.length - 1; k >= 0; k--) { - newMultiple.unshift({ - xmlActionNode: rule, // && rule[4], - amlNode: this, - selNode: multiple[k], - xmlNode: multiple[k] - }) - } - } - - //@todo apf3.0 Shouldn't the contextNode be made by the match - var ev = new apf.AmlEvent("before" + action.toLowerCase(), { - action: atAction, - args: args, - xmlActionNode: rule, - amlNode: this, - selNode: contextNode, - multiple: newMultiple || false - - }); - - //Call Event and cancel if it returns false - if (!noevent) { - //Allow the action and arguments to be changed by the event - if (this.dispatchEvent(ev.name, null, ev) === false) - return false; - - delete ev.currentTarget; - } - - //Call ActionTracker and return ID of Action in Tracker - var at = this.getActionTracker(); - if (!at)// This only happens at destruction of apf - return UndoObj; - - var UndoObj = at.execute(ev); - ev.xmlNode = UndoObj.xmlNode; - ev.undoObj = UndoObj; - - //Call After Event - if (!noevent) { //@todo noevent is not implemented for before.. ??? - ev.name = "after" + action.toLowerCase(); - ev.cancelBubble = false; - delete ev.returnValue; - delete ev.currentTarget; - this.dispatchEvent(ev.name, null, ev); - } - - return UndoObj; - }; - - /* - * Executes an action based on the set name and the new value - * @param {String} atName the name of the action rule defined in actions for this element. - * @param {String} setName the name of the binding rule defined in bindings for this element. - * @param {XMLElement} xmlNode the xml element to which the rules are applied - * @param {String} value the new value of the node - */ - this.$executeSingleValue = function(atName, setName, xmlNode, value, getArgList) { - var xpath, args, rule = this.$getBindRule(setName, xmlNode); - - //recompile bindrule to create nodes - if (!rule) { - - return false; - } - - var compiled; - ["valuematch", "match", "value"].each(function(type) { - if (!rule[type] || compiled) - return; - - compiled = rule["c" + type]; //cvaluematch || (rule.value ? rule.cvalue : rule.cmatch); - if (!compiled) - compiled = rule.compile(type); - - if (compiled.type != 3) - compiled = null; - }); - - - - var atAction, model, node, - sel = compiled.xpaths, //get first xpath - shouldLoad = false; - - if (sel[0] == "#" || sel[1] == "#") { - var m = (rule.cvalue3 || (rule.cvalue3 = apf.lm.compile(rule.value, { - xpathmode: 5 - })))(xmlNode, apf.nameserver.lookup["all"]); - - model = m.model && m.model.$isModel && m.model; - if (model) { - node = model.queryNode(m.xpath); - xmlNode = model.data; - } - else if (m.model) { - model = apf.xmldb.findModel(m.model); - node = m.model.selectSingleNode(m.xpath); - xmlNode = m.model; - } - else { - - } - - sel[1] = m.xpath; - } - else { - - model = sel[0] && apf.nameserver.get("model", sel[0]) || this.$model, - node = model - ? model.queryNode(sel[1]) - : (xmlNode || this.xmlRoot).selectSingleNode(sel[1]); - if (model && !xmlNode) - xmlNode = model.data; //@experimental, after changing this, please run test/test_rename_edge.html - - } - - if (node) { - if (apf.queryValue(node) == value) return; // Do nothing if value is unchanged - - atAction = (node.nodeType == 1 || node.nodeType == 3 - || node.nodeType == 4) ? "setTextNode" : "setAttribute"; - args = (node.nodeType == 1) - ? [node, value] - : (node.nodeType == 3 || node.nodeType == 4 - ? [node.parentNode, value] - : [node.ownerElement || node.selectSingleNode(".."), node.nodeName, value]); - } - else { - atAction = "setValueByXpath"; - xpath = sel[1]; - - if (!this.$createModel || this.getModel() && !this.getModel().$createModel) { - throw new Error("Model data does not exist, and I am not " - + "allowed to create the element for xpath '" - + xpath + "' and element " + this.serialize(true)); - } - - if (!xmlNode) { - //Assuming this component is connnected to a model - if (!model) - model = this.getModel(); - if (model) { - if (!model.data) - model.load(""); - - xpath = (model.getXpathByAmlNode(this) || ".") - + (xpath && xpath != "." ? "/" + xpath : ""); - xmlNode = model.data; - } - else { - if (!this.dataParent) - return false; - - xmlNode = this.dataParent.parent.selected || this.dataParent.parent.xmlRoot; - if (!xmlNode) - return false; - - xpath = (this.dataParent.xpath || ".") - + (xpath && xpath != "." ? "/" + xpath : ""); - shouldLoad = true; - } - } - - args = [xmlNode, value, xpath]; - } - - if (getArgList) { - return { - action: atAction, - args: args - }; - } - - //Use Action Tracker - var result = this.$executeAction(atAction, args, atName, xmlNode); - - if (shouldLoad) - this.load(xmlNode.selectSingleNode(xpath)); - - return result; - }; - - /** - * Changes the value of this element. - * @action - * @param {String} [string] The new value of this element. - * - */ - this.change = function(value, force){ // @todo apf3.0 maybe not for multiselect?? - why is clearError handling not in setProperty for value - - if (this.errBox && this.errBox.visible && this.isValid && this.isValid()) - this.clearError(); - - //Not databound - if (!this.xmlRoot && !this.$createModel || !(this.$mainBind == "value" - && this.hasFeature(apf.__MULTISELECT__) - ? this.$attrBindings["value"] - : this.$hasBindRule(this.$mainBind))) { - - if (!force && value === this.value - || this.dispatchEvent("beforechange", {value : value}) === false) - return false; - - //@todo in theory one could support actions - //@todo disabled below, because it gives unexpected behaviour when - //form elements are used for layout and other UI alterations - /*this.getActionTracker().execute({ - action: "setProperty", - args: [this, "value", value, false, true], - amlNode: this - });*/ - this.setProperty("value", value); - - return this.dispatchEvent("afterchange", {value : value}); - - } - - var valueRule = this.$attrBindings["eachvalue"] && "eachvalue" - || this.$bindings["value"] && "value" - || this.$hasBindRule("caption") && "caption"; - - if (value === (valueRule != "value" && (this.xmlRoot - && this.$applyBindRule("value", this.xmlRoot, null, true)) - || this.value)) - return false; - - this.$executeSingleValue("change", this.$mainBind, this.xmlRoot, value); - - }; - - this.$booleanProperties["render-root"] = true; - this.$supportedProperties.push("create-model", "actions"); - - /** - * @attribute {Boolean} create-model Sets or gets whether the model this element connects - * to is extended when the data pointed to does not exist. Defaults to true. - * - * #### Example - * - * In this example, a model is extended when the user enters information in - * the form elements. Because no model is specified for the form elements, - * the first available model is chosen. At the start, it doesn't have any - * data; this changes when (for instance) the name is filled in. A root node - * is created, and under that a 'name' element with a textnode containing - * the entered text. - * - * ```xml - * - * Name - * - * - * Address - * - * - * Country - * - * Submit - * - * ``` - */ - this.$propHandlers["create-model"] = function(value) { - this.$createModel = value; - }; - - this.addEventListener("DOMNodeInsertedIntoDocument", function(e) { - if (typeof this["create-model"] == "undefined" - && !this.$setInheritedAttribute("create-model")) { - this.$createModel = true; - } - }); -}; - -apf.config.$inheritProperties["create-model"] = 1; - - - - - - - -apf.__CACHE__ = 1 << 2; - - - -/** - * All elements inheriting from this {@link term.baseclass baseclass} have caching features. It takes care of - * storing, retrieving, and updating rendered data (in HTML form) - * to overcome the waiting time while rendering the contents every time the - * data is loaded. - * - * @class apf.Cache - * @baseclass - * @author Ruben Daniels (ruben AT ajax DOT org) - * @version %I%, %G% - * @since 0.4 - */ -apf.Cache = function(){ - /* ******************************************************************** - PROPERTIES - *********************************************************************/ - this.cache = {}; - this.$subTreeCacheContext = null; - - this.caching = true; - this.$regbase = this.$regbase | apf.__CACHE__; - - /* ******************************************************************** - PUBLIC METHODS - *********************************************************************/ - - this.addEventListener("$load", function(e) { - if (!this.caching || e.forceNoCache) - return; - - // retrieve the cacheId - if (!this.cacheId) { - this.cacheId = this.$generateCacheId && this.$generateCacheId(e.xmlNode) - || e.xmlNode.getAttribute(apf.xmldb.xmlIdTag) - || apf.xmldb.nodeConnect(apf.xmldb.getXmlDocId(e.xmlNode), e.xmlNode);//e.xmlNode - } - - // Retrieve cached version of document if available - var fromCache = getCache.call(this, this.cacheId, e.xmlNode); - if (fromCache) { - if (fromCache == -1 || !this.getTraverseNodes) - return (e.returnValue = false); - - var nodes = this.getTraverseNodes(); - - //Information needs to be passed to the followers... even when cached... - if (nodes.length) { - if (this["default"]) - this.select(this["default"]); - else if (this.autoselect) - this.select(nodes[0], null, null, null, true); - } - else if (this.clearSelection) - this.clearSelection(); //@todo apf3.0 was setProperty("selected", null - - if (!nodes.length) { - // Remove message notifying user the control is without data - this.$removeClearMessage(); - this.$setClearMessage(this["empty-message"], "empty"); - } - - - //@todo move this to getCache?? - if (nodes.length != this.length) - this.setProperty("length", nodes.length); - - - return false; - } - }); - - this.addEventListener("$clear", function(){ - if (!this.caching) - return; - - /* - Check if we borrowed an HTMLElement - We should return it where it came from - - note: There is a potential that we can't find the exact location - to put it back. We should then look at it's position in the xml. - (but since I'm lazy it's not doing this right now) - There might also be problems when removing the xmlroot - */ - if (this.hasFeature(apf.__MULTISELECT__) - && this.$subTreeCacheContext && this.$subTreeCacheContext.oHtml) { - if (this.renderRoot) { - this.$subTreeCacheContext.parentNode.insertBefore( - this.$subTreeCacheContext.oHtml, this.$subTreeCacheContext.beforeNode); - } - else { - var container = this.$subTreeCacheContext.container || this.$container; - while (container.childNodes.length) - this.$subTreeCacheContext.oHtml.appendChild(container.childNodes[0]); - } - - this.documentId = this.xmlRoot = this.cacheId = this.$subTreeCacheContext = null; - } - else { - /* If the current item was loaded whilst offline, we won't cache - * anything - */ - if (this.$loadedWhenOffline) { - this.$loadedWhenOffline = false; - } - else { - // Here we cache the current part - var fragment = this.$getCurrentFragment(); - if (!fragment) return;//this.$setClearMessage(this["empty-message"]); - - fragment.documentId = this.documentId; - fragment.xmlRoot = this.xmlRoot; - - if (this.cacheId || this.xmlRoot) - setCache.call(this, this.cacheId || - this.xmlRoot.getAttribute(apf.xmldb.xmlIdTag) || "doc" - + this.xmlRoot.getAttribute(apf.xmldb.xmlDocTag), fragment); - } - } - }); - - /* - * Checks the cache for a cached item by ID. If the ID is found, the - * representation is loaded from cache and set active. - * - * @param {String} id The id of the cache element which is looked up. - * @param {Object} xmlNode - * @return {Boolean} If `true`, the cache element was found and set active - * @see baseclass.databinding.method.load - * @private - */ - function getCache(id, xmlNode) { - /* - Let's check if the requested source is actually - a sub tree of an already rendered part - */ - - if (xmlNode && this.hasFeature(apf.__MULTISELECT__) && this.$isTreeArch) { - var cacheItem, - htmlId = xmlNode.getAttribute(apf.xmldb.xmlIdTag) + "|" + this.$uniqueId, - node = this.$pHtmlDoc.getElementById(htmlId); - if (node) - cacheItem = id ? false : this.$container; //@todo what is the purpose of this statement? - else { - for (var prop in this.cache) { - if (this.cache[prop] && this.cache[prop].nodeType) { - node = this.cache[prop].getElementById(htmlId); - if (node) { - cacheItem = id ? prop : this.cache[prop]; //@todo what is the purpose of this statement? - break; - } - } - } - } - - if (cacheItem && !this.cache[id]) { - /* - Ok so it is, let's borrow it for a while - We can't clone it, because the updates will - get ambiguous, so we have to put it back later - */ - var oHtml = this.$findHtmlNode( - xmlNode.getAttribute(apf.xmldb.xmlIdTag) + "|" + this.$uniqueId); - this.$subTreeCacheContext = { - oHtml: oHtml, - parentNode: oHtml.parentNode, - beforeNode: oHtml.nextSibling, - cacheItem: cacheItem - }; - - this.documentId = apf.xmldb.getXmlDocId(xmlNode); - this.cacheId = id; - this.xmlRoot = xmlNode; - - //Load html - if (this.renderRoot) - this.$container.appendChild(oHtml); - else { - while (oHtml.childNodes.length) - this.$container.appendChild(oHtml.childNodes[0]); - } - - return true; - } - } - - - //Checking Cache... - if (!this.cache[id]) return false; - - //Get Fragment and clear Cache Item - var fragment = this.cache[id]; - - this.documentId = fragment.documentId; - this.cacheId = id; - this.xmlRoot = xmlNode;//fragment.xmlRoot; - - - this.setProperty("root", this.xmlRoot); - - - this.clearCacheItem(id); - - this.$setCurrentFragment(fragment); - - return true; - }; - - /* - * Sets cache element and its ID. - * - * @param {String} id The id of the cache element to be stored. - * @param {DocumentFragment} fragment The data to be stored. - * @private - */ - function setCache(id, fragment) { - if (!this.caching) return; - - this.cache[id] = fragment; - }; - - /* - * Finds HTML presentation node in cache by ID. - * - * @param {String} id The id of the HTMLElement which is looked up. - * @return {HTMLElement} The HTMLElement found. When no element is found, `null` is returned. - */ - this.$findHtmlNode = function(id) { - var node = this.$pHtmlDoc.getElementById(id); - if (node) return node; - - for (var prop in this.cache) { - if (this.cache[prop] && this.cache[prop].nodeType) { - node = this.cache[prop].getElementById(id); - if (node) return node; - } - } - - return null; - }; - - /** - * Removes an item from the cache. - * - * @param {String} id The id of the HTMLElement which is looked up. - * @param {Boolean} [remove] Specifies whether to destroy the Fragment. - * @see baseclass.databinding.method.clear - * @private - */ - this.clearCacheItem = function(id, remove) { - this.cache[id].documentId = - this.cache[id].cacheId = - this.cache[id].xmlRoot = null; - - if (remove) - apf.destroyHtmlNode(this.cache[id]); - - this.cache[id] = null; - }; - - /* - * Removes all items from the cache - * - * @see baseclass.databinding.method.clearCacheItem - * @private - */ - this.clearAllCache = function(){ - for (var prop in this.cache) { - if (this.cache[prop]) - this.clearCacheItem(prop, true); - } - }; - - /** - * Gets the cache item by its id - * - * @param {String} id The id of the HTMLElement which is looked up. - * @see baseclass.databinding.method.clearCacheItem - * @private - */ - this.getCacheItem = function(id) { - return this.cache[id]; - }; - - /* - * Checks whether a cache item exists by the specified id - * - * @param {String} id the id of the cache item to check. - * @see baseclass.databinding.method.clearCacheItem - * @private - */ - this.$isCached = function(id) { - return this.cache[id] || this.cacheId == id ? true : false; - }; - - if (!this.$getCurrentFragment) { - this.$getCurrentFragment = function(){ - var fragment = this.$container.ownerDocument.createDocumentFragment(); - - while (this.$container.childNodes.length) { - fragment.appendChild(this.$container.childNodes[0]); - } - - return fragment; - }; - - this.$setCurrentFragment = function(fragment) { - this.$container.appendChild(fragment); - - if (!apf.window.hasFocus(this) && this.blur) - this.blur(); - }; - } - - /** - * @attribute {Boolean} caching Sets or gets whether caching is enabled for this element. - */ - this.$booleanProperties["caching"] = true; - this.$supportedProperties.push("caching"); - - this.addEventListener("DOMNodeRemovedFromDocument", function(e) { - //Remove all cached Items - this.clearAllCache(); - }); -}; - -apf.GuiElement.propHandlers["caching"] = function(value) { - if (!apf.isTrue(value)) return; - - if (!this.hasFeature(apf.__CACHE__)) - this.implement(apf.Cache); -}; - - - - - - - - -apf.__RENAME__ = 0; - - - - - - - -/** - * The baseclass of elements that allows the user to select one or more items - * out of a list. - * - * @class apf.BaseList - * @baseclass - * - * @inherits apf.MultiSelect - * @inherits apf.Cache - * @inherits apf.DataAction - * @inheritsElsewhere apf.XForms - * - * @author Ruben Daniels (ruben AT ajax DOT org) - * @version %I%, %G% - * @since 0.8 - * @default_private - * - */ -/** - * @binding caption Determines the caption of a node. - */ -/** - * @binding icon Determines the icon of a node. -* - * This binding rule is used - * to determine the icon displayed when using a list skin. The {@link baseclass.baselist.binding.image image binding} - * is used to determine the image in the thumbnail skin. - */ -/** - * @binding image Determines the image of a node. - * - * This binding rule is used - * to determine the image displayed when using a thumbnail skin. The {@link baseclass.baselist.binding.icon icon binding} - * is used to determine the icon in the list skin. - * - * #### Example - * - * In this example, the image URL is read from the thumbnail attribute of the data node. - * - * ```xml - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * ``` - * - */ -/** - * @binding css Determines a CSS class for a node. - * - * #### Example - * - * In this example a node is bold when the folder contains unread messages: - * - * ```xml - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * ``` - * - */ -/** - * @binding tooltip Determines the tooltip of a node. - */ -/** - * @event notunique Fires when the `more` attribute is set and an item is added that has a caption that already exists in the list. - * @param {Object} e The standard event object, with the following properties: - * - value ([[String]]): The value that was entered - */ -apf.BaseList = function(){ - this.$init(true); - - - this.$dynCssClasses = []; - - - this.listNodes = []; -}; - -(function() { - - this.implement( - - apf.Cache, - - - apf.DataAction, - - - apf.K - ); - - - // *** Properties and Attributes *** // - - this.$focussable = true; // This object can get the focus - this.$isWindowContainer = -1; - - this.multiselect = true; // Initially Disable MultiSelect - - /** - * @attribute {String} fill Sets or gets the set of items that should be loaded into this - * element. Items are seperated by a comma (`,`). Ranges are specified by a start and end value seperated by a dash (`-`). - * - * #### Example - * - * This example loads a list with items starting at 1980 and ending at 2050. It also loads several other items and ranges. - * - * ```xml - * - * - * - * - * - - * ``` - */ - this.$propHandlers["fill"] = function(value) { - if (value) - this.loadFillData(this.getAttribute("fill")); - else - this.clear(); - }; - - - - /** - * @attribute {String} mode Sets or gets the way this element interacts with the user. - * - * The following values are possible: - * - * - `check`: the user can select a single item from this element. The selected item is indicated. - * - `radio`: the user can select multiple items from this element. Each selected item is indicated. - */ - this.$mode = 0; - this.$propHandlers["mode"] = function(value) { - if ("check|radio".indexOf(value) > -1) { - if (!this.hasFeature(apf.__MULTICHECK__)) - this.implement(apf.MultiCheck); - - this.addEventListener("afterrename", $afterRenameMode); //what does this do? - - this.multicheck = value == "check"; //radio is single - this.$mode = this.multicheck ? 1 : 2; - } - else { - //@todo undo actionRules setting - this.removeEventListener("afterrename", $afterRenameMode); - //@todo unimplement?? - this.$mode = 0; - } - }; - - //@todo apf3.0 retest this completely - function $afterRenameMode(){ - } - - - - // *** Keyboard support *** // - - - - //Handler for a plane list - this.$keyHandler = function(e) { - var key = e.keyCode, - ctrlKey = e.ctrlKey, - shiftKey = e.shiftKey, - selHtml = this.$caret || this.$selected; - - if (e.returnValue == -1 || !selHtml || this.renaming) //@todo how about allowdeselect? - return; - - var selXml = this.caret || this.selected, - oExt = this.$ext, - // variables used in the switch statement below: - node, margin, items, lines, hasScroll, hasScrollX, hasScrollY; - - switch (key) { - case 13: - if (this.$tempsel) - this.$selectTemp(); - - if (this.ctrlselect == "enter") - this.select(this.caret, true); - - this.choose(this.selected); - break; - case 32: - if (ctrlKey || !this.isSelected(this.caret)) - this.select(this.caret, ctrlKey); - break; - case 109: - case 46: - //DELETE - if (this.disableremove) - return; - - if (this.$tempsel) - this.$selectTemp(); - - this.remove(); - break; - case 36: - //HOME - var node = this.getFirstTraverseNode(); - - - if (this.hasFeature(apf.__VIRTUALVIEWPORT__)) - return this.$viewport.scrollIntoView(node); - - - this.select(node, false, shiftKey); - this.$container.scrollTop = 0; - break; - case 35: - //END - var node = this.getLastTraverseNode(); - - - if (this.hasFeature(apf.__VIRTUALVIEWPORT__)) - return this.$viewport.scrollIntoView(node, true); - - - this.select(node, false, shiftKey); - this.$container.scrollTop = this.$container.scrollHeight; - break; - case 107: - //+ - if (this.more) - this.startMore(); - break; - case 37: - //LEFT - if (!selXml && !this.$tempsel) - return; - - node = this.$tempsel - ? apf.xmldb.getNode(this.$tempsel) - : selXml; - margin = apf.getBox(apf.getStyle(selHtml, "margin")); - items = selHtml.offsetWidth - ? Math.floor((oExt.offsetWidth - - (hasScroll ? 15 : 0)) / (selHtml.offsetWidth - + margin[1] + margin[3])) - : 1; - - //margin = apf.getBox(apf.getStyle(selHtml, "margin")); - - node = this.getNextTraverseSelected(node, false); - if (node) - this.$setTempSelected(node, ctrlKey, shiftKey, true); - else - return; - - selHtml = apf.xmldb.findHtmlNode(node, this); - if (selHtml.offsetTop < oExt.scrollTop) { - oExt.scrollTop = Array.prototype.indexOf.call(this.getTraverseNodes(), node) < items - ? 0 - : selHtml.offsetTop - margin[0]; - } - break; - case 38: - //UP - if (!selXml && !this.$tempsel) - return; - - node = this.$tempsel - ? apf.xmldb.getNode(this.$tempsel) - : selXml; - - margin = apf.getBox(apf.getStyle(selHtml, "margin")); - hasScroll = oExt.scrollHeight > oExt.offsetHeight; - items = selHtml.offsetWidth - ? Math.floor((oExt.offsetWidth - - (hasScroll ? 15 : 0)) / (selHtml.offsetWidth - + margin[1] + margin[3])) - : 1; - - node = this.getNextTraverseSelected(node, false, items); - if (node) - this.$setTempSelected (node, ctrlKey, shiftKey, true); - else - return; - - - if (this.hasFeature(apf.__VIRTUALVIEWPORT__)) - return this.$viewport.scrollIntoView(node); - - - selHtml = apf.xmldb.findHtmlNode(node, this); - if (selHtml.offsetTop < oExt.scrollTop) { - oExt.scrollTop = Array.prototype.indexOf.call(this.getTraverseNodes(), node) < items - ? 0 - : selHtml.offsetTop - margin[0]; - } - break; - case 39: - //RIGHT - if (!selXml && !this.$tempsel) - return; - - node = this.$tempsel - ? apf.xmldb.getNode(this.$tempsel) - : selXml; - margin = apf.getBox(apf.getStyle(selHtml, "margin")); - node = this.getNextTraverseSelected(node, true); - if (node) - this.$setTempSelected (node, ctrlKey, shiftKey); - else - return; - - selHtml = apf.xmldb.findHtmlNode(node, this); - if (selHtml.offsetTop + selHtml.offsetHeight - > oExt.scrollTop + oExt.offsetHeight) { - oExt.scrollTop = selHtml.offsetTop - - oExt.offsetHeight + selHtml.offsetHeight - + margin[0]; - } - break; - case 40: - //DOWN - if (!selXml && !this.$tempsel) - return; - - node = this.$tempsel - ? apf.xmldb.getNode(this.$tempsel) - : selXml; - - margin = apf.getBox(apf.getStyle(selHtml, "margin")); - hasScroll = oExt.scrollHeight > oExt.offsetHeight; - items = selHtml.offsetWidth - ? Math.floor((oExt.offsetWidth - - (hasScroll ? 15 : 0)) / (selHtml.offsetWidth - + margin[1] + margin[3])) - : 1; - - node = this.getNextTraverseSelected(node, true, items); - if (node) - this.$setTempSelected (node, ctrlKey, shiftKey); - else - return; - - - if (this.hasFeature(apf.__VIRTUALVIEWPORT__)) - return this.$viewport.scrollIntoView(node, true); - - - selHtml = apf.xmldb.findHtmlNode(node, this); - if (selHtml.offsetTop + selHtml.offsetHeight - > oExt.scrollTop + oExt.offsetHeight) { // - (hasScroll ? 10 : 0) - oExt.scrollTop = selHtml.offsetTop - - oExt.offsetHeight + selHtml.offsetHeight - + margin[0]; //+ (hasScroll ? 10 : 0) - } - break; - case 33: - //PGUP - if (!selXml && !this.$tempsel) - return; - - node = this.$tempsel - ? apf.xmldb.getNode(this.$tempsel) - : selXml; - - margin = apf.getBox(apf.getStyle(selHtml, "margin")); - hasScrollY = oExt.scrollHeight > oExt.offsetHeight; - hasScrollX = oExt.scrollWidth > oExt.offsetWidth; - items = Math.floor((oExt.offsetWidth - - (hasScrollY ? 15 : 0)) / (selHtml.offsetWidth - + margin[1] + margin[3])); - lines = Math.floor((oExt.offsetHeight - - (hasScrollX ? 15 : 0)) / (selHtml.offsetHeight - + margin[0] + margin[2])); - - node = this.getNextTraverseSelected(node, false, items * lines); - if (!node) - node = this.getFirstTraverseNode(); - if (node) - this.$setTempSelected (node, ctrlKey, shiftKey, true); - else - return; - - - if (this.hasFeature(apf.__VIRTUALVIEWPORT__)) - return this.$viewport.scrollIntoView(node); - - - selHtml = apf.xmldb.findHtmlNode(node, this); - if (selHtml.offsetTop < oExt.scrollTop) { - oExt.scrollTop = Array.prototype.indexOf.call(this.getTraverseNodes(), node) < items - ? 0 - : selHtml.offsetTop - margin[0]; - } - break; - case 34: - //PGDN - if (!selXml && !this.$tempsel) - return; - - node = this.$tempsel - ? apf.xmldb.getNode(this.$tempsel) - : selXml; - - margin = apf.getBox(apf.getStyle(selHtml, "margin")); - hasScrollY = oExt.scrollHeight > oExt.offsetHeight; - hasScrollX = oExt.scrollWidth > oExt.offsetWidth; - items = Math.floor((oExt.offsetWidth - (hasScrollY ? 15 : 0)) - / (selHtml.offsetWidth + margin[1] + margin[3])); - lines = Math.floor((oExt.offsetHeight - (hasScrollX ? 15 : 0)) - / (selHtml.offsetHeight + margin[0] + margin[2])); - - node = this.getNextTraverseSelected(selXml, true, items * lines); - if (!node) - node = this.getLastTraverseNode(); - if (node) - this.$setTempSelected (node, ctrlKey, shiftKey); - else - return; - - - if (this.hasFeature(apf.__VIRTUALVIEWPORT__)) - return this.$viewport.scrollIntoView(node, true); - - - selHtml = apf.xmldb.findHtmlNode(node, this); - if (selHtml.offsetTop + selHtml.offsetHeight - > oExt.scrollTop + oExt.offsetHeight) { // - (hasScrollY ? 10 : 0) - oExt.scrollTop = selHtml.offsetTop - - oExt.offsetHeight + selHtml.offsetHeight - + margin[0]; //+ 10 + (hasScrollY ? 10 : 0) - } - break; - - default: - if (key == 65 && ctrlKey) { - this.selectAll(); - } - else if (this.$hasBindRule("caption")) { - if (!this.xmlRoot || this.autorename) return; - - //this should move to a onkeypress based function - if (!this.lookup || new Date().getTime() - - this.lookup.date.getTime() > 300) { - this.lookup = { - str: "", - date: new Date() - }; - } - - this.lookup.str += String.fromCharCode(key); - - var nodes = this.getTraverseNodes(); //@todo start at current indicator - for (var v, i = 0; i < nodes.length; i++) { - v = this.$applyBindRule("caption", nodes[i]); - if (v && v.substr(0, this.lookup.str.length) - .toUpperCase() == this.lookup.str) { - - if (!this.isSelected(nodes[i])) { - this.select(nodes[i]); - } - - if (selHtml) { - this.$container.scrollTop = selHtml.offsetTop - - (this.$container.offsetHeight - - selHtml.offsetHeight) / 2; - } - return; - } - } - return; - } - break; - } - - this.lookup = null; - return false; - }; - - - - // *** Private databinding functions *** // - - this.$deInitNode = function(xmlNode, htmlNode) { - if (!htmlNode) return; - - //Remove htmlNodes from tree - htmlNode.parentNode.removeChild(htmlNode); - }; - - this.$updateNode = function(xmlNode, htmlNode, noModifier) { - //Update Identity (Look) - var elIcon = this.$getLayoutNode("item", "icon", htmlNode); - - if (elIcon) { - if (elIcon.nodeType == 1) { - elIcon.style.backgroundImage = "url(" + - apf.getAbsolutePath(this.iconPath, - this.$applyBindRule("icon", xmlNode)) + ")"; - } - else { - elIcon.nodeValue = apf.getAbsolutePath(this.iconPath, - this.$applyBindRule("icon", xmlNode)); - } - } - else { - //.style.backgroundImage = "url(" + this.$applyBindRule("image", xmlNode) + ")"; - var elImage = this.$getLayoutNode("item", "image", htmlNode); - if (elImage) { - if (elImage.nodeType == 1) { - elImage.style.backgroundImage = "url(" + - apf.getAbsolutePath(apf.hostPath, - this.$applyBindRule("image", xmlNode)) + ")"; - } - else { - elImage.nodeValue = apf.getAbsolutePath(apf.hostPath, - this.$applyBindRule("image", xmlNode)); - } - } - } - - var elCaption = this.$getLayoutNode("item", "caption", htmlNode); - if (elCaption) { - if (elCaption.nodeType == 1) { - - elCaption.innerHTML = this.$applyBindRule("caption", xmlNode); - } - else - elCaption.nodeValue = this.$applyBindRule("caption", xmlNode); - } - - - //@todo - - - htmlNode.title = this.$applyBindRule("title", xmlNode) || ""; - - - var cssClass = this.$applyBindRule("css", xmlNode); - - if (cssClass || this.$dynCssClasses.length) { - this.$setStyleClass(htmlNode, cssClass, this.$dynCssClasses); - if (cssClass && !this.$dynCssClasses.contains(cssClass)) { - this.$dynCssClasses.push(cssClass); - } - } - - - if (!noModifier && this.$updateModifier) - this.$updateModifier(xmlNode, htmlNode); - }; - - this.$moveNode = function(xmlNode, htmlNode) { - if (!htmlNode) return; - - var oPHtmlNode = htmlNode.parentNode; - var nNode = this.getNextTraverse(xmlNode); //@todo could optimize because getTraverseNodes returns array indexOf - var beforeNode = nNode - ? apf.xmldb.findHtmlNode(nNode, this) - : null; - - oPHtmlNode.insertBefore(htmlNode, beforeNode); - //if(this.emptyMessage && !oPHtmlNode.childNodes.length) this.setEmpty(oPHtmlNode); - }; - - this.$add = function(xmlNode, Lid, xmlParentNode, htmlParentNode, beforeNode) { - //Build Row - this.$getNewContext("item"); - var oItem = this.$getLayoutNode("item"), - elSelect = this.$getLayoutNode("item", "select"), - elIcon = this.$getLayoutNode("item", "icon"), - elImage = this.$getLayoutNode("item", "image"), - //elCheckbox = this.$getLayoutNode("item", "checkbox"), // NOT USED - elCaption = this.$getLayoutNode("item", "caption"); - - oItem.setAttribute("id", Lid); - - elSelect.setAttribute("onmouseover", "var o = apf.lookup(" + this.$uniqueId - + "); o.$setStyleClass(this, 'hover', null, true);"); - elSelect.setAttribute("onselectstart", "return false;"); - elSelect.setAttribute("style", (elSelect.getAttribute("style") || "") - + ";user-select:none;-moz-user-select:none;-webkit-user-select:none;"); - - if (this.hasFeature(apf.__RENAME__) || this.hasFeature(apf.__DRAGDROP__)) { - elSelect.setAttribute("ondblclick", "var o = apf.lookup(" + this.$uniqueId + "); " + - - "o.stopRename();" + - - " o.choose()"); - elSelect.setAttribute("onmouseout", "var o = apf.lookup(" + this.$uniqueId + ");\ - o.$setStyleClass(this, '', ['hover'], true);\ - this.hasPassedDown = false;"); - elSelect.setAttribute(this.itemSelectEvent || "onmousedown", - 'var o = apf.lookup(' + this.$uniqueId + ');\ - var xmlNode = apf.xmldb.findXmlNode(this);\ - var isSelected = o.isSelected(xmlNode);\ - this.hasPassedDown = true;\ - if (event.button == 2) \ - o.stopRename();\ - else if (!o.renaming && o.hasFocus() && isSelected == 1) \ - this.dorename = true;\ - if (event.button == 2 && isSelected)\ - return;\ - if (!o.hasFeature(apf.__DRAGDROP__) || !isSelected && !event.ctrlKey)\ - o.select(this, event.ctrlKey, event.shiftKey, -1)'); - elSelect.setAttribute("onmouseup", 'if (!this.hasPassedDown) return;\ - var o = apf.lookup(' + this.$uniqueId + ');' + - - 'if (o.hasFeature(apf.__RENAME__) && this.dorename)\ - o.startDelayedRename(event, null, true);' + - - 'this.dorename = false;\ - var xmlNode = apf.xmldb.findXmlNode(this);\ - var isSelected = o.isSelected(xmlNode);\ - if (o.hasFeature(apf.__DRAGDROP__))\ - o.select(this, event.ctrlKey, event.shiftKey, -1)'); - } //@todo add DRAGDROP ifdefs - else { - elSelect.setAttribute("onmouseout", "apf.setStyleClass(this, '', ['hover']);"); - elSelect.setAttribute("ondblclick", 'var o = apf.lookup(' - + this.$uniqueId + '); o.choose(null, true)'); - elSelect.setAttribute(this.itemSelectEvent - || "onmousedown", 'var o = apf.lookup(' + this.$uniqueId - + '); o.select(this, event.ctrlKey, event.shiftKey, -1)'); - } - - - - - if (this.$mode) { - var elCheck = this.$getLayoutNode("item", "check"); - if (elCheck) { - elCheck.setAttribute("onmousedown", - "var o = apf.lookup(" + this.$uniqueId + ");\ - o.checkToggle(this, true);\o.$skipSelect = true;"); - - if (apf.isTrue(this.$applyBindRule("checked", xmlNode))) { - this.$checkedList.push(xmlNode); - this.$setStyleClass(oItem, "checked"); - } - else if (this.isChecked(xmlNode)) - this.$setStyleClass(oItem, "checked"); - } - else { - - return false; - } - } - - - //Setup Nodes Identity (Look) - if (elIcon) { - if (elIcon.nodeType == 1) { - elIcon.setAttribute("style", "background-image:url(" - + apf.getAbsolutePath(this.iconPath, this.$applyBindRule("icon", xmlNode)) - + ")"); - } - else { - elIcon.nodeValue = apf.getAbsolutePath(this.iconPath, - this.$applyBindRule("icon", xmlNode)); - } - } - else if (elImage) { - if (elImage.nodeType == 1) { - if ((elImage.tagName || "").toLowerCase() == "img") { - elImage.setAttribute("src", apf.getAbsolutePath(apf.hostPath, this.$applyBindRule("image", xmlNode))); - } - else { - elImage.setAttribute("style", "background-image:url(" - + apf.getAbsolutePath(apf.hostPath, this.$applyBindRule("image", xmlNode)) - + ")"); - } - } - else { - if (apf.isSafariOld) { //@todo this should be changed... blrgh.. - var p = elImage.ownerElement.parentNode, - img = p.appendChild(p.ownerDocument.createElement("img")); - img.setAttribute("src", - apf.getAbsolutePath(apf.hostPath, this.$applyBindRule("image", xmlNode))); - } - else { - elImage.nodeValue = - apf.getAbsolutePath(apf.hostPath, this.$applyBindRule("image", xmlNode)); - } - } - } - - if (elCaption) { - - { - apf.setNodeValue(elCaption, - this.$applyBindRule("caption", xmlNode)); - } - } - oItem.setAttribute("title", this.$applyBindRule("tooltip", xmlNode) || ""); - - - var cssClass = this.$applyBindRule("css", xmlNode); - if (cssClass) { - this.$setStyleClass(oItem, cssClass); - if (cssClass) - this.$dynCssClasses.push(cssClass); - } - - - if (this.$addModifier && - this.$addModifier(xmlNode, oItem, htmlParentNode, beforeNode) === false) - return; - - if (htmlParentNode) - apf.insertHtmlNode(oItem, htmlParentNode, beforeNode); - else - this.listNodes.push(oItem); - }; - - this.addEventListener("$skinchange", function(e) { - if (this.more) - delete this.moreItem; - }); - - this.$fill = function(){ - if (this.more && !this.moreItem) { - this.$getNewContext("item"); - var Item = this.$getLayoutNode("item"), - elCaption = this.$getLayoutNode("item", "caption"), - elSelect = this.$getLayoutNode("item", "select"); - - Item.setAttribute("class", this.$baseCSSname + "More"); - elSelect.setAttribute("onmousedown", 'var o = apf.lookup(' + this.$uniqueId - + ');o.clearSelection();o.$setStyleClass(this, "more_down", null, true);'); - elSelect.setAttribute("onmouseout", 'apf.lookup(' + this.$uniqueId - + ').$setStyleClass(this, "", ["more_down"], true);'); - elSelect.setAttribute("onmouseup", 'apf.lookup(' + this.$uniqueId - + ').startMore(this, true)'); - - if (elCaption) - apf.setNodeValue(elCaption, - this.more.match(/caption:(.*)(;|$)/i)[1]); - this.listNodes.push(Item); - } - - apf.insertHtmlNodes(this.listNodes, this.$container); - this.listNodes.length = 0; - - if (this.more && !this.moreItem) { - this.moreItem = this.$container.lastChild; - } - - }; - - /** - * Adds a new item to the list, and lets the users type in the new name. - * - * This functionality is especially useful in the interface when - * the list mode is set to `check` or `radio`--for instance in a form. - */ - this.startMore = function(o, userAction) { - if (userAction && this.disabled) - return; - - this.$setStyleClass(o, "", ["more_down"]); - - var xmlNode; - if (!this.$actions["add"]) { - if (this.each && !this.each.match(/[\/\[]/)) { - xmlNode = "<" + this.each + (this.each.match(/^a:/) - ? " xmlns:a='" + apf.ns.aml + "'" - : "") + " custom='1' />"; - } - else { - - //return false; - xmlNode = ""; - } - } - - this.add(xmlNode, null, null, function(addedNode) { - this.select(addedNode, null, null, null, null, true); - if (this.morePos == "begin") - this.$container.insertBefore(this.moreItem, this.$container.firstChild); - else - this.$container.appendChild(this.moreItem); - - var undoLastAction = function(){ - this.getActionTracker().undo(this.autoselect ? 2 : 1); - - this.removeEventListener("stoprename", undoLastAction); - this.removeEventListener("beforerename", removeSetRenameEvent); - this.removeEventListener("afterrename", afterRename); - } - var afterRename = function(){ - //this.select(addedNode); - this.removeEventListener("afterrename", afterRename); - }; - var removeSetRenameEvent = function(e) { - this.removeEventListener("stoprename", undoLastAction); - this.removeEventListener("beforerename", removeSetRenameEvent); - - //There is already a choice with the same value - var xmlNode = this.findXmlNodeByValue(e.args[1]); - if (xmlNode || !e.args[1]) { - if (e.args[1] && this.dispatchEvent("notunique", { - value: e.args[1] - }) === false) { - this.startRename(); - - this.addEventListener("stoprename", undoLastAction); - this.addEventListener("beforerename", removeSetRenameEvent); - } - else { - this.removeEventListener("afterrename", afterRename); - - this.getActionTracker().undo();//this.autoselect ? 2 : 1); - if (!this.isSelected(xmlNode)) - this.select(xmlNode); - } - - return false; - } - }; - - this.addEventListener("stoprename", undoLastAction); - this.addEventListener("beforerename", removeSetRenameEvent); - this.addEventListener("afterrename", afterRename); - - - this.startDelayedRename({}, 1); - - }); - }; - - // *** Selection *** // - - this.$calcSelectRange = function(xmlStartNode, xmlEndNode) { - var r = [], - nodes = this.hasFeature(apf.__VIRTUALVIEWPORT__) - ? this.xmlRoot.selectNodes(this.each) - : this.getTraverseNodes(), - f, i; - for (f = false, i = 0; i < nodes.length; i++) { - if (nodes[i] == xmlStartNode) - f = true; - if (f) - r.push(nodes[i]); - if (nodes[i] == xmlEndNode) - f = false; - } - - if (!r.length || f) { - r = []; - for (f = false, i = nodes.length - 1; i >= 0; i--) { - if (nodes[i] == xmlStartNode) - f = true; - if (f) - r.push(nodes[i]); - if (nodes[i] == xmlEndNode) - f = false; - } - } - - return r; - }; - - this.$selectDefault = function(XMLRoot) { - this.select(this.getTraverseNodes()[0], null, null, null, true); - }; - - /** - * Generates a list of items based on a string. - * @param {String} str The description of the items. Items are seperated by a comma (`,`). Ranges are specified by a start and end value seperated by a dash (`-`). - * - * #### Example - * - * This example loads a list with items starting at 1980 and ending at 2050. - * - * #### ```xml - * lst.loadFillData("1980-2050"); - * lst.loadFillData("red,green,blue,white"); - * lst.loadFillData("None,100-110,1000-1100"); // 101, 102...110, 1000,1001, e.t.c. - * lst.loadFillData("1-10"); // 1 2 3 4 e.t.c. - * lst.loadFillData("01-10"); //01, 02, 03, 04, e.t.c. - * ``` - */ - this.loadFillData = function(str) { - var len, start, end, parts = str.splitSafe(","), data = []; - - for (var p, part, i = 0; i < parts.length; i++) { - if ((part = parts[i]).match(/^\d+-\d+$/)) { - p = part.split("-"); - start = parseInt(p[0]); - end = parseInt(p[1]); - - if (p[0].length == p[1].length) { - len = Math.max(p[0].length, p[1].length); - for (var j = start; j < end + 1; j++) { - data.push("" + (j + "").pad(len, "0") + ""); - } - } - else { - for (var j = start; j < end + 1; j++) { - data.push("" + j + ""); - } - } - } - else { - data.push("" + part + ""); - } - } - - //@todo this is all an ugly hack (copied from item.js line 486) - //this.$preventDataLoad = true;//@todo apf3.0 add remove for this - - this.$initingModel = true; - - this.each = "item"; - this.$setDynamicProperty("caption", "[label/text()|@caption|text()]"); - this.$setDynamicProperty("eachvalue", "[value/text()|@value|text()]"); - this.$canLoadDataAttr = false; - - this.load("" + data.join("") + ""); - }; - -}).call(apf.BaseList.prototype = new apf.MultiSelect()); - - - - - - - - -/** - * An element allowing a user to select a value from a list, which is - * displayed when the user clicks a button. - * - * #### Example: Simple Dropdown - * - * ```xml, demo - * - * - * - * America - * Armenia - * The Netherlands - * - * - * - * ``` - * - * #### Example: Loading Items From XML - * - * ```xml, demo - * - * - * - * - * - * ``` - * - * #### Example: Capturing and Emitting Events - * - * A databound dropdown using the bindings element - * - * ```xml, demo - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * Slide Down - * Slide Up - * - * - * - * ``` - * - * #### Example: Dynamically Adding Entries - * - * ```xml, demo - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * New Name? - * - * - * ``` - * - * @class apf.dropdown - * @define dropdown - * @form - * @allowchild item, {smartbinding} - * - * - * @inherits apf.BaseList - * - * @author Ruben Daniels (ruben AT ajax DOT org) - * @version %I%, %G% - * @since 0.4 - */ -/** - * @event slidedown Fires when the dropdown slides open. - * @cancelable Prevents the dropdown from sliding open - */ -/** - * @event slideup Fires when the dropdown slides up. - * @cancelable Prevents the dropdown from sliding up - * - */ -apf.dropdown = function(struct, tagName) { - this.$init(tagName || "dropdown", apf.NODE_VISIBLE, struct); -}; - -(function(){ - this.$animType = 1; - this.$animSteps = 5; - this.$animSpeed = 20; - this.$itemSelectEvent = "onmouseup"; - - // *** Properties and Attributes *** // - - this.dragdrop = false; - this.reselectable = true; - this.$focussable = apf.KEYBOARD; - this.autoselect = false; - this.multiselect = false; - this.disableremove = true; - this.delayedselect = false; - this.maxitems = 5; - - this.$booleanProperties["disableremove"] = true; - this.$supportedProperties.push("maxitems", "disableremove", - "initial-message", "fill"); - - /** - * @attribute {String} initial-message Sets or gets the message displayed by this element - * when it doesn't have a value set. This property is inherited from parent - * nodes. When none is found it is looked for on the appsettings element. - * - */ - /** - * @attribute {Number} maxitems Sets or gets the number of items that are shown at the - * same time in the container. - */ - this.$propHandlers["maxitems"] = function(value) { - this.sliderHeight = value - ? (Math.min(this.maxitems || 100, value) * this.itemHeight) - : 10; - this.containerHeight = value - ? (Math.min(this.maxitems || 100, value) * this.itemHeight) - : 10; - /*if (this.containerHeight > 20) - this.containerHeight = Math.ceil(this.containerHeight * 0.9);*/ - }; - - this.addEventListener("prop.class", function(e) { - this.$setStyleClass(this.oSlider, e.value); - }); - - // *** Public methods *** // - - /* - * Toggles the visibility of the container with the list elements. It opens - * or closes it using a slide effect. - * @private - */ - this.slideToggle = function(e, userAction) { - if (!e) e = event; - if (userAction && this.disabled) - return; - - if (this.isOpen) - this.slideUp(); - else - this.slideDown(e); - }; - - /* - * Shows the container with the list elements using a slide effect. - * @private - */ - this.slideDown = function(e) { - if (this.dispatchEvent("slidedown") === false) - return false; - - this.isOpen = true; - - this.$propHandlers["maxitems"].call(this, this.xmlRoot && this.each - ? this.getTraverseNodes().length : this.childNodes.length); //@todo apf3.0 count element nodes - - this.oSlider.style.display = "block"; - if (!this.ignoreOverflow) { - this.oSlider.style[apf.supportOverflowComponent - ? "overflowY" - : "overflow"] = "visible"; - this.$container.style.overflowY = "hidden"; - } - - this.oSlider.style.display = ""; - - this.$setStyleClass(this.$ext, this.$baseCSSname + "Down"); - - //var pos = apf.getAbsolutePosition(this.$ext); - this.oSlider.style.height = (this.sliderHeight - 1) + "px"; - this.oSlider.style.width = (this.$ext.offsetWidth - 2 - this.widthdiff) + "px"; - - var _self = this; - var _popupCurEl = apf.popup.getCurrentElement(); - apf.popup.show(this.$uniqueId, { - x: 0, - y: this.$ext.offsetHeight, - zindextype: "popup+", - animate: true, - container: this.$getLayoutNode("container", "contents", this.oSlider), - ref: this.$ext, - width: this.$ext.offsetWidth - this.widthdiff, - height: this.containerHeight, - allowTogether: (_popupCurEl && apf.isChildOf(_popupCurEl.$ext, _self.$ext)), - callback: function(container) { - if (!_self.ignoreOverflow) { - _self.$container.style.overflowY = "auto"; - } - } - }); - }; - - /* - * Hides the container with the list elements using a slide effect. - * @private - */ - this.slideUp = function(){ - if (this.isOpen == 2) return false; - if (this.dispatchEvent("slideup") === false) return false; - - this.isOpen = false; - if (this.selected) { - var htmlNode = apf.xmldb.findHtmlNode(this.selected, this); - if (htmlNode) this.$setStyleClass(htmlNode, '', ["hover"]); - } - - this.$setStyleClass(this.$ext, '', [this.$baseCSSname + "Down"]); - if (apf.popup.last == this.$uniqueId) - apf.popup.hide(); - return false; - }; - - // *** Private methods and event handlers *** // - - //@todo apf3.0 why is this function called 6 times on init. - this.$setLabel = function(value) { - - this.oLabel.innerHTML = value || this["initial-message"] || ""; - - - this.$setStyleClass(this.$ext, value ? "" : this.$baseCSSname + "Initial", - !value ? [] : [this.$baseCSSname + "Initial"]); - }; - - this.addEventListener("afterselect", function(e) { - if (!e) e = event; - - this.slideUp(); - if (!this.isOpen) - this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Over"]); - - this.$setLabel(e.selection.length - ? this.$applyBindRule("caption", this.selected) - : ""); - }); - - function setMaxCount() { - if (this.isOpen == 2) - this.slideDown(); - } - - this.addEventListener("afterload", setMaxCount); - this.addEventListener("xmlupdate", function(){ - setMaxCount.call(this); - this.$setLabel(this.$applyBindRule("caption", this.selected)); - }); - - // Private functions - this.$blur = function(){ - this.slideUp(); - //this.$ext.dispatchEvent("mouseout") - if (!this.isOpen) - this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Over"]) - - this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Focus"]); - }; - - /*this.$focus = function(){ - apf.popup.forceHide(); - this.$setStyleClass(this.oFocus || this.$ext, this.$baseCSSname + "Focus"); - }*/ - - this.$setClearMessage = function(msg) { - this.$setLabel(msg); - }; - - this.$removeClearMessage = function(){ - this.$setLabel(""); - }; - - this.addEventListener("popuphide", this.slideUp); - - // *** Keyboard Support *** // - - - this.addEventListener("keydown", function(e) { - var key = e.keyCode; - //var ctrlKey = e.ctrlKey; << unused - //var shiftKey = e.shiftKey; - - if (!this.xmlRoot) return; - - var node; - - switch (key) { - case 32: - this.slideToggle(e.htmlEvent); - break; - case 38: - //UP - if (e.altKey) { - this.slideToggle(e.htmlEvent); - return; - } - - if (!this.selected) - return; - - node = this.getNextTraverseSelected(this.caret - || this.selected, false); - - if (node) - this.select(node); - break; - case 40: - //DOWN - if (e.altKey) { - this.slideToggle(e.htmlEvent); - return; - } - - if (!this.selected) { - node = this.getFirstTraverseNode(); - if (!node) - return; - } - else - node = this.getNextTraverseSelected(this.selected, true); - - if (node) - this.select(node); - - break; - default: - if (key == 9 || !this.xmlRoot) return; - - //if(key > 64 && key < - if (!this.lookup || new Date().getTime() - this.lookup.date.getTime() > 1000) - this.lookup = { - str: "", - date: new Date() - }; - - this.lookup.str += String.fromCharCode(key); - - var caption, nodes = this.getTraverseNodes(); - for (var i = 0; i < nodes.length; i++) { - caption = this.$applyBindRule("caption", nodes[i]); - if (caption && caption.indexOf(this.lookup.str) > -1) { - this.select(nodes[i]); - return; - } - } - return; - } - - return false; - }, true); - - - // *** Init *** // - - this.$draw = function(){ - this.$getNewContext("main"); - this.$getNewContext("container"); - - this.$animType = this.$getOption("main", "animtype") || 1; - this.clickOpen = this.$getOption("main", "clickopen") || "button"; - - //Build Main Skin - this.$ext = this.$getExternal(null, null, function(oExt) { - oExt.setAttribute("onmouseover", 'var o = apf.lookup(' + this.$uniqueId - + ');o.$setStyleClass(o.$ext, o.$baseCSSname + "Over", null, true);'); - oExt.setAttribute("onmouseout", 'var o = apf.lookup(' + this.$uniqueId - + ');if(o.isOpen) return;o.$setStyleClass(o.$ext, "", [o.$baseCSSname + "Over"], true);'); - - //Button - var oButton = this.$getLayoutNode("main", "button", oExt); - if (oButton) { - oButton.setAttribute("onmousedown", 'apf.lookup(' - + this.$uniqueId + ').slideToggle(event, true);'); - } - - //Label - var oLabel = this.$getLayoutNode("main", "label", oExt); - if (this.clickOpen == "both") { - oLabel.parentNode.setAttribute("onmousedown", 'apf.lookup(' - + this.$uniqueId + ').slideToggle(event, true);'); - } - }); - this.oLabel = this.$getLayoutNode("main", "label", this.$ext); - - - if (this.oLabel.nodeType == 3) - this.oLabel = this.oLabel.parentNode; - - - this.oIcon = this.$getLayoutNode("main", "icon", this.$ext); - if (this.$button) - this.$button = this.$getLayoutNode("main", "button", this.$ext); - - this.oSlider = apf.insertHtmlNode(this.$getLayoutNode("container"), - document.body); - this.$container = this.$getLayoutNode("container", "contents", this.oSlider); - this.$container.host = this; - - //Set up the popup - this.$pHtmlDoc = apf.popup.setContent(this.$uniqueId, this.oSlider, - apf.skins.getCssString(this.skinName)); - - //Get Options form skin - //Types: 1=One dimensional List, 2=Two dimensional List - this.listtype = parseInt(this.$getLayoutNode("main", "type")) || 1; - - this.itemHeight = this.$getOption("main", "item-height") || 18.5; - this.widthdiff = this.$getOption("main", "width-diff") || 0; - this.ignoreOverflow = apf.isTrue(this.$getOption("main", "ignore-overflow")) || false; - }; - - this.addEventListener("DOMNodeInsertedIntoDocument", function(){ - if (typeof this["initial-message"] == "undefined") - this.$setInheritedAttribute("initial-message"); - - if (!this.selected && this["initial-message"]) - this.$setLabel(); - }); - - this.$destroy = function(){ - apf.popup.removeContent(this.$uniqueId); - apf.destroyHtmlNode(this.oSlider); - this.oSlider = null; - }; - - -}).call(apf.dropdown.prototype = new apf.BaseList()); - -apf.config.$inheritProperties["initial-message"] = 1; - -apf.aml.setElement("dropdown", apf.dropdown); - - - - - - - - - - - - - - -/** - * This element displays a skinnable list of options which can be selected. - * - * Selection of multiple items is allowed. Items can be renamed - * and removed. The list can be used as a collection of checkboxes or - * radiobuttons. This is especially useful for use in forms. - * - * This element is one of the most often used elements. It can display lists - * of items in a CMS-style interface, or display a list of search results in - * a more website like interface. - * - * #### Example: A Simple List - * - * ```xml, demo - * - * - * - * The Netherlands - * United States of America - * United Kingdom - * - * - * - * ``` - * - * #### Example: Loading from a Model - * - * ```xml, demo - * - * - * - * - * - * - * ``` - * - * #### Example: Using XPaths - * - * ```xml, demo - * - * - * - * - * - * - * - * - * - * - * - * ``` - * @class apf.list - * @define list - * @allowchild {smartbinding} - * - * @selection - * @inherits apf.BaseList - * @inherits apf.Rename - * - * @author Ruben Daniels (ruben AT ajax DOT org) - * @version %I%, %G% - * @since 0.4 - */ -/** - * @event click Fires when a user presses a mouse button while over this element. - */ - -apf.list = function(struct, tagName) { - this.$init(tagName || "list", apf.NODE_VISIBLE, struct); -}; - -(function(){ - this.morePos = "end"; - - - - this.$getCaptionElement = function(){ - if (!(this.$caret || this.$selected)) - return; - - var x = this.$getLayoutNode("item", "caption", this.$caret || this.$selected); - if (!x) - return; - return x.nodeType == 1 ? x : x.parentNode; - }; - - - - - - - - // *** Properties and Attributes *** // - - this.$supportedProperties.push("appearance", "mode", "more", "thumbsize", "morepos"); - - this.$propHandlers["morepos"] = function(value) { - this.morePos = value; - }; - - this.$propHandlers["thumbsize"] = function(value) { - var className = this.thumbclass; - - apf.setStyleRule(className, "width", value + "px"); - apf.setStyleRule(className, "height", value + "px"); - }; - - - /** - * @attribute {String} appearance Sets or gets the type of select this element is. - * This is an xforms property and only available if APF is compiled - * with `__WITH_XFORMS` set to `1`. - * - * Possible values include: - * - * - `"full"` : depending on the tagName this element is either a list of radio options or of checked options. - * - `"compact"`: this elements functions like a list with multiselect off. - * - `"minimal"`: this element functions as a dropdown element. - */ - this.$propHandlers["appearance"] = function(value) { - - }; - - - /** - * @attribute {String} more Adds a new item to the list and lets the users - * type in the new name. This is especially useful in the interface when - * the mode is set to check or radio--for instance in a form. - * - * #### Example - * - * This example shows a list in form offering the user several options. The - * user can add a new option. A server script could remember the addition - * and present it to all new users of the form. - * - * ```xml - * - * - * - * Suggestion 1 - * Suggestion 2 - * - * - * - * Which newspapers do you read? - * - * - * - * - * - * - * - * - * - * - * New Answer - * - * - * - * ``` - */ - this.$propHandlers["more"] = function(value) { - if (value) { - this.delayedselect = false; - this.addEventListener("xmlupdate", $xmlUpdate); - this.addEventListener("afterload", $xmlUpdate); - //this.addEventListener("afterrename", $afterRenameMore); - //this.addEventListener("beforeselect", $beforeSelect); - - this.$addMoreItem = function(msg) { - if (!this.moreItem) - this.$fill(); - if (this.morePos == "begin") - this.$container.insertBefore(this.moreItem, this.$container.firstChild); - else - this.$container.appendChild(this.moreItem); - }; - this.$updateClearMessage = function(){} - this.$removeClearMessage = function(){}; - } - else { - this.removeEventListener("xmlupdate", $xmlUpdate); - this.removeEventListener("afterload", $xmlUpdate); - //this.removeEventListener("afterrename", $afterRenameMore); - //this.removeEventListener("beforeselect", $beforeSelect); - } - }; - - function $xmlUpdate(e) { - if ((!e.action || "insert|add|synchronize|move".indexOf(e.action) > -1) && this.moreItem) { - if (this.morePos == "begin") - this.$container.insertBefore(this.moreItem, this.$container.firstChild); - else - this.$container.appendChild(this.moreItem); - } - } - - /*function $afterRenameMore(){ - var caption = this.$applyBindRule("caption", this.caret) - var xmlNode = this.findXmlNodeByValue(caption); - - var curNode = this.caret; - if (xmlNode != curNode || !caption) { - if (xmlNode && !this.isSelected(xmlNode)) - this.select(xmlNode); - this.remove(curNode); - } - else - if (!this.isSelected(curNode)) - this.select(curNode); - } - - function $beforeSelect(e) { - //This is a hack - if (e.xmlNode && this.isSelected(e.xmlNode) - && e.xmlNode.getAttribute('custom') == '1') { - this.setCaret(e.xmlNode); - this.selected = e.xmlNode; - $setTimeout(function(){ - _self.startRename() - }); - return false; - } - }*/ - - - // *** Keyboard support *** // - - - this.addEventListener("keydown", this.$keyHandler, true); - - - // *** Init *** // - - this.$draw = function(){ - this.appearance = this.getAttribute("appearance") || "compact"; - - //Build Main Skin - this.$ext = this.$getExternal(); - this.$container = this.$getLayoutNode("main", "container", this.$ext); - - if (apf.hasCssUpdateScrollbarBug && !this.mode) - this.$fixScrollBug(); - - var _self = this; - this.$ext.onclick = function(e) { - _self.dispatchEvent("click", { - htmlEvent: e || event - }); - } - - - - //Get Options form skin - //Types: 1=One dimensional List, 2=Two dimensional List - this.listtype = parseInt(this.$getOption("main", "type")) || 1; - //Types: 1=Check on click, 2=Check independent - this.behaviour = parseInt(this.$getOption("main", "behaviour")) || 1; - - this.thumbsize = this.$getOption("main", "thumbsize"); - this.thumbclass = this.$getOption("main", "thumbclass"); - }; - - this.$loadAml = function(x) { - }; - - this.$destroy = function(){ - if (this.$ext) - this.$ext.onclick = null; - apf.destroyHtmlNode(this.oDrag); - this.oDrag = null; - }; -}).call(apf.list.prototype = new apf.BaseList()); - -apf.aml.setElement("list", apf.list); - - - - - -}; - -}); \ No newline at end of file diff --git a/plugins/c9.ide.ui/lib/flexbox.js b/plugins/c9.ide.ui/lib/flexbox.js deleted file mode 100644 index 17f54cf7..00000000 --- a/plugins/c9.ide.ui/lib/flexbox.js +++ /dev/null @@ -1,994 +0,0 @@ -define(function(require, module, exports) { -return function(apf) { -var $setTimeout = setTimeout; -var $setInterval = setInterval; - - - - - - - - - - - -/** - * A container that stacks its children vertically. - * - * #### Example - * - * ```xml, demo - * - * - * - * Button 1 - * Button 2 - * Button 3 - * - * - * - * ``` - * - * @class apf.vbox - * @layout - * @define vbox - * - * - * @see element.hbox - * - * @author Ruben Daniels (ruben AT ajax DOT org) - * @version %I%, %G% - * @since 0.9 - * @layout - */ -/** - * A container that stacks its children horizontally. - * - * #### Example - * - * ```xml, demo - * - * - * - * Button 1 - * Button 2 - * Button 3 - * - * - * - * ``` - * - * - * #### Remarks - * - * Firefox has some issues: - * - * 1. Sometimes it's necessary to put a fixed width to have it calculate the right - * height value. - * 2. Using flex="1" on non fixed height/width tree's will give unexpected results. - * - * - * - * @class apf.hbox - * @inherits apf.GuiElement - * @define hbox - * @layout - * - * @see element.vbox - * @author Ruben Daniels (ruben AT ajax DOT org) - * @version %I%, %G% - * @since 0.9 - */ -apf.hbox = function(struct, tagName) { - this.$init(tagName || "hbox", apf.NODE_VISIBLE, struct); -}; -apf.vbox = function(struct, tagName) { - this.$init(tagName || "vbox", apf.NODE_VISIBLE, struct); -}; - -(function(){ - this.minwidth = 0; - this.minheight = 0; - - // *** Properties and Attributes *** // - - this.$focussable = false; - this.$useLateDom = true; - this.$box = true; - this.$layout = true; - - var input = {"INPUT":1, "SELECT":1, "TEXTAREA":1}; - - /** - * @attribute {String} [padding=2] Sets or gets the space between each element. - */ - /** - * @attribute {Boolean} reverse Sets or gets whether the sequence of the elements is in reverse order. - */ - /** - * @attribute {String} [edge="5,5,5,5"] Sets or gets the space between the container and the elements, space seperated in pixels for each side. Similar to CSS in the sequence (_.i.e._. `top right bottom left`). - * - * #### Example - * - * ```xml - * - * ``` - */ - // @todo Doc - /** - * @attribute {String} pack - * - * Possible values include: - * - * - `"start"`: - * - `"center"`: - * - `"end"`: - */ - /** - * @attribute {Boolean} align - * - * Possible values include: - * - * - `"start"`: - * - `"center"`: - * - `"end"`: - * - `"stretch"`: - */ - this.$booleanProperties["splitters"] = true; - this.$supportedProperties.push("padding", "reverse", "edge", "pack", "align", "splitters"); - - this.$propHandlers["padding"] = function(value) { - this.padding = parseInt(value); - - var node, nodes = this.childNodes, elms = []; - for (var i = 0, l = nodes.length; i < l; i++) { - if ((node = nodes[i]).nodeFunc == apf.NODE_VISIBLE - && node.$ext && node.visible !== false) - elms.push(node); - } - - if (!elms.length) - return; - - for (var last, b, el, i = elms.length - 2; i >= 0; i--) { - b = (el = elms[i]).margin && apf.getBox(el.margin) || [0,0,0,0]; - - if ((!last || !last.$splitter) && !el.$splitter) { - b[this.$vbox ? 2 : 1] += this.padding; - - if (!apf.hasFlexibleBox && i != 0 && this.align == "stretch" && this.$vbox) - b[0] += this.padding; - } - - el.$ext.style.margin = b.join("px ") + "px"; - last = el; - } - b = (el = elms[elms.length - 1]).margin && apf.getBox(el.margin) || [0,0,0,0]; - el.$ext.style.margin = b.join("px ") + "px"; - - if (!apf.hasFlexibleBox) - this.$resize(); - } - - this.$propHandlers["reverse"] = function(value) { - if (apf.hasFlexibleBox) - this.$int.style[apf.CSSPREFIX + "BoxDirection"] = value ? "reverse" : "normal"; - else { - //@todo - } - }; - - this.$propHandlers["edge"] = function(value) { - var el = !apf.hasFlexibleBox && this.$vbox ? this.$ext : this.$int; - el.style.padding = (this.$edge = apf.getBox(value)).join("px ") + "px"; - - if (!apf.hasFlexibleBox) - this.$resize(); - }; - - this.$propHandlers["pack"] = function(value) { - if (apf.hasFlex) { - if (value == "start" || value == "end") - value = "flex-" + value; - this.$int.style.justifyContent = value || "flex-start"; - } else if (apf.hasFlexibleBox) { - this.$int.style[apf.CSSPREFIX + "BoxPack"] = value || "start"; - } else if (this.$amlLoaded) { - if (this.$vbox) { - this.$int.style.verticalAlign = value == "center" ? "middle" : (value == "end" ? "bottom" : "top"); - } - else { - this.$int.style.textAlign = ""; - - var nodes = this.childNodes; - for (var i = 0, l = nodes.length; i < l; i++) { - if ((node = nodes[i]).nodeFunc != apf.NODE_VISIBLE || !node.$amlLoaded) //|| node.visible === false - continue; - - node.$ext.style.textAlign = apf.getStyle(node.$ext, "textAlign") || "left"; - } - - this.$int.style.textAlign = value == "center" ? "center" : (value == "end" ? "right" : "left"); - } - } - }; - - //@todo change overflow when height/width changes depending on $vbox - - this.$propHandlers["align"] = function(value) { - if (apf.hasFlex) { - if (value == "start" || value == "end") - value = "flex-" + value; - this.$int.style.alignItems = value || "stretch"; // flex-start - } - else if (apf.hasFlexibleBox) { - this.$int.style[apf.CSSPREFIX + "BoxAlign"] = value || "stretch"; - - //@todo this should probably be reinstated - var stretch = !value || value == "stretch"; - var nodes = this.childNodes; - var size = this.$vbox ? "width" : "height"; - - var isInFixed = false, loopNode = this; - while (!isInFixed && loopNode) { - isInFixed = loopNode[size] || loopNode.anchors || (loopNode.$vbox ? loopNode.top && loopNode.bottom : loopNode.left && loopNode.right); - if (!loopNode.flex) - break; - loopNode = loopNode.parentNode || loopNode.$parentNode; - } - - for (var i = 0, l = nodes.length; i < l; i++) { - if (!(node = nodes[i]).$ext || node.$ext.nodeType != 1) - continue; - - //(this[size] || this.anchors || (this.$vbox ? this.top && this.bottom : this.left && this.right) - if (stretch && !node[size]) //(node.$altExt || - node.$ext.style[size] = (input[node.$ext.tagName] - ? "100%" : "auto"); - else if (node[size]) - handlers["true"][size].call(node, node[size]); - } - } - else if (this.$amlLoaded) { - var stretch = !value || value == "stretch"; - - if (!this.$vbox) { - var nodes = this.childNodes; - for (var i = 0, l = nodes.length; i < l; i++) { - if ((node = nodes[i]).nodeFunc != apf.NODE_VISIBLE || !node.$amlLoaded) //|| node.visible === false - continue; - - node.$ext.style.verticalAlign = value == "center" ? "middle" : (value == "end" ? "bottom" : "top"); - } - } - else { - var el = !apf.hasFlexibleBox && this.$vbox ? this.$ext : this.$int; - el.style.textAlign = ""; - - var node, nodes = this.childNodes; - for (var i = 0, l = nodes.length; i < l; i++) { - if ((node = nodes[i]).nodeFunc != apf.NODE_VISIBLE || !node.$amlLoaded) //|| node.visible === false - continue; - - if (node.visible !== false) { - node.$ext.style.display = value == "stretch" ? "block" : "inline-block"; - node.$br.style.display = value == "stretch" ? "none" : ""; - } - node.$ext.style.textAlign = apf.getStyle(node.$ext, "textAlign") || "left"; - } - - el.style.textAlign = value == "center" ? "center" : (value == "end" ? "right" : "left"); - } - } - }; - - function visibleHandler(e) { - - if (this.parentNode.splitters && !this.$splitter) { - if (!e.value) { - if (this.nextSibling && this.nextSibling.$splitter) - this.nextSibling.removeNode(); - else if (this.previousSibling && this.previousSibling.$splitter) - this.previousSibling.removeNode(); - } - else { - var isLast = isLastVisibleChild(this); - if (!isLast) { - if (!this.nextSibling.$splitter && !this.nextSibling.nosplitter - && !isFirstVisibleChild(this) && !this.nosplitter) { - this.parentNode.insertBefore( - this.ownerDocument.createElementNS(apf.ns.aml, "splitter"), - this.nextSibling); - } - } - else if (this.previousSibling && !this.previousSibling.$splitter - && !this.previousSibling.nosplitter) { - this.parentNode.insertBefore( - this.ownerDocument.createElementNS(apf.ns.aml, "splitter"), - this); - } - } - } - - - //@todo this can be more optimized by calcing if it WAS the last vis child. - if (this.parentNode.$propHandlers["padding"]) {// && isLastVisibleChild(this)) { - this.parentNode.$propHandlers["padding"] - .call(this.parentNode, this.parentNode.padding); - } - - apf.layout.forceResize(this.parentNode.$int); - - if (apf.hasFlexibleBox) { - if (this.$altExt) - this.$altExt.style.display = e.value - ? apf.CSS_DISPLAY_FLEX - : "none"; - return; - } - - if (e.value) { - this.$ext.style.display = this.parentNode.$vbox - && this.parentNode.align == "stretch" ? "block" : "inline-block"; - if (this.$br) - this.$br.style.display = this.parentNode.align == "stretch" ? "none" : ""; - } - else { - if (this.$br) - this.$br.style.display = "none"; - } - - this.parentNode.$resize(); - } - - function resizeHandler(){ - if (!this.flex) { - if (this.$isRszHandling || this.$lastSizeChild && - this.$lastSizeChild[0] == this.$ext.offsetWidth && - this.$lastSizeChild[1] == this.$ext.offsetHeight) - return; - - /*if (this.$skipResizeOnce) - delete this.$skipResizeOnce; - else*/ - this.parentNode.$resize(true); - - this.$lastSizeChild = [this.$ext.offsetWidth, this.$ext.offsetHeight]; - } - } - - var handlers = { - //Handlers for flexible box layout - "true" : { - "optimize" : function(value) { - this.optimize = apf.isTrue(value); - }, - - "width" : function(value) { - //@todo this should check the largest and only allow that one - //if (this.parentNode.$vbox && this.parentNode.align == "stretch") - //return; - - (this.$altExt || this.$ext).style.width = !apf.isNot(value) - ? (parseFloat(value) == value - ? value + "px" - : value) - : ""; - }, - - "height" : function(value) { - //@todo this should check the largest and only allow that one - //if (!this.parentNode.$vbox && this.parentNode.align == "stretch") - //return; - - (this.$altExt || this.$ext).style.height = !apf.isNot(value) - ? (parseFloat(value) == value - ? value + "px" - : value) - : (apf.isGecko && this.flex && this.parentNode.$vbox ? "auto" : ""); - }, - - "margin" : function(value) { - var b = apf.getBox(value); - if (!isLastVisibleChild(this)) - b[this.parentNode.$vbox ? 2 : 1] += this.parentNode.padding; - this.$ext.style.margin = b.join("px ") + "px"; - }, - - "flex" : function(value) { - this.flex = value = parseInt(value); - if (value) { - if (!this.optimize && !this.$altExt) { - this.$altExt = this.$ext.ownerDocument.createElement("div"); - this.parentNode.$int.replaceChild(this.$altExt, this.$ext); - this.$altExt.appendChild(this.$ext); - this.$altExt.style.boxSizing = "border-box"; - this.$altExt.style.display = apf.CSS_DISPLAY_FLEX; - this.$altExt.style[apf.CSSPREFIX + "BoxOrient"] = "vertical"; - this.$ext.style[apf.CSS_FLEX_PROP] = 1; - var size = this.parentNode.$vbox ? "height" : "width"; - //var osize = this.parentNode.$vbox ? "width" : "height"; - - if (!this.preventforcezero) - this.$altExt.style[size] = "0px"; - } - - (this.$altExt || this.$ext).style[apf.CSS_FLEX_PROP] = parseInt(value) || 1; - } - else if (this.$altExt) { - this.parentNode.$int.replaceChild(this.$ext, this.$altExt); - this.$ext.style[apf.CSS_FLEX_PROP] = ""; - delete this.$altExt; - } - } - }, - - //Handlers for older browsers - "false" : { - "width" : function(value) { - //@todo this should check the largest and only allow that one - //if (this.parentNode.$vbox && this.parentNode.align == "stretch") - //return; - - this.$ext.style.width = value - ? (parseFloat(value) == value - ? Math.max(0, value - apf.getWidthDiff(this.$ext)) + "px" - : value) - : ""; - }, - - "height" : function(value) { - //@todo this should check the largest and only allow that one - //if (this.parentNode.localName == "hbox" && this.parentNode.align == "stretch") - //return; - - this.$ext.style.height = value - ? (parseFloat(value) == value - ? Math.max(0, value - apf.getHeightDiff(this.$ext)) + "px" - : value) - : ""; - }, - - "margin" : function(value) { - var b = apf.getBox(value); - if (this.padding) { - if (!isLastVisibleChild(this)) - b[this.parentNode.$vbox ? 2 : 1] += this.padding; - if (this != this.parentNode.firstChild && this.parentNode.align == "stretch" && this.parentNode.$vbox) //@todo - b[0] += this.padding; - } - this.$ext.style.margin = b.join("px ") + "px"; - }, - - "flex" : function(value) { - this.flex = parseInt(value); - if (this.$amlLoaded) - this.parentNode.$resize(true); - } - } - } - - function isFirstVisibleChild(amlNode) { - var firstChild = amlNode.parentNode.firstChild; - while (firstChild && (firstChild.nodeFunc != apf.NODE_VISIBLE - || firstChild.visible === false - || firstChild.visible == 2 && apf.isFalse(firstChild.getAttribute("visible")))) { - firstChild = firstChild.nextSibling; - } - - return firstChild && firstChild == amlNode; - } - - function isLastVisibleChild(amlNode) { - var lastChild = amlNode.parentNode.lastChild; - while (lastChild && (lastChild.nodeFunc != apf.NODE_VISIBLE - || lastChild.visible === false - || lastChild.visible == 2 && apf.isFalse(lastChild.getAttribute("visible")))) { - lastChild = lastChild.previousSibling; - } - - return lastChild && lastChild == amlNode; - } - - //@todo move this to enableTable, disableTable - this.register = function(amlNode, insert) { - if (amlNode.$altExt) //@todo hack, need to re-arch layouting - return; - - amlNode.$propHandlers["left"] = - amlNode.$propHandlers["top"] = - amlNode.$propHandlers["right"] = - amlNode.$propHandlers["bottom"] = apf.K; - - var propHandlers = handlers[apf.hasFlexibleBox]; - for (var prop in propHandlers) { - amlNode.$propHandlers[prop] = propHandlers[prop]; - } - - if (amlNode.nodeFunc == apf.NODE_VISIBLE) { - if (apf.hasFlexibleBox) { - //input elements are not handled correctly by firefox and webkit - if (amlNode.$ext.tagName == "INPUT" || input[amlNode.$ext.tagName]) { - var doc = amlNode.$ext.ownerDocument; - amlNode.$altExt = doc.createElement("div"); - amlNode.parentNode.$int.replaceChild(amlNode.$altExt, amlNode.$ext); - amlNode.$altExt.style.boxSizing = "border-box"; - amlNode.$altExt.appendChild(amlNode.$ext); - - var d = apf.getDiff(amlNode.$ext); - //amlNode.$altExt.style.padding = "0 " + d[0] + "px " + d[1] + "px 0"; - amlNode.$altExt.style.height = "100%"; - amlNode.$altExt.style.width = "0"; - amlNode.$altExt.style.lineHeight = 0; - amlNode.$altExt.style.margin = "-1px 0 0 0"; - amlNode.$ext.style.width = "100%"; - amlNode.$ext.style.height = "100%"; - amlNode.$ext.style.top = "1px"; - amlNode.$ext.style.position = "relative"; - } - else { - if (apf.getStyle(amlNode.$ext, "display") == "inline") - amlNode.$ext.style.display = "block"; //@todo undo - //This is nice for positioning elements in the context of an hbox/vbox - //if (apf.getStyle(amlNode.$ext, "position") == "absolute") - //amlNode.$ext.style.position = "relative"; //@todo undo - } - - amlNode.$ext.style.boxSizing = "border-box"; - } - else { - if (this.$vbox) { - amlNode.$br = this.$int.insertBefore(amlNode.$ext.ownerDocument.createElement("br"), amlNode.$ext.nextSibling); - if (amlNode.visible === false) - amlNode.$br.style.display = "none"; - } - else { - if (amlNode.visible !== false) { - amlNode.$ext.style.display = "inline-block"; - } - this.$int.style.whiteSpace = ""; - amlNode.$ext.style.whiteSpace = apf.getStyle(amlNode.$ext, "whiteSpace") || "normal"; - this.$int.style.whiteSpace = "nowrap"; - } - - this.$int.style.fontSize = "0"; - if (!amlNode.$box) { - var fontSize = apf.getStyle(amlNode.$ext, "fontSize"); - if (fontSize == "0px") { - amlNode.$ext.style.fontSize = ""; - var pNode = this.$int.parentNode; - while (apf.getStyle(pNode, "fontSize") == "0px") { - pNode = pNode.parentNode; - } - fontSize = apf.getStyle(pNode, "fontSize"); - } - amlNode.$ext.style.fontSize = fontSize;//apf.getStyle(amlNode.$ext, "fontSize") || "normal"; - } - - amlNode.addEventListener("resize", resizeHandler); - } - - amlNode.addEventListener("prop.visible", visibleHandler); - - this.$noResize = true; - - if (amlNode.height) - propHandlers.height.call(amlNode, amlNode.height); - if (amlNode.width) - propHandlers.width.call(amlNode, amlNode.width); - if (amlNode.margin) - propHandlers.margin.call(amlNode, amlNode.margin); - if (amlNode.flex) - propHandlers.flex.call(amlNode, amlNode.flex); - - //Ie somehow sets the visible flags in between registration - var isLast = isLastVisibleChild(amlNode); - if (isLast || insert) { - this.$propHandlers["padding"].call(this, this.padding); - this.$propHandlers["align"].call(this, this.align); - - if (!apf.hasFlexibleBox) - this.$propHandlers["pack"].call(this, this.pack); - - if (amlNode.visible !== false) //insert && - removed because for new nodes that are being attached to the tree insert is not set - visibleHandler.call(amlNode, {value: true}); - - //@todo this needs more work - if (insert && amlNode.previousSibling) { - var prev = amlNode.previousSibling; - while (prev && (prev.nodeType != 1 || prev.localName == "splitter")) - prev = prev.previousSibling; - if (prev) - visibleHandler.call(prev, {value: true}); - } - } - - else if (this.splitters && !amlNode.$splitter && amlNode.visible !== false && !amlNode.nosplitter) { - if (amlNode.$ext.nextSibling != (amlNode.nextSibling - && (amlNode.nextSibling.$altExt || amlNode.nextSibling.$ext))) { - var _self = this; - setTimeout(function(){ - _self.insertBefore( - _self.ownerDocument.createElementNS(apf.ns.aml, "splitter"), - amlNode.nextSibling); - }); - } - else { - this.insertBefore( - this.ownerDocument.createElementNS(apf.ns.aml, "splitter"), - amlNode.nextSibling); - } - } - - - delete this.$noResize; - - if (!apf.hasFlexibleBox && isLast) - this.$resize(); - } - } - - this.unregister = function(amlNode) { - if (!amlNode.$propHandlers) - return; - - amlNode.$propHandlers["left"] = - amlNode.$propHandlers["top"] = - amlNode.$propHandlers["right"] = - amlNode.$propHandlers["bottom"] = null; - - var propHandlers = handlers[apf.hasFlexibleBox]; - for (var prop in propHandlers) { - delete amlNode.$propHandlers[prop]; - } - - //Clear css properties and set layout - if (amlNode.nodeFunc == apf.NODE_VISIBLE) { - if (amlNode.flex) { - var flex = amlNode.flex; - propHandlers.flex.call(amlNode, 0); - amlNode.flex = flex; - } - - if (apf.hasFlexibleBox) { - amlNode.$ext.style.boxSizing = ""; - } - else { - amlNode.$ext.style.verticalAlign = ""; - amlNode.$ext.style.textAlign = ""; - amlNode.$ext.style.whiteSpace = ""; - - if (amlNode.$br) { - amlNode.$br.parentNode.removeChild(amlNode.$br); - delete amlNode.$br; - //amlNode.$ext.style.fontSize = ""; - } - - amlNode.removeEventListener("resize", resizeHandler); - } - - amlNode.removeEventListener("prop.visible", visibleHandler); - - amlNode.$ext.style.display = amlNode.visible ? "block" : "none"; - - if (amlNode.margin) - amlNode.$ext.style.margin = ""; - - if (amlNode.width) - amlNode.$ext.style.width = ""; - - - if (this.splitters && !amlNode.$splitter) { - if (amlNode.nextSibling && amlNode.nextSibling.$splitter) - amlNode.nextSibling.removeNode(); - if (isLastVisibleChild(amlNode) && amlNode.previousSibling - && amlNode.previousSibling.$splitter) - amlNode.previousSibling.removeNode(); - } - - } - } - /* - this.addEventListener("DOMNodeInsertedIntoDocument", function(e) { - this.register(this.parentNode); - }); - */ - - // *** DOM Hooks *** // - - this.addEventListener("DOMNodeRemoved", function(e) { - if (e.$doOnlyAdmin || e.currentTarget == this) - return; - - if (e.relatedNode == this) { - this.unregister(e.currentTarget); - //e.currentTarget.$setLayout(); - } - }); - - this.addEventListener("DOMNodeInserted", function(e) { - if (e.currentTarget == this) { - if (this.visible) - this.$ext.style.display = apf.CSS_DISPLAY_FLEX; //Webkit issue - return; - } - - if (e.currentTarget.nodeType != 1 - || e.currentTarget.nodeFunc != apf.NODE_VISIBLE) - return; - - if (e.relatedNode == this && !e.$isMoveWithinParent) { - e.currentTarget.$setLayout(this.localName, true); - - if (e.currentTarget.$altExt) { - - return false; - } - } - }); - - function myVisibleHandler(e) { - if (e.value) - this.$int.style.display = apf.CSS_DISPLAY_FLEX; - } - - function myHeightHandler(e) { - clearInterval(this.$heighttimer); - if (e.value || this.align != "stretch") { - delete this.$heighttimer; - } - else if (!this.$heighttimer) { - var _self = this; - this.$heighttimer = $setInterval(function(){ - if (_self.$amlDestroyed) - return; - - var nodes = _self.childNodes; - for (var $int, i = 0, l = nodes.length; i < l; i++) { - if (!($int = (node = nodes[i]).$int || node.$container)) - continue; - - if (Math.min($int.scrollHeight, node["maxheight"] || 10000) > $int.offsetHeight) - return _self.$resize(true); - } - - if (_self.flex) - clearInterval(this.$heighttimer); - }, this.flex ? 1 : 500); - } - } - - this.$draw = function(){ - var doc = this.$pHtmlNode.ownerDocument; - this.$ext = this.$pHtmlNode.appendChild(doc.createElement("div")); - if (this.getAttribute("style")) - this.$ext.setAttribute("style", this.getAttribute("style")); - this.$ext.className = this.localName; - - this.$vbox = this.localName == "vbox"; - this.$int = !apf.hasFlexibleBox && this.$vbox //@todo reparenting for gecko needs some admin work - ? this.$ext.appendChild(doc.createElement("div")) - : this.$ext; - this.$ext.host = this; - - if (!apf.hasFlexibleBox && this.$vbox) { - this.$int.style.display = "inline-block"; - this.$int.style.width = "100%"; - } - - if (apf.hasFlex) { - this.$display = "-" + apf.CSSPREFIX +"-box"; - - this.$int.style.display = this.$int.style.display || apf.CSS_DISPLAY_FLEX; - this.$int.style.flexDirection = this.localName == "hbox" ? "" : "column"; - this.$int.style.alignItems = "stretch"; - - this.addEventListener("prop.visible", myVisibleHandler); - } - else if (apf.hasFlexibleBox) { - this.$display = "-" + apf.CSSPREFIX +"-box"; - - this.$int.style.display = apf.CSS_DISPLAY_FLEX; - this.$int.style[apf.CSSPREFIX + "BoxOrient"] = this.localName == "hbox" ? "horizontal" : "vertical"; - this.$int.style[apf.CSSPREFIX + "BoxAlign"] = "stretch"; - - this.addEventListener("prop.visible", myVisibleHandler); - } - else { - if (!this.$vbox) { - this.$int.style.whiteSpace = "nowrap"; - this.addEventListener("prop.height", myHeightHandler); - } - - var spacer = (!apf.hasFlexibleBox && this.$vbox ? this.$ext : this.$int) - .appendChild(doc.createElement("strong")); - spacer.style.height = "100%"; - spacer.style.display = "inline-block"; - //spacer.style.marginLeft = "-4px"; - spacer.style.verticalAlign = "middle"; - - this.addEventListener("resize", this.$resize); - } - - if (this.getAttribute("class")) - apf.setStyleClass(this.$ext, this.getAttribute("class")); - - this.$originalMin = [this.minwidth || 0, this.minheight || 0]; - }; - - this.$resize = function(force) { - if (!this.$amlLoaded || this.$noResize) - return; - - //Protection for stretch re-resizing - if (force !== true && this.$lastSize && - this.$lastSize[0] == this.$int.offsetWidth && - this.$lastSize[1] == this.$int.offsetHeight) - return; - - if (!apf.window.vManager.check(this, this.$uniqueId, this.$resize)) - return; - - this.$noResize = true; - this.$lastSize = [this.$int.offsetWidth, this.$int.offsetHeight]; - - //this.$ext.style.border = "1px solid " + (["red", "green", "blue", "orange", "pink", "yellow"])[Math.round(Math.random() * 5)]; - - /*if (this.$table.offsetWidth >= this.$ext.offsetWidth) - this.$ext.style.minWidth = (this.minwidth = Math.max(0, this.$table.offsetWidth - - apf.getWidthDiff(this.$ext))) + "px"; - else { - this.$ext.style.minWidth = ""; - this.minwidth = this.$originalMin[0]; - } - - if (this.$table.offsetHeight >= this.$ext.offsetHeight) - this.$ext.style.minHeight = (this.minheight = Math.max(0, this.$table.offsetHeight - - apf.getHeightDiff(this.$ext))) + "px"; - else { - this.$ext.style.minHeight = ""; - this.minheight = this.$originalMin[1]; - }*/ - - //if (!this.$vbox) alert("here"); - - var total = 0; - var size = this.$vbox ? "width" : "height"; - var minsize = this.$vbox ? "minWidth" : "minHeight"; - var osize = this.$vbox ? "height" : "width"; - var scroll = this.$vbox ? "scrollWidth" : "scrollHeight"; - var offset = this.$vbox ? "offsetWidth" : "offsetHeight"; - var ooffset = this.$vbox ? "offsetHeight" : "offsetWidth"; - var getDiff = this.$vbox ? "getWidthDiff" : "getHeightDiff"; - var ogetDiff = this.$vbox ? "getHeightDiff" : "getWidthDiff"; - var inner = this.$vbox ? "getHtmlInnerWidth" : "getHtmlInnerHeight"; - var oinner = this.$vbox ? "getHtmlInnerHeight" : "getHtmlInnerWidth"; - var borders = this.$vbox ? "getVerBorders" : "getHorBorders"; - - var nodes = this.childNodes, hNodes = [], fW = 0, max = 0; - for (var node, i = 0; i < nodes.length; i++) { - if ((node = nodes[i]).nodeFunc != apf.NODE_VISIBLE || node.visible === false || !node.$amlLoaded) - continue; - - hNodes.push(node); - if (!node[size]) { - //if (!node.$skipResizeOnce) node.$skipResizeOnce = 1; - //else node.$skipResizeOnce++; - //node.$skipResizeOnce = 1 - //node.$ext.style[size] = ""; //@todo this is a sucky way of measuring - var m = node.margin && apf.getBox(node.margin); - if (m && this.$vbox) m.unshift(); - var mdiff = (m ? m[0] + m[2] : 0); - max = Math.max(max, mdiff + Math.min(node.$ext[scroll] + apf[borders](node.$ext), node["max" + size] || 10000)); - } - - if (parseInt(node.flex)) - total += parseFloat(node.flex); - else { - var m = node.margin && apf.getBox(node.margin); - if (m && !this.$vbox) m.shift(); - fW += node.$ext[ooffset] + (m ? m[0] + m[2] : 0); //this.padding + - } - } - if (!max && this[size]) { - max = this[size] - //- (this.$vbox ? this.$edge[0] + this.$edge[2] : this.$edge[1] + this.$edge[3]); - - apf[ogetDiff](this.$ext); - } - - /* - && (this[size] || this.flex) - */ - if (this.align == "stretch") { - //var hasSize = this[size] || this.flex; - var l = hNodes.length; - var pH = max;//this.$int[offset] - apf[getDiff](this.$int);// - (2 * this.padding); - for (var i = 0; i < l; i++) { - node = hNodes[i]; - - if (!node[size] && !this.$vbox || this.$vbox && input[node.$ext.tagName]) { - var m = node.margin && apf.getBox(node.margin); - if (m && this.$vbox) m.unshift(); - var mdiff = (m ? m[0] + m[2] : 0); - - /*shouldClear = !this[size] && !this.flex && node.$ext.offsetHeight == (pH - mdiff); - if (shouldClear) - node.$ext.style[size] = ""; - else - node.$ext.style[size] = Math.max(0, pH - apf[getDiff](node.$ext) - mdiff) + "px"; - node.$setResizeHeight = !shouldClear;*/ - - //!this[size] && !this.flex - if (max && Math.min(node.$ext[scroll], node["max" + size] || 10000) != max) - node.$ext.style[size] = Math.max(0, max - apf[getDiff](node.$ext) - mdiff) + "px"; - else - node.$ext.style[size] = ""; - - /*node.$ext.style[size] = !this[size] && !this.flex && node.$ext.offsetHeight == pH - mdiff - ? "" - : Math.max(0, pH - apf[getDiff](node.$ext) - mdiff) + "px";*/ - } - } - } - - //Flexing - if (total > 0) { - if (this.$vbox) - this.$int.style.height = "100%"; - this.$int.style.overflow = "hidden"; - - var splitterCount = this.$aml.querySelectorAll("splitter").length * 2; - - var rW = this.$int[ooffset] - apf[ogetDiff](this.$int) - fW - - ((hNodes.length - 1 - splitterCount) * this.padding);// - (2 * this.edge); - var lW = rW, done = 0; - for (var i = 0, l = hNodes.length; i < l; i++) { - if ((node = hNodes[i]).flex) { - var v = (i % 2 == 0 ? Math.floor : Math.ceil)((rW / total) * parseInt(node.flex)); - done += parseInt(node.flex); - var m = node.margin && apf.getBox(node.margin); - if (m && !this.$vbox) m.shift(); - node.$ext.style[osize] = Math.max(0, (done == total ? lW : v) - apf[ogetDiff](node.$ext) - (m ? m[0] + m[2] : 0)) + "px"; - lW -= v; - } - } - } - else { - if (this.$vbox) - this.$int.style.height = ""; - this.$int.style.overflow = ""; - } - - this.$noResize = false; - /*this.$noResize = true; - var _self = this; - setTimeout(function(){ - _self.$noResize = false; - });*/ - } - - this.$loadAml = function(x) { - if (this.padding == undefined) - this.padding = 0; - //this.$propHandlers.padding.call(this, this.padding = 0); - if (this.edge == undefined) - this.$propHandlers.edge.call(this, this.edge = 0); - if (this.pack == undefined) - this.$propHandlers.pack.call(this, this.edge = "start"); - if (this.align == undefined) - this.align = "stretch"; - //this.$propHandlers.align.call(this, this.align = "stretch"); - if (!apf.hasFlexibleBox && !this.$vbox && !this.height && this.align == "stretch") - myHeightHandler.call(this, {}); - }; -}).call(apf.vbox.prototype = new apf.GuiElement()); - -apf.hbox.prototype = apf.vbox.prototype; - -apf.aml.setElement("hbox", apf.hbox); -apf.aml.setElement("vbox", apf.vbox); - - -}; -}); \ No newline at end of file diff --git a/plugins/c9.ide.ui/lib/menu/menu.js b/plugins/c9.ide.ui/lib/menu/menu.js deleted file mode 100644 index dc71cabe..00000000 --- a/plugins/c9.ide.ui/lib/menu/menu.js +++ /dev/null @@ -1,1830 +0,0 @@ -define(function(require, module, exports) { -return function(apf) { -var $setTimeout = setTimeout; -var $setInterval = setInterval; - -apf.popup = { - cache : {}, - focusFix : {"INPUT":1,"TEXTAREA":1,"SELECT":1}, - - setContent : function(cacheId, content, style, width, height){ - if (!this.popup) this.init(); - - this.cache[cacheId] = { - content : content, - style : style, - width : width, - height : height - }; - content.style.position = "absolute"; - //if(content.parentNode) content.parentNode.removeChild(content); - //if(style) apf.importCssString(style, this.popup.document); - - content.onmousedown = function(e) { - if (!e) e = event; - - - - //@todo can this cancelBubble just go? - //apf.cancelBubble(e, null, true); - //e.cancelBubble = true; - }; - - return content.ownerDocument; - }, - - removeContent : function(cacheId){ - this.cache[cacheId] = null; - delete this.cache[cacheId]; - }, - - init : function(){ - //consider using iframe - this.popup = {}; - - apf.addEventListener("hotkey", function(e){ - if (e.keyCode == "27" || e.altKey) - apf.popup.forceHide(); - }); - }, - - show : function(cacheId, options){ - if (!this.popup) this.init(); - - options = apf.extend({ - x : 0, - y : 0, - animate : false, - ref : null, - width : null, - height : null, - callback : null, - draggable : false, - resizable : false, - allowTogether: false, - autoCorrect : true, - noleft : false, - setZindex : true - }, options); - - if ((!options.allowTogether - || options.allowTogether !== true && options.allowTogether != this.last) - && this.last != cacheId - && this.cache[this.last] - && (!this.cache[this.last].options || this.cache[this.last].options.autohide !== false)) - this.hide(); - - var o = this.cache[cacheId]; - o.options = options; - - var dp, - popup = o.content, - moveUp = false, - moveLeft = false, - fixed = false; - - if (options.setZindex) - apf.window.zManager.set(options.zindextype || "popup", o.content); - - if ((dp = o.content.style.display) && dp.indexOf("none") > -1) - o.content.style.display = ""; - - var x = options.x; - var y = options.y; - - var refNode = options.ref; - while (refNode && refNode.nodeType == 1) { - if (fixed = apf.getStyle(refNode, "position") == "fixed") - break; - refNode = refNode.parentNode || refNode.$parentNode; - } - - if (!fixed) { - if (refNode) { - var pos = apf.getAbsolutePosition(options.ref, - o.content.offsetParent || o.content.parentNode); - x = (x || 0) + pos[0]; - y = (y || 0) + pos[1]; - } - - if (options.width || o.width) - popup.style.width = ((options.width || o.width) - 3) + "px"; - - popup.style.position = "absolute"; - popup.style.maxHeight = ""; - - var parentMenu = this.cache[options.allowTogether]; - var pOverflow = apf.getOverflowParent(o.content); - var edgeY = (pOverflow == document.documentElement - ? (apf.isIE - ? pOverflow.offsetHeight - : (window.innerHeight + window.pageYOffset)) + pOverflow.scrollTop - : pOverflow.offsetHeight + pOverflow.scrollTop); - moveUp = options.up || options.autoCorrect && (y - + (options.height || o.height || o.content.offsetHeight)) - > edgeY; - - var maxHeight = 0; - if (moveUp) { - var value; - var height = (options.height || o.height || o.content.offsetHeight); - if (options.ref) - value = (pos[1] - height); - else - value = Math.max(0, edgeY - height); - - if (!options.up && value < 0) { - moveUp = false; - popup.style.top = y + "px"; - maxHeight = edgeY - y - this.$screenMargin.bottom - 10; - } - else { - var minTop = this.$screenMargin.top + 3; - maxHeight = (pos ? pos[1] : edgeY) - minTop - 10; - popup.style.top = Math.max(value, minTop) + "px"; - } - - } - else { - popup.style.top = y + "px"; - maxHeight = edgeY - y - this.$screenMargin.bottom - 10; - } - - popup.style.overflowY = "auto"; - popup.style.maxHeight = maxHeight ? maxHeight + "px" : ""; - - if (!options.noleft) { - var edgeX = (pOverflow == document.documentElement - ? (apf.isIE - ? pOverflow.offsetWidth - : (window.innerWidth + window.pageXOffset)) + pOverflow.scrollLeft - : pOverflow.offsetWidth + pOverflow.scrollLeft); - moveLeft = options.autoCorrect && (x - + (options.width || o.width || o.content.offsetWidth)) - > edgeX; - - if (moveLeft) { - var value; - if (options.ref) { - value = (pos[0] - (options.width || o.width || o.content.offsetWidth)) - + (options.ref.offsetWidth); - } - else { - value = (edgeX - (options.width || o.width || o.content.offsetWidth) - - (parentMenu ? (edgeX - parentMenu.content.offsetLeft) : 0)) - + 5; - //parentMenu.width || parentMenu.content.offsetWidth) : 0)); - } - popup.style.left = value < 0 ? x : (value - 1) + "px"; - } - else { - popup.style.left = x + "px"; - } - } - } - else { - pos = apf.getAbsolutePosition(options.ref, refNode); - y = (y || 0) + pos[1] + refNode.offsetTop; - pos[0] += refNode.offsetLeft; - popup.style.position = "fixed"; - popup.style.top = y + "px"; - - if (!options.noleft) - popup.style.left = x + "px"; - } - - - // set a className that specifies the direction, to help skins with - // specific styling options. - apf.setStyleClass(popup, moveUp ? "upward" : "downward", [moveUp ? "downward" : "upward"]); - apf.setStyleClass(popup, moveLeft ? "moveleft" : "moveright", [moveLeft ? "moveright" : "moveleft"]); - - - if (options.animate) { - if (options.animate == "fade") { - apf.tween.single(popup, { - type : 'fade', - from : 0, - to : 1, - anim : apf.tween.NORMAL, - steps : options.steps || 15 * apf.animSteps - }); - } - else { - var iVal, steps = apf.isIE8 ? 5 : 7, i = 0; - iVal = setInterval(function(){ - var value = ++i * ((options.height || o.height) / steps); - - popup.style.height = value + "px"; - if (moveUp) - popup.style.top = (y - value - (options.y || 0)) + "px"; - else - (options.container || popup).scrollTop = -1 * (i - steps) * ((options.height || o.height) / steps); - popup.style.display = "block"; - - if (i >= steps) { - clearInterval(iVal) - - if (options.callback) - options.callback(popup); - } - }, 10); - } - } - else { - if (!refNode) { - if (options.height || o.height) - popup.style.height = (options.height || o.height) + "px"; - value = (edgeY - (options.height || o.height || o.content.offsetHeight)); - popup.style.top = y + (options.height || o.height || o.content.offsetHeight) < edgeY - ? y - : value - + "px"; - } - popup.style.display = "block"; - - if (options.callback) - options.callback(popup); - } - - $setTimeout(function(){ - apf.popup.last = cacheId; - }); - - if (options.draggable) { - options.id = cacheId; - this.makeDraggable(options); - } - }, - - hide : function(){ - if (this.isDragging) return; - - var o = this.cache[this.last]; - if (o) { - if (o.content) - o.content.style.display = "none"; - - if (o.options && o.options.onclose) { - o.options.onclose(apf.extend(o.options, {htmlNode: o.content})); - o.options.onclose = false; - } - } - }, - - isShowing : function(cacheId){ - return this.last && this.last == cacheId - && this.cache[this.last] - && this.cache[this.last].content.style.display != "none"; - }, - - isDragging : false, - - makeDraggable: function(options) { - if (!apf.Interactive || this.cache[options.id].draggable) - return; - - var oHtml = this.cache[options.id].content; - this.cache[options.id].draggable = true; - var o = { - $propHandlers : {}, - minwidth : 10, - minheight : 10, - maxwidth : 10000, - maxheight : 10000, - dragOutline : false, - resizeOutline : false, - draggable : true, - resizable : options.resizable, - $ext : oHtml, - oDrag : oHtml.firstChild - }; - - oHtml.onmousedown = - oHtml.firstChild.onmousedown = function(e){ - if (!e) e = event; - - - - (e || event).cancelBubble = true; - } - - apf.implement.call(o, apf.Interactive); - - o.$propHandlers["draggable"].call(o, true); - o.$propHandlers["resizable"].call(o, true); - }, - - getCurrentElement : function(){ - return typeof this.last == "number" && apf.lookup(this.last); - }, - - $mousedownHandler : function(amlNode, e){ - if (!this.last || (amlNode && this.last == amlNode.$uniqueId) || !this.cache[this.last]) - return; - - var htmlNode = e.srcElement || e.target; - - var uId = this.last; - - while (this.cache[uId]) { - if (apf.isChildOf(this.cache[uId].content, htmlNode, true)) - return; - - if (!this.cache[uId].options) - return; - - uId = this.cache[uId].options.allowTogether; - } - - this.forceHide(); - }, - - forceHide : function(){ - if (document.body.classList.contains("noInput")) return; - - if (this.last - - && !apf.plane.current - - && this.isShowing(this.last) - && this.cache[this.last] - && this.cache[this.last].options - && this.cache[this.last].options.autohide !== false) { - var o = apf.lookup(this.last); - if (!o) - this.last = null; - else if (o.dispatchEvent("popuphide") !== false) - this.hide(); - } - }, - - destroy : function(){ - for (var cacheId in this.cache) { - if (this.cache[cacheId]) { - this.cache[cacheId].content.onmousedown = null; - apf.destroyHtmlNode(this.cache[cacheId].content); - this.cache[cacheId].content = null; - this.cache[cacheId] = null; - } - } - - if (!this.popup) return; - //this.popup.document.body.c = null; - //this.popup.document.body.onmouseover = null; - }, - - setMargin: function(m) { - for (var i in this.$screenMargin) - if (i in m) - this.$screenMargin[i] = m[i] || 0; - }, - $screenMargin: {top: 0, left: 0, right: 0, bottom: 0} -}; - - - - - - -/** - * This element displays a skinnable menu of items which can be choosen. - * - * Based on the context of the menu, items can be shown and hidden. - * - * - * #### Example - * - * ```xml, demo - * - * - * - * Tutorials - * Contact - * - * - * Tutorials - * - * Visit Ajax.org - * Exit - * - * - * - * - * File - * Edit - * - * - * - * - * - * ``` - * - * @class apf.menu - * @define menu - * @selection - * @allowchild item, divider, check, radio - * - * @author Ruben Daniels (ruben AT ajax DOT org) - * @version %I%, %G% - * @since 0.4 - * - * @inherits apf.Presentation - */ -/** - * @event display Fires when the contextmenu is shown. - */ -/** - * @event itemclick Fires when a user presses the mouse button while over a child of this element. - * @param {Object} e The standard event object. The following property is available: - * - `value` ([[String]]): the value of the clicked element. - * - */ -apf.menu = function(struct, tagName){ - this.$init(tagName || "menu", apf.NODE_VISIBLE, struct); - - this.animate = apf.enableAnim; -}; - -(function(){ - this.$focussable = apf.MENU; - this.$positioning = "basic" - //var _self = this; - //var blurring = false; - - // *** Properties and Attributes *** // - - //this.zindex = 10000000; - this.visible = false; - this.matchhide = false; - - this.$booleanProperties["animate"] = true; - this.$booleanProperties["pinned"] = true; - this.$booleanProperties["sticky"] = true; - this.$booleanProperties["matchhide"] = true; - - this.$propHandlers["visible"] = function(value, prop, force, nofocus, hideOpener){ - if (!this.$ext) - return; - - if (value) { - this.$ext.style.display = "block"; - if (this.opener && this.opener.localName.indexOf('item') > -1) - this.opener.parentNode.$showingSubMenu = this; - } - else { - this.$ext.style.display = "none"; - - var lastFocus = apf.menu.lastFocussed; - var opener = this.opener; - //@todo test this with a list being the opener of the menu - if (lastFocus != this.opener && this.opener && this.opener.$blur) - this.opener.$blur(); - - if (this.opener && this.opener.parentNode && this.opener.parentNode.localName == "menu") { - if (!this.$hideTree) - this.$hideTree = -1 - this.opener.parentNode.focus(); - } - - - else if (lastFocus) { - //We're being hidden because some other object gets focus - if (apf.window.$settingFocus) { - if (apf.window.$settingFocus != lastFocus && lastFocus.$blur) - lastFocus.$blur(); - this.$blur(); - - if (apf.window.$settingFocus.localName != "menu") //not menu walking - apf.menu.lastFocussed = null; - } - //We're being hidden because window looses focus - - //We're just being hidden - else if (this.$hideTree) { - if (!this.$hideTree) - this.$hideTree = -1 - - var visTest = (lastFocus.disabled || lastFocus.$ext - && !lastFocus.$ext.offsetHeight) // || !lastFocus.visible - && lastFocus != apf.document.documentElement; - - if (nofocus || visTest) { - if (lastFocus.$blur) - lastFocus.$blur(); - this.$blur(); - apf.document.activeElement = null; - - if (visTest && apf.window.moveNext() === false) - apf.window.$focusRoot(); - } - else { - lastFocus.focus(null, null, true); - } - - apf.menu.lastFocussed = null; - } - } - - - clearTimeout(this.$submenuTimer); - - if (this.$showingSubMenu) { - this.$showingSubMenu.hide(); - this.$showingSubMenu = null; - } - - if (this.opener && this.opener.$submenu) { - this.opener.$submenu(true, true); - - //@todo problem with loosing focus when window looses focus - if (this.$hideTree === true && this.opener - && this.opener.parentNode - && this.opener.parentNode.localName == "menu" - && this.opener.parentNode.$hideTree != -1) { - this.opener.parentNode.$hideTree = true - this.opener.parentNode.hide(); - } - - this.opener = null; - } - this.$hideTree = null; - - if (this.$selected) { - apf.setStyleClass(this.$selected.$ext, "", ["hover"]); - this.$selected = null; - } - - this.dispatchEvent("hide", {opener: opener}); - } - }; - - // *** Public Methods *** // - - var lastFocus; - - /** - * Shows the menu, optionally within a certain context. - * @param {Number} x The left position of the menu. - * @param {Number} y The top position of the menu. - * @param {Boolean} noanim Whether to animate the showing of this menu. - * @param {apf.AmlElement} opener The element that is the context of this menu. - * @param {XMLElement} xmlNode The {@link term.datanode data node} that provides data context to the menu child nodes. - * @see apf.GuiElement@contextmenu - */ - this.display = function(x, y, noanim, opener, xmlNode, openMenuId, btnWidth){ - this.opener = opener; - - var lastFocus; - if (!apf.menu.lastFocussed) - lastFocus = apf.menu.lastFocussed = apf.menu.lastFocussedItem; - - //Show / hide Child Nodes Based on XML - if (xmlNode && !this.disabled) { - var last, i, node, - nodes = this.childNodes, - c = 0, - l = nodes.length, result; - for (i = 0; i < l; i++) { - node = nodes[i]; - if (node.nodeType != 1 || node.localName != "item") - continue; - - result = !xmlNode || !node.match || (node.cmatch || (node.cmatch = apf.lm.compile(node.match, { - xpathmode : 3, - injectself : true - })))(xmlNode) - - if (result) { - if (this.matchhide) - node.show(); - else - node.enable(); - - if (node.localName == "divider" && this.matchhide) { - last = node; - if (c == 0) - node.hide(); - c = 0; - } - else c++; - } - else { - if (this.matchhide) - node.hide(); - else - node.disable(); - - if (!node.nextSibling && c == 0 && last) - last.hide(); - } - } - } - - if (this.oOverlay) { - if (btnWidth) { - this.oOverlay.style.display = "block"; - this.oOverlay.style.width = btnWidth + "px"; - } - else - this.oOverlay.style.display = "none"; - } - - function afterRender(){ - if (x === null) { - apf.popup.show(this.$uniqueId, { - x : 0, - y : this.ref ? 0 : opener.$ext.offsetHeight, - animate : noanim || !this.animate ? false : "fade", - steps : 10, - ref : (this.ref || opener).$ext, - allowTogether: openMenuId, - autohide : !this.pinned, - noleft : this.left !== undefined, - setZindex : this.zindex ? false : true, - up : (this.ref || opener).submenudir == "up" - }); - } - else { - //var bodyPos = apf.getAbsolutePosition(document.body); - apf.popup.show(this.$uniqueId, { - x : x, - y : y - (apf.isIE && apf.isIE < 8 ? 1 : 0), - animate : noanim || !this.animate ? false : "fade", - steps : 10, - //ref : this.$ext.offsetParent, - allowTogether: openMenuId, - autohide : !this.pinned, - setZindex : this.zindex ? false : true - //autoCorrect : false - }); - } - - // var lastFocus = - // apf.menu.lastFocus = opener && opener.$focussable === true - // ? opener - // : apf.menu.lastFocus || apf.document.activeElement; - - apf.popup.last = null; - - //if (!apf.isGecko) //This disables keyboard support for gecko - very strange behaviour - this.focus(); - - //Make the component that provides context appear to have focus - // second argument is needed for ace tree - if (lastFocus && lastFocus != this && lastFocus.$focus) - lastFocus.$focus(null, {fromContextMenu: true}); - - this.xmlReference = xmlNode; - - //@todo consider renaming this to onshow and onhide - this.dispatchEvent("display", {opener: opener}); - } - - this.visible = false; - - if (!this.parentNode) - apf.document.documentElement.appendChild(this); - - if (this.$rendered !== false) { - this.show(); - afterRender.call(this); - } - else { - this.addEventListener("afterrender", afterRender); - this.show(); - } - }; - - /** - * Returns the current group value of this element. - * @return {String} The current selected value. - */ - this.getValue = function(group){ - return this.getSelected(group).value || ""; - }; - - /** - * Retrieves the selected element from a group of radio elements. - * @param {String} group The name of the group. - * @return {apf.radiobutton} The selected radio element. - */ - this.getSelected = function(group){ - var nodes = this.childNodes; - var i, l = nodes.length; - for (i = 0; i < l; i++) { - if (nodes[i].group != group) - continue; - - if (nodes[i].selected) - return nodes[i]; - } - - return false; - }; - - /** - * Selects an element within a radio group. - * @param {String} group The name of the group. - * @param {String} value The value of the item to select. - */ - this.select = function(group, value){ - var nodes = this.childNodes; - var i, l = nodes.length; - for (i = 0; i < l; i++) { - if (nodes[i].group != group) - continue; - - if (value && (nodes[i].value == value || !nodes[i].value && nodes[i].caption == value)) - nodes[i].setProperty("selected", true, false, true); - //nodes[i].$handlePropSet("selected", true); - else if (nodes[i].selected) - nodes[i].setProperty("selected", false, false, true); - //nodes[i].$handlePropSet("selected", false); - } - }; - - // *** Events *** // - - - this.addEventListener("prop.visible", function() { - this.$initChildren(); - }, true); - - this.addEventListener("keydown", function(e){ - var node, key = e.keyCode; - //var ctrlKey = e.ctrlKey; - //var shiftKey = e.shiftKey; - - switch (key) { - case 13: - if (!this.$selected) - return; - - node = this.$selected; - node.$down(); - node.$up(); - node.$click(); - break; - case 27: - this.hide(); - break; - case 38: - //UP - node = this.$selected && this.$selected.previousSibling - || this.lastChild; - - if (node && node.localName == "divider") - node = node.previousSibling; - - if (!node) - return; - - if (this.$selected) - apf.setStyleClass(this.$selected.$ext, "", ["hover"]); - - apf.setStyleClass(node.$ext, "hover"); - this.$selected = node; - break; - case 40: - //DOWN - node = this.$selected && this.$selected.nextSibling - || this.firstChild; - - if (node && node.localName == "divider") - node = node.nextSibling; - - if (!node) - return; - - if (this.$selected) - apf.setStyleClass(this.$selected.$ext, "", ["hover"]); - - apf.setStyleClass(node.$ext, "hover"); - this.$selected = node; - break; - case 37: - //LEFT - //if (this.$selected && this.$selected.submenu) - //this.$selected.$submenu(true, true); - - if (!this.opener) - return; - - if (this.opener.localName == "button") { - node = this.opener.previousSibling; - while(node && !node.submenu) - node = node.previousSibling; - - if (node) { - node.dispatchEvent("mouseover"); - - var btnMenu = node.parentNode.menuIsPressed; - if (btnMenu) { - self[btnMenu.submenu].dispatchEvent("keydown", { - keyCode : 40 - }); - } - } - } - else if (this.opener.parentNode.localName == "menu") { - //@todo Ahum bad abstraction boundary - var op = this.opener; - this.hide(); - apf.setStyleClass(op.$ext, "hover"); - op.parentNode.$showingSubMenu = null; - } - - break; - case 39: - //RIGHT - if (this.$selected && this.$selected.submenu) { - this.$selected.$submenu(null, true); - this.$showingSubMenu.dispatchEvent("keydown", { - keyCode : 40 - }); - - return; - } - - if (this.opener) { - var op = this.opener; - while (op && op.parentNode && op.parentNode.localName == "menu") - op = op.parentNode.opener; - - if (op && op.localName == "button") { - node = op.nextSibling; - while(node && !node.submenu) - node = node.nextSibling; - - if (node) { - node.dispatchEvent("mouseover"); - - var btnMenu = node.parentNode.menuIsPressed; - if (btnMenu) { - (self[btnMenu.submenu] || btnMenu.submenu).dispatchEvent("keydown", { - keyCode : 40 - }); - } - - return; - } - } - } - - if (!this.$selected) { - arguments.callee.call(this, { - keyCode : 40 - }); - } - - break; - default: - return; - } - - return false; - }, true); - - - //Hide menu when it looses focus or when the popup hides itself - function forceHide(e){ - if (this.$showingSubMenu || this.pinned - || apf.isChildOf(e.fromElement, e.toElement) - || apf.isChildOf(e.toElement, e.fromElement) - || apf.isChildOf(this, e.toElement) - || (e.name !== "popuphide" && !e.toElement) - || e.toElement && apf.popup.cache[e.toElement.$uniqueId]) - return; - - if (this.$hideTree != -1) { - this.$hideTree = true; - this.hide(); - } - - return false; - } - - this.addEventListener("focus", function(e){ - apf.popup.last = this.$uniqueId; - - if (!apf.menu.lastFocussed) - apf.menu.lastFocussed = apf.menu.lastFocussedItem; - }); - - this.addEventListener("blur", forceHide); - this.addEventListener("popuphide", forceHide); - - // *** Init *** // - - this.$draw = function(){ - this.$pHtmlNode = document.body; - - //Build Main Skin - this.$ext = this.$getExternal(); - this.oOverlay = this.$getLayoutNode("main", "overlay", this.$ext); - - apf.popup.setContent(this.$uniqueId, this.$ext, "", null, null); - - // workaround for a chrome bug where clicking on shadow clciks on contents of overflown element - this.$ext.addEventListener("mouseup", function(e) { - var rect = this.getBoundingClientRect(); - if (e.clientY > rect.bottom) { - e.stopPropagation(); - e.preventDefault(); - } - }, true); - }; - - this.$loadAml = function(x){ - this.$int = this.$getLayoutNode("main", "container", this.$ext); - }; - - this.$destroy = function(){ - apf.popup.removeContent(this.$uniqueId); - }; - - this.$initChildren = function() { - this.childNodes.forEach(function(amlNode) { - if (!amlNode.$amlLoaded) - amlNode.dispatchEvent("DOMNodeInsertedIntoDocument"); - }); - }; - var insertBefore = this.insertBefore; - this.insertBefore = function(node, beforeNode) { - // if menu is visible call apf insertBefore since it rearranges html nodes - // otherwise use fake and much faster method - if (this.visible) - return insertBefore.call(this, node, beforeNode); - if (beforeNode == node) - return node; - if (!this || this == node) - throw new Error("Invalid insertBefore call"); - - var children = this.childNodes; - // if (node.parentNode == this) - // children[index] - if (node.parentNode) - node.removeNode(); - - var index = beforeNode ? children.indexOf(beforeNode) : children.length; - node.parentNode = this; - - if (beforeNode) { - children.splice(index, 0, node); - } else { - children.push(node); - } - - node.previousSibling = children[index - 1]; - node.nextSibling = children[index + 1]; - if (node.previousSibling) - node.previousSibling.nextSibling = node; - else - this.firstChild = children[0]; - - if (node.nextSibling) - node.nextSibling.previousSibling = node; - else - this.lastChild = children[this.childNodes.length - 1]; - - return node; - }; - - this.appendChild = function(node) { - return this.insertBefore(node); - }; -}).call(apf.menu.prototype = new apf.Presentation()); - -apf.addEventListener("movefocus", function(e){ - var next = e.toElement; - if (next && next.localName != "menu") - apf.menu.lastFocussedItem = next; -}); - -apf.aml.setElement("menu", apf.menu); - - - -/** - * Element displaying a divider. For use in toolbars, menus, and such. - * @class apf.divider - * @define divider - * @inherits apf.Presentation - */ -apf.divider = function(struct, tagName){ - this.$init(tagName || "divider", apf.NODE_VISIBLE, struct); -}; - -(function() { - this.$focussable = false; - - this.minwidth = 0; - this.minheight = 0; - - this.implement(apf.ChildValue); - this.$childProperty = "caption"; - - //@todo apf3.0 fix this - this.addEventListener("AMLReparent", function(beforeNode, pNode, withinParent){ - if (!this.$amlLoaded) - return; - - if (!withinParent && this.skinName != pNode.skinName) { - //@todo for now, assuming dom garbage collection doesn't leak - this.loadAml(); - } - }); - - /** - * @attribute {String} caption the text displayed in the area defined by this - * element. - */ - this.$supportedProperties.push("caption", "value", "for", "textalign"); - this.$propHandlers["caption"] = function(value){ - if (this.$caption) { - this.$setStyleClass(this.$ext, this.$baseCSSname + "Caption"); - this.$caption.innerHTML = value; - } - else { - this.$setStyleClass(this.$ext, "", [this.$baseCSSname + "Caption"]); - } - }; - - this.$canLeechSkin = true; - - /** - * @private - */ - this.$draw = function() { - if (this.parentNode.isPaged && this.parentNode.$buttons) - this.$pHtmlNode = this.parentNode.$buttons; - - if (this.$isLeechingSkin) { - this.$ext = apf.insertHtmlNode( - this.parentNode.$getLayoutNode("divider"), this.$pHtmlNode); - } - else { - this.$ext = this.$getExternal("main"); - this.$caption = this.$getLayoutNode("main", "caption", this.$ext); - } - }; -}).call(apf.divider.prototype = new apf.Presentation()); - -apf.aml.setElement("divider", apf.divider); - - - - - - -/** - * Represents an item in a menu, displaying a clickable area. - * - * #### Example - * - * ```xml, demo - * - * - * - * Tutorials - * Contact - * - * - * - * File - * - * - * - * - * ``` - * - * @class apf.item - * @selection - * @define item - * @inherits apf.Presentation - * - */ -/** - * @event click Fires when a user presses the mouse button while over this element. - * @param {Object} e The standard event object. It contains the following properties: - * - xmlContext ([[XMLElement]]): The XML data node that was selected in the opener at the time of showing the context menu. - * - opener ([[apf.AmlElement]]): The element that was clicked upon when showing the context menu. - */ -apf.item = function(struct, tagName){ - this.$init(tagName || "item", apf.NODE_VISIBLE, struct); -}; - -(function(){ - this.$focussable = false; - this.$childProperty = "caption"; - this.$canLeechSkin = "item"; - - this.checked = false; - this.selected = false; - - this.implement(apf.ChildValue); - - // *** Properties and Attributes *** // - - //1 = force no bind rule, 2 = force bind rule - this.$attrExcludePropBind = apf.extend({ - "match" : 1 - }, this.$attrExcludePropBind); - - this.$booleanProperties["checked"] = true; - this.$booleanProperties["selected"] = true; - - this.$supportedProperties.push("submenu", "value", "match", "group", "icon", - "checked", "selected", "disabled", "caption", - "type", "values"); - - /** - * @attribute {String} submenu Sets or gets the id of the menu that is shown - * when the user hovers over this menu item. - * - * #### Example - * - * ```xml - * - * test - * test2 - * - * - * - * Sub menu - * - * - * - * - * File - * - * - * ``` - */ - this.$propHandlers["submenu"] = function(value){ - apf.setStyleClass(this.$ext, "submenu"); - } - - /** - * @attribute {String} value Sets or gets the value of this element. - */ - - /** - * @attribute {String} [select] Sets or gets the XPath statement which works on the - * XML context of the parent menu element to determine whether this - * item is shown. - * - * #### Example - * - * This example shows a list: - * - * ```xml - * - * Send an E-mail - * Call Number - * - * Remove - * - * View Pictures - * - * - * - * Reboot - * - * - * - * Please right-click on this plane - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * ``` - */ - this.$propHandlers["select"] = function(value){ - this.select = value - ? "self::" + value.split("|").join("|self::") - : value; - } - - /** - * @attribute {String} [group] Sets or gets the name of the group this item belongs - * to. - * - * #### Example - * - * ```xml - * - * item 1 - * item 2 - * item 3 - * item 4 - * - * ``` - */ - this.$propHandlers["group"] = function(value){ - if (this.$group && this.$group.$removeRadio) - this.$group.$removeRadio(this); - - if (!value) { - this.$group = null; - return; - } - - var group = typeof value == "string" - ? - - apf.nameserver.get("group", value) - - : value; - if (!group) { - - group = apf.nameserver.register("group", value, - new apf.$group()); - group.setAttribute("id", value); - group.dispatchEvent("DOMNodeInsertedIntoDocument"); - group.parentNode = this; - - } - this.$group = group; - - this.$group.$addRadio(this); - }; - - - /** - * @attribute {String} hotkey Sets or gets the key combination a user can press - * to active the function of this element. Use any combination of - * [[keys: Ctrl]], [[keys: Shift]], [[keys: Alt]], [[keys: F1]]-[[keys: F12]], and alphanumerical characters. Use a - * space, a minus or plus sign as a seperator. - * - * #### Example - * - * ```xml - * Quit - * ``` - */ - this.$propHandlers["hotkey"] = function(value){ - if (!this.$amlLoaded) { - var _self = this; - this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ - if (_self.$hotkey && _self.hotkey) - apf.setNodeValue(this.$hotkey, apf.isMac - ? apf.hotkeys.toMacNotation(_self.hotkey) : _self.hotkey); - }); - } - else if (this.$hotkey) - apf.setNodeValue(this.$hotkey, apf.isMac ? apf.hotkeys.toMacNotation(value) : value); - - if (this.$lastHotkey) { - apf.hotkeys.remove(this.$lastHotkey[0], this.$lastHotkey[1]); - delete this.$lastHotkey[0]; - } - - if (value) { - this.$lastHotkey = [value]; - var _self = this; - apf.hotkeys.register(value, this.$lastHotkey[1] = function(){ - if (_self.disabled || !_self.visible) - return; - - //hmm not very scalable... - if (_self.parentNode) { - var buttons = apf.document.getElementsByTagNameNS(apf.ns.aml, "button"); - for (var i = 0; i < buttons.length; i++) { - if (buttons[i].submenu == _self.parentNode.name) { - var btn = buttons[i]; - btn.$setState("Over", {}); - - $setTimeout(function(){ - btn.$setState("Out", {}); - }, 200); - - break; - } - } - } - - _self.$down(); - _self.$up(); - _self.$click(); - }); - } - } - - /** - * @attribute {String} icon Sets or gets the URL of the image used as an icon or - * a reference to an iconmap. - */ - this.$propHandlers["icon"] = function(value){ - if (this.$icon) - apf.skins.setIcon(this.$icon, value, this.parentNode.iconPath); - } - - /** - * @attribute {String} caption Sets or gets the text displayed on the item. - */ - this.$propHandlers["caption"] = function(value){ - if (this.$caption) - apf.setNodeValue(this.$caption, value); - } - - /** - * @attribute {String} type Sets or gets the function of this item. - * - * Possible values include: - * - `"item"` - * - `"check"` - * - `"radio"` - */ - this.$propHandlers["type"] = function(value){ - apf.setStyleClass(this.$ext, value, ["item", "check", "radio"]); - } - - this.$propHandlers["values"] = function(value){ - this.$values = typeof value == "string" - ? value.split("\|") - : (value || [1, 0]); - - this.$propHandlers["value"].call(this, this.value); - }; - - this.$propHandlers["value"] = function(value){ - if (this.type != "check") - return; - - value = (typeof value == "string" ? value.trim() : value); - - var checked; - if (this.$values) { - checked = (typeof value != "undefined" && value !== null - && value.toString() == this.$values[0].toString()); - } - else { - checked = apf.isTrue(value); - } - - if (checked != this.checked) { - this.checked = checked; - this.$propHandlers.checked.call(this, checked); - } - }; - - /** - * @attribute {Boolean} checked Sets or gets whether the item is checked. - */ - this.$propHandlers["checked"] = function(value){ - if (this.type != "check") - return; - - if (apf.isTrue(value)) - apf.setStyleClass(this.$ext, "checked"); - else - apf.setStyleClass(this.$ext, "", ["checked"]); - - if (!this.$values) { - if (this.getAttribute("values")) - this.$propHandlers["values"].call(this, this.getAttribute("values")); - else - this.$values = [true, false]; - } - - if(this.$values && this.$values[value ? 0 : 1] != this.value) - this.setProperty("value", this.$values ? this.$values[value ? 0 : 1] : true); - } - - this.select = function(){ - this.parentNode.select(this.group, this.value || this.caption); - } - - this.check = function(){ - this.setProperty("value", this.$values - ? this.$values[0] - : true); - } - this.uncheck = function(){ - this.setProperty("value", this.$values - ? this.$values[1] - : false); - } - - this.$check = function(){ - apf.setStyleClass(this.$ext, "selected"); - } - - this.$uncheck = function(){ - apf.setStyleClass(this.$ext, "", ["selected"]); - } - - /** - * @attribute {Boolean} selected Sets or gets whether the item is selected. - */ - this.$propHandlers["selected"] = function(value){ - if (this.type != "radio") - return; - - - if (apf.isTrue(value)) { - if (this.$group) - this.$group.setProperty("value", this.value); - this.$check(); - } - else - this.$uncheck(); - } - - /** - * @attribute {Boolean} disabled Sets or gets whether the item is active. - */ - this.$propHandlers["disabled"] = function(value){ - if (apf.isTrue(value) || value == -1) - apf.setStyleClass(this.$ext, "disabled"); - else - apf.setStyleClass(this.$ext, "", ["disabled"]); - } - - // *** Dom Hooks *** // - - //@todo apf3.0 - this.addEventListener("AMLReparent", function(beforeNode, pNode, withinParent){ - if (!this.$amlLoaded) - return; - - if (!withinParent && this.skinName != pNode.skinName) { - //@todo for now, assuming dom garbage collection doesn't leak - this.loadAml(); - } - }); - - // *** Events *** // - - this.$down = function(){ - - }; - - this.$up = function(){ - - - if (this.type == "radio") - this.parentNode.select(this.group, this.value || this.caption); - - else if (this.type == "check") { - this.setProperty("checked", !this.checked); - //this.$handlePropSet("checked", !this.checked); - } - - if (this.submenu) { - this.$over(null, true); - return; - } - - this.parentNode.$hideTree = true; - - //@todo This statement makes the menu loose focus. - if (!this.parentNode.sticky) - this.parentNode.hide();//true not focus?/ - - this.parentNode.dispatchEvent("itemclick", { - value : this.value || this.caption, - relatedNode : this, - checked : this.checked, - selected : this.selected - }); - - //@todo Anim effect here? - - this.dispatchEvent("click", { - xmlContext : this.parentNode.xmlReference, - opener : this.parentNode.opener - }); - - - }; - - this.$click = function(){ - - }; - - var timer; - this.$out = function(e){ - if (apf.isChildOf(this.$ext, e.toElement || e.explicitOriginalTarget) - || apf.isChildOf(this.$ext, e.srcElement || e.target)) //@todo test FF - return; - - clearTimeout(timer); - if (!this.submenu || this.$submenu(true)) { - apf.setStyleClass(this.$ext, "", ['hover']); - - var sel = this.parentNode.$selected; - if (sel && sel != this) - apf.setStyleClass(sel.$ext, "", ["hover"]); - - this.parentNode.$selected = null; - } - - - }; - - this.$over = function(e, force){ - function selectItem(el) { - if (el.parentNode.$selected == el && e) - return false; - - if (el.parentNode.$selected) - apf.setStyleClass(el.parentNode.$selected.$ext, "", ["hover"]); - - apf.setStyleClass(el.$ext, "hover"); - el.parentNode.$selected = el; - return true; - } - - if (!selectItem(this)) - return; - - var opener = this.parentNode.opener; - if (opener && opener.parentNode.$showingSubMenu) - selectItem(opener); - - if (!force && (apf.isChildOf(this.$ext, e.toElement || e.explicitOriginalTarget) - || apf.isChildOf(this.$ext, e.fromElement || e.target))) //@todo test FF - return; - - var _self = this, ps = this.parentNode.$showingSubMenu; - if (ps && ps.name && ps.name == this.submenu) { - this.parentNode.$selected = null; - this.parentNode.$showingSubMenu = null; - _self.$submenu(); - return; - } - - clearTimeout(timer); - - - function submenu(){ - if (ps && ps.visible) { - ps.hide(); - - if (_self.parentNode.$showingSubMenu == ps) - _self.parentNode.$showingSubMenu = null; - } - if (_self.submenu && (!_self.parentNode.opener - || _self.parentNode.opener.visible)) - _self.$submenu(); - } - - if (force) - submenu(); - else { - timer = $setTimeout(function(){ - submenu(); - timer = null; - }, 210); - } - }; - - this.$submenu = function(hide, force){ - if (!this.submenu) - return true; - - var menu = self[this.submenu] || this.submenu; - if (!menu) { - - - return; - } - - if (!hide) { - //if (this.parentNode.showingSubMenu == this.submenu) - //return; - - this.parentNode.$showingSubMenu = menu; - - var pos = apf.getAbsolutePosition(this.$ext, this.parentNode.$ext.offsetParent); - menu.display(pos[0] + this.$ext.offsetWidth - 3, - pos[1] + 3, true, this, - this.parentNode.xmlReference, this.parentNode.$uniqueId); - menu.setAttribute("zindex", (this.parentNode.zindex || this.parentNode.$ext.style.zIndex || 1) + 1); - } - else { - if (menu.visible && !force) { - return false; - } - - if (this.parentNode.$showingSubMenu == menu) - this.parentNode.$showingSubMenu = null; - - apf.setStyleClass(this.$ext, '', ['hover']); - menu.hide(); - return true; - } - }; - - // *** Init *** // - - this.$draw = function(isSkinSwitch){ - var p = this.parentNode; - while (p.$canLeechSkin == "item") - p = p.parentNode; - - if (p.hasFeature(apf.__MULTISELECT__)) { - var _self = this; - - //@todo DOMNodeInserted should reset this - //@todo DOMNodeRemoved should reset this - if (!this.$hasSetSkinListener) { - var f; - this.parentNode.addEventListener("$skinchange", f = function(){ - if (_self.$amlDestroyed) //@todo apf3.x - return; - - if (_self.$ext.parentNode) - this.$deInitNode(_self, _self.$ext); - - var oInt = p == _self.parentNode ? p.$container : _self.parentNode.$container; - var node = oInt.lastChild;//@todo this should be more generic - p.$add(_self, _self.getAttribute(apf.xmldb.xmlIdTag) + "|" + this.$uniqueId, - _self.parentNode, oInt != p.$container && oInt, null); - p.$fill(); - - if (p.$isTreeArch) { - _self.$container = p.$getLayoutNode("item", "container", - _self.$ext = node && node.nextSibling || oInt.firstChild);//@todo this should be more generic - } - else _self.$ext = node && node.nextSibling || oInt.firstChild; - - var ns = _self; - while((ns = ns.nextSibling) && ns.nodeType != 1); - - if (!ns || ns.$canLeechSkin != "item") - p.dispatchEvent("afterload"); - }); - this.addEventListener("DOMNodeRemoved", function(e){ - if (e.currentTarget == this) - this.parentNode.removeEventListener("$skinchange", f); - }); - - this.$hasSetSkinListener = true; - } - - if (!p.$itemInited) { - p.canrename = false; //@todo fix rename - p.$removeClearMessage(); //@todo this should be more generic - p.$itemInited = [p.getTraverseNodes, p.getFirstTraverseNode, p.getTraverseParent]; - - p.getTraverseNodes = function(xmlNode){ - return (xmlNode || p).getElementsByTagNameNS(apf.ns.apf, "item"); - } - p.getFirstTraverseNode = function(xmlNode){ - return (xmlNode || p).getElementsByTagNameNS(apf.ns.apf, "item")[0]; - } - p.getTraverseParent = function(xmlNode){ - return xmlNode && xmlNode.parentNode; - } - p.each = (this.prefix ? this.prefix + ":" : "") + "item"; - - //@todo this is all an ugly hack (copied to baselist.js line 868) - p.$preventDataLoad = true;//@todo apf3.0 add remove for this - - p.$initingModel = true; - p.$setDynamicProperty("icon", "[@icon]"); - p.$setDynamicProperty("image", "[@image]"); - p.$setDynamicProperty("caption", "[label/text()|@caption|text()]"); - p.$setDynamicProperty("eachvalue", "[value/text()|@value|text()]"); - p.$canLoadDataAttr = false; - - if (!p.xmlRoot) - p.xmlRoot = p; - } - - this.$loadAml = function(){ - //hack - if (!this.getAttribute("caption")) - this.setAttribute("caption", this.caption); - - var oInt = p == this.parentNode ? p.$container : this.parentNode.$container; - var node = oInt.lastChild;//@todo this should be more generic - if (!p.documentId) - p.documentId = apf.xmldb.getXmlDocId(this); - p.$add(this, apf.xmldb.nodeConnect(p.documentId, this, null, p), - this.parentNode, oInt != p.$container && oInt, null); - p.$fill(); - - if (p.$isTreeArch) { - this.$container = p.$getLayoutNode("item", "container", - this.$ext = node && node.nextSibling || oInt.firstChild);//@todo this should be more generic - } - else this.$ext = node && node.nextSibling || oInt.firstChild; - - var ns = this; - while((ns = ns.nextSibling) && ns.nodeType != 1); - - if (!ns || ns.$canLeechSkin != "item") { - p.dispatchEvent("afterload"); - if (p.autoselect) - p.$selectDefault(this.parentNode); - } - } - - return; - } - - this.$ext = this.$getExternal(this.$isLeechingSkin - ? "item" //this.type - : "main", null, function($ext){ - var o = 'var o = apf.lookup(' + this.$uniqueId + '); if (!o || o.disabled) return; o'; - $ext.setAttribute("onmouseup", o + '.$up(event)'); - $ext.setAttribute("onmousemove", o + '.$over(event)'); - $ext.setAttribute("onmouseout", o + '.$out(event)'); - $ext.setAttribute("onmousedown", o + '.$down()'); - $ext.setAttribute("onclick", o + '.$click()'); - }); - // getExternal always appends to the end which is wrong when drawing is delayed - var next = this.nextSibling && this.nextSibling.$ext; - if (next && next.parentNode === this.$ext.parentNode && this.$ext.nextSibling !== next) { - next.parentNode.insertBefore(this.$ext, next); - } - - var _self = this; - apf.addListener(this.$ext, "mouseover", function(e) { - if (!_self.disabled) - _self.dispatchEvent("mouseover", {htmlEvent: e}); - }); - - apf.addListener(this.$ext, "mouseout", function(e) { - if (!_self.disabled) - _self.dispatchEvent("mouseout", {htmlEvent: e}); - }); - - /*p.$getNewContext("item"); - var elItem = p.$getLayoutNode("item");*/ - - //@todo if not elItem try using own skin - - //this.$ext = apf.insertHtmlNode(elItem, this.parentNode.$container); - this.$caption = this.$getLayoutNode("item", "caption", this.$ext) - this.$icon = this.$getLayoutNode("item", "icon", this.$ext); - this.$hotkey = this.$getLayoutNode("item", "hotkey", this.$ext); - - if (!isSkinSwitch && this.nextSibling && this.nextSibling.$ext - && this.nextSibling.$ext.parentNode == this.$ext.parentNode) { - this.$ext.parentNode.insertBefore(this.$ext, this.nextSibling.$ext); - } - }; - - /* - * @private - */ - this.addEventListener("DOMNodeInsertedIntoDocument", function(e){ - //var x = this.$aml; - - //this.skinName = this.parentNode.skinName; - var isSkinSwitch = this.$ext ? true : false; - if (isSkinSwitch) { - if (typeof this.checked !== "undefined") - this.$handlePropSet("checked", this.checked); - else if (typeof this.selected !== "undefined") - this.$handlePropSet("selected", this.selected); - - if (this.disabled) - this.$handlePropSet("disabled", this.disabled); - - if (this.caption) - this.$handlePropSet("caption", this.caption); - } - }); -}).call(apf.item.prototype = new apf.Presentation()); - -//apf.aml.setElement("radio", apf.radio); -//apf.aml.setElement("check", apf.check); -apf.aml.setElement("item", apf.item); - -}; - -}); \ No newline at end of file diff --git a/plugins/c9.ide.ui/lib/page.js b/plugins/c9.ide.ui/lib/page.js deleted file mode 100644 index d97b521b..00000000 --- a/plugins/c9.ide.ui/lib/page.js +++ /dev/null @@ -1,2171 +0,0 @@ -define(function(require, module, exports) { -return function(apf) { -var $setTimeout = setTimeout; -var $setInterval = setInterval; - - -/** - * A page in a pageable element (_i.e._ a page in {@link apf.tab}). - * - * #### Example - * - * ```xml, demo - * - * - * - * - * - * Example - * Example - * - * - * Test checkbox - * Test checkbox - * Test checkbox - * - * - * This ok? - * This better? - * - * - * - * - * - * ``` - * - * @class apf.page - * @define page - * @container - * @inherits apf.Presentation - * @allowchild {elements}, {anyaml} - * - * - * @author Ruben Daniels (ruben AT ajax DOT org) - * @version %I%, %G% - * @since 0.8 - */ -apf.page = function(struct, tagName) { - this.$init(tagName || "page", apf.NODE_VISIBLE, struct); -}; - -(function(){ - this.canHaveChildren = true; - - this.$focussable = false; - this.closebtn = false; - this.autofocus = true; - - - - - /** - * Sets the caption of the button of this element. - * @param {String} caption The text displayed on the button of this element. - */ - this.setCaption = function(caption) { - this.setProperty("caption", caption, false, true); - }; - - /** - * Sets the icon of the button of this element. - * @param {String} icon The icon displayed on the button of this element. - */ - this.setIcon = function(icon) { - this.setProperty("icon", icon, false, true); - }; - - - // *** Delayed Render Support *** // - - - //Hack - this.addEventListener("beforerender", function(){ - this.parentNode.dispatchEvent("beforerender", { - page: this - }); - }); - - this.addEventListener("afterrender", function(){ - this.parentNode.dispatchEvent("afterrender", { - page: this - }); - }); - - - // *** Properties *** // - - this.$booleanProperties["visible"] = true; - this.$booleanProperties["fake"] = true; - this.$booleanProperties["closebtn"] = true; - this.$booleanProperties["autofocus"] = true; - this.$supportedProperties.push("fake", "caption", "icon", "tooltip", - "type", "buttons", "closebtn", "trans-in", "trans-out", "autofocus"); - - - /** - * @attribute {Boolean} closebtn Sets or gets whether this page's button shows a close button inside it. - */ - this.$propHandlers["closebtn"] = function(value) { - //if (!this.$amlLoaded || !this.parentNode.$hasButtons) - // return; - var _self = this; - - if (value) { - var btncontainer = this.parentNode.$getLayoutNode("button", "container", this.$button); - - this.parentNode.$getNewContext("btnclose"); - var elBtnClose = this.parentNode.$getLayoutNode("btnclose"); - - if (elBtnClose) { - // if (elBtnClose.nodeType == 1) { - apf.setStyleClass(this.$button, "btnclose"); - - elBtnClose.addEventListener("mousedown", function(e) { - apf.cancelBubble(e, apf.lookup(_self.$uniqueId)); - }, false); - - elBtnClose.addEventListener("click", function(e) { - var page = apf.lookup(_self.$uniqueId); - page.parentNode.remove(page, e); - }, false); - - btncontainer.appendChild(elBtnClose); - } - - } - }; - - - /** - * @attribute {String} caption Sets or gets the text displayed on the button of this element. - */ - this.$propHandlers["tooltip"] = function(value) { - if (!this.parentNode) - return; - - var node = this.parentNode - .$getLayoutNode("button", "caption", this.$button); - - (node.nodeType == 1 ? node : node.parentNode).setAttribute("title", value || ""); - } - - /** - * @attribute {String} caption Sets or gets the text displayed on the button of this element. - */ - this.$propHandlers["caption"] = function(value) { - if (!this.parentNode) - return; - - var node = this.parentNode - .$getLayoutNode("button", "caption", this.$button); - - if (node.nodeType == 1) - node.innerHTML = value; - else - node.nodeValue = value; - }; - - this.$propHandlers["icon"] = function(value) { - if (!this.parentNode) - return; - - var node = this.parentNode - .$getLayoutNode("button", "icon", this.$button); - - if (node && node.nodeType == 1) - apf.skins.setIcon(node, value, this.parentNode.iconPath); - }; - - this.$propHandlers["visible"] = function(value) { - if (!this.parentNode) - return; - - if (value) { - if (this.$fake) { - this.parentNode.set(this.$fake); - this.visible = false; - return; - } - - this.$ext.style.display = ""; - if (this.parentNode.$hasButtons) - this.$button.style.display = "block"; - - if (!this.parentNode.$activepage) - this.parentNode.set(this); - } - else { - if (this.$active) { - this.$deactivate(); - - // Try to find a next page, if any. - var nextPage = this.parentNode.activepagenr + 1; - var pages = this.parentNode.getPages() - var len = pages.length - while (nextPage < len && !pages[nextPage].visible) - nextPage++; - - if (nextPage == len) { - // Try to find a previous page, if any. - nextPage = this.parentNode.activepagenr - 1; - while (nextPage >= 0 && len && !pages[nextPage].visible) - nextPage--; - } - - if (nextPage >= 0) - this.parentNode.set(nextPage); - else { - this.parentNode.activepage = - this.parentNode.activepagenr = - this.parentNode.$activepage = null; - } - } - - this.$ext.style.display = "none"; - if (this.parentNode.$hasButtons) - this.$button.style.display = "none"; - } - }; - - /** - * @attribute {Boolean} fake Sets or gets whether this page actually contains elements or - * only provides a button in the pageable parent element. - */ - this.$propHandlers["fake"] = function(value) { - if (this.$ext) { - apf.destroyHtmlNode(this.$ext); - this.$int = this.$ext = null; - } - }; - - this.$propHandlers["type"] = function(value) { - this.setProperty("fake", true); - - if (this.relPage && this.$active) - this.relPage.$deactivate(); - - this.relPage = this.parentNode.getPage(value); - if (this.$active) - this.$activate(); - }; - - // *** DOM Hooks *** // - - this.addEventListener("DOMNodeRemoved", function(e) { - if (e && e.currentTarget != this) - return; - - if (this.$button) { - if (this.$position & 1) - this.parentNode.$setStyleClass(this.$button, "", ["firstbtn", "firstcurbtn"]); - if (this.$position & 2) - this.parentNode.$setStyleClass(this.$button, "", ["lastbtn"]); - } - - if (!e.$doOnlyAdmin) { - if (this.$button) - this.$button.parentNode.removeChild(this.$button); - - if (this.parentNode && this.parentNode.$activepage == this) { - if (this.$button) - this.parentNode.$setStyleClass(this.$button, "", ["curbtn"]); - this.parentNode.$setStyleClass(this.$ext, "", ["curpage"]); - } - } - }); - - this.addEventListener("DOMNodeRemovedFromDocument", function(e) { - if (this.fake && this.parentNode && this.parentNode.$activepage == this) - this.$deactivate(); - }); - - this.addEventListener("DOMNodeInserted", function(e) { - if (e && e.currentTarget != this || !this.$amlLoaded) //|| !e.$oldParent - return; - - if (!e.$isMoveWithinParent - && this.skinName != this.parentNode.skinName) { - // this.$destroy(); //clean up button - var skin = this.parentNode.skinName.split(":"); - this.$forceSkinChange(skin[1], skin[0]); - } - else if (this.$button && (!e.$oldParent || e.$oldParent.$hasButtons) && this.parentNode.$buttons) - this.parentNode.$buttons.insertBefore(this.$button, - e.$beforeNode && e.$beforeNode.$button || null); - - if (this.type) - this.relPage = this.parentNode.getPage && this.parentNode.getPage(this.type); - - }, true); - - // *** Private state functions *** // - - this.$position = 0; - this.$first = function(remove) { - if (remove) { - this.$isFirst = false; - this.$position -= 1; - this.parentNode.$setStyleClass(this.$button, "", - ["firstbtn", "firstcurbtn"]); - } - else { - this.$isFirst = true; - this.$position = this.$position | 1; - this.parentNode.$setStyleClass(this.$button, "firstbtn" - + (this.parentNode.$activepage == this ? " firstcurbtn" : "")); - } - }; - - this.$last = function(remove) { - if (remove) { - this.$isLast = false; - this.$position -= 2; - this.parentNode.$setStyleClass(this.$button, "", ["lastbtn"]); - } - else { - this.$isLast = true; - this.$position = this.$position | 2; - this.parentNode.$setStyleClass(this.$button, "lastbtn"); - } - }; - - this.$deactivate = function(fakeOther) { - this.$active = false; - - if (!this.parentNode) - return; - - if (this.parentNode.$hasButtons) { - if (this.$position > 0) - this.parentNode.$setStyleClass(this.$button, "", ["firstcurbtn"]); - this.parentNode.$setStyleClass(this.$button, "", ["curbtn"]); - } - - if ((!this.fake || this.relPage) && !fakeOther) { - this.parentNode.$setStyleClass(this.fake - ? this.relPage.$ext - : this.$ext, "", ["curpage"]); - - if (this.fake) { - if (!this.relPage.visible) - this.relPage.$ext.style.display = "none"; - - this.relPage.dispatchEvent("prop.visible", {value:false}); - } - - this.dispatchEvent("prop.visible", {value:false}); - } - }; - - this.$deactivateButton = function() { - if (this.parentNode && this.parentNode.$hasButtons) { - if (this.$position > 0) - this.parentNode.$setStyleClass(this.$button, "", ["firstcurbtn"]); - this.parentNode.$setStyleClass(this.$button, "", ["curbtn"]); - } - }; - - this.$activate = function(){ - //if (this.disabled) - //return false; - - this.$active = true; - - if (!this.$drawn) { - var f; - this.addEventListener("DOMNodeInsertedIntoDocument", f = function(e) { - this.removeEventListener("DOMNodeInsertedIntoDocument", f); - if (!this.$active) - return; - - this.$activate(); - }); - return; - } - - if (this.parentNode.$hasButtons) { - if (this.$isFirst) - this.parentNode.$setStyleClass(this.$button, "firstcurbtn"); - this.parentNode.$setStyleClass(this.$button, "curbtn"); - } - - if (!this.fake || this.relPage) { - if (this.fake) { - if (this.relPage) { - this.relPage.$ext.style.display = ""; - this.parentNode.$setStyleClass(this.relPage.$ext, "curpage"); - this.relPage.$fake = this; - - - if (this.relPage.$render) - this.relPage.$render(); - - - this.relPage.dispatchEvent("prop.visible", {value:true}); - } - } - else { - this.parentNode.$setStyleClass(this.$ext, "curpage"); - } - - - if (apf.layout && this.relPage) - apf.layout.forceResize(this.fake ? this.relPage.$int : this.$int); - - } - - - if (this.$render) - this.$render(); - - - if (!this.fake) { - this.dispatchEvent("prop.visible", {value:true}); - } - }; - - this.$activateButton = function() { - if (this.$active) - return; - - if (!this.$drawn) { - var f; - this.addEventListener("DOMNodeInsertedIntoDocument", f = function(e) { - this.removeEventListener("DOMNodeInsertedIntoDocument", f); - this.$activateButton(); - }); - return; - } - - if (this.parentNode && this.parentNode.$hasButtons) { - if (this.$isFirst) - this.parentNode.$setStyleClass(this.$button, "firstcurbtn"); - this.parentNode.$setStyleClass(this.$button, "curbtn"); - } - - - if (this.$render) - this.$render(); - - }; - - this.addEventListener("$skinchange", function(){ - if (this.caption) - this.$propHandlers["caption"].call(this, this.caption); - - if (this.icon) - this.$propHandlers["icon"].call(this, this.icon); - }); - - this.$enable = function(){ - if (this.$button) - this.$setStyleClass(this.$button, null, ["btnDisabled"]);//@todo this.$baseCSSname + - }; - - this.$disable = function(){ - if (this.$button) - this.$setStyleClass(this.$button, "btnDisabled");//@todo this.$baseCSSname + - }; - - function $btnSet(oHtml) { - this.parentNode.set(this); - if (this.autofocus) - this.canHaveChildren = 2; - this.$setStyleClass(oHtml, "down", null, true); - } - - this.$btnDown = function(oHtml, htmlEvent) { - if (this.disabled) - return; - - if (htmlEvent.button == 2) { - return; - } - if (htmlEvent.button == 1) { - apf.stopEvent(htmlEvent); - return; - } - - if (this.parentNode.dispatchEvent("tabselectclick", { - page: this, - htmlEvent: htmlEvent - }) === false) - return; - - this.$btnPressed = true; - - //if (!this.parentNode.$order) - $btnSet.call(this, oHtml); - } - - this.$btnUp = function(oHtml, htmlEvent) { - this.parentNode.$setStyleClass(oHtml, "", ["down"], true); - - if (this.disabled) - return; - - - if (htmlEvent.button == 1) { - apf.stopEvent(htmlEvent); - var page = apf.lookup(this.$uniqueId); - page.parentNode.remove(page, htmlEvent); - return; - } - - if (false && this.parentNode.$order && this.$btnPressed) { - this.$dragging = false; - - $btnSet.call(this, oHtml); - } - - this.$btnPressed = false; - - this.parentNode.dispatchEvent("tabselectmouseup"); - } - - this.$btnOut = function(oHtml) { - this.parentNode.$setStyleClass(oHtml, "", ["over"], true); - - this.canHaveChildren = true; - this.$dragging = false; - this.$btnPressed = false; - } - - // *** Init *** // - - this.$canLeechSkin = true; - - this.addEventListener("prop.class", function(e) { - apf.setStyleClass(this.$button, e.value, this.$lastClassValueBtn ? [this.$lastClassValueBtn] : null); - this.$lastClassValueBtn = e.value; - }); - - this.$draw = function(isSkinSwitch) { - this.skinName = this.parentNode.skinName; - - var sType = this.getAttribute("type") - if (sType) { - this.fake = true; - this.relPage = this.parentNode.getPage(sType) || null; - } - - if (this.parentNode.$hasButtons) { - //this.parentNode.$removeEditable(); //@todo multilingual support is broken when using dom - - this.parentNode.$getNewContext("button"); - var elBtn = this.parentNode.$getLayoutNode("button"); - elBtn.setAttribute(this.parentNode.$getOption("main", "select") || "onmousedown", - 'apf.lookup(' + this.$uniqueId + ').$btnDown(this, event);'); - elBtn.setAttribute("onmouseup", - 'apf.lookup(' + this.$uniqueId + ').$btnUp(this, event)'); - elBtn.setAttribute("onmouseover", 'var o = apf.lookup(' - + this.$uniqueId + ').parentNode;if(apf.lookup(' + this.$uniqueId - + ') != o.$activepage' + (this.parentNode.overactivetab ? " || true" : "") + ') o.$setStyleClass(this, "over", null, true);'); - elBtn.setAttribute("onmouseout", 'var o = apf.lookup(' - + this.$uniqueId + ');o&&o.$btnOut(this, event);'); - - //var cssClass = this.getAttribute("class"); - //if (cssClass) { - // apf.setStyleClass(elBtn, cssClass); - // this.$lastClassValueBtn = cssClass; - //} - - this.$button = apf.insertHtmlNode(elBtn, this.parentNode.$buttons); - - var closebtn = this.closebtn = this.getAttribute("closebtn"); - if ((apf.isTrue(closebtn) || ((this.parentNode.buttons || "").indexOf("close") > -1 && !apf.isFalse(closebtn)))) - this.$propHandlers["closebtn"].call(this, true); - - -// if (this.parentNode.$scale) { -// var w = apf.getHtmlInnerWidth(this.parentNode.$buttons); -// var l = this.parentNode.getPages().length; -// this.$button.style.width = Math.round(Math.min(w/l, this.parentNode.$maxBtnWidth)) + "px"; -// } - - - if (!isSkinSwitch && this.nextSibling && this.nextSibling.$button) - this.$button.parentNode.insertBefore(this.$button, this.nextSibling.$button); - - this.$button.host = this; - } - - if (this.fake) - return; - - if (this.$ext) - this.$ext.parentNode.removeChild(this.$ext); //@todo mem leaks? - - this.$ext = this.parentNode.$getExternal("page", - this.parentNode.oPages, null, this); - this.$ext.host = this; - - this.$int = this.parentNode - .$getLayoutNode("page", "container", this.$ext); - //if (this.$int) - //this.$int.setAttribute("id", this.$int.getAttribute("id")); - - //@todo this doesnt support hidden nodes. - if (this.visible) { - if (this.$isLast) - this.$last(); - if (this.$isFirst) - this.$first(); - } - - var _self = this; - this.parentNode && this.parentNode.getPages().forEach(function(page) { - if (page && page.type == _self.id) { - page.relPage = _self; - if (page.$active) { - _self.$fake = page; - page.$activate(); - } - _self.$button.style.display = "none"; - _self.visible = false; - } - }) - }; - - this.$destroy = function(){ - if (this.$button) { - if (this.parentNode && !this.parentNode.$amlDestroyed - && this.$button.parentNode) - this.$button.parentNode.removeChild(this.$button); - - this.$button.host = null; - this.$button = null; - } - }; - - -}).call(apf.page.prototype = new apf.Presentation()); - -apf.aml.setElement("page", apf.page); - - - -/** - * Baseclass of a paged element. - * - * @class apf.BaseTab - * @baseclass - * @allowchild page - * @author Ruben Daniels (ruben AT ajax DOT org) - * @version %I%, %G% - * @since 0.8 - * @inherits apf.Presentation - * - */ -/** - * @event beforeswitch Fires before this element switches to another page. - * @cancelable Prevents the page to become active. - * @param {Object} e The standard event object. It contains the following properties: - * - previous ([[String]] or [[Number]]): The name or number of the current page. - * - previousId ([[Number]]): The number of the current page. - * - previousPage ([[apf.page]]): The current page. - * - next ([[String]] or [[Number]]): The name or number of the page the will become active. - * - nextId ([[Number]]): The number of the page the will become active. - * - nextPage ([[apf.page]]): The page the will become active. - */ -/** - * @event afterswitch Fires after this element has switched to another page. - * @param {Object} e The standard event object. It contains the following properties: - * - previous ([[String]] or [[Number]]): The name or number of the previous page. - * - previousId ([[Number]]): The number of the previous page. - * - previousPage ([[apf.page]]): The previous page. - * - next ([[String]] or [[Number]]): The name or number of the current page. - * - nextId ([[Number]]): The number of the the current page. - * - nextPage ([[apf.page]]): The the current page. - */ -apf.BaseTab = function(){ - this.$init(true); -}; - -(function() { - this.isPaged = true; - this.$focussable = apf.KEYBOARD; - this.length = 0; - this.isLoading = {}; - this.inited = - this.ready = false; - this.$scroll = true; - - /** - * Sets the current page of this element. - * @param {String | Number} page The name or number of the page which is made active. - * @param {Function} callback The function called after setting the page. Especially handy when using the `src` attribute. - */ - this.set = function(page, callback, noEvent) { - if (noEvent || this.src && !this.$findPage(page, {})) { - return this.$propHandlers["activepage"].call( - this, page, null, null, callback, noEvent); - } - - if (this.activepage == (page.name || page)) - return callback && callback(this.getPage(page)); - - this.$lastCallback = callback; - this.setProperty("activepage", page); - }; - - // *** Properties and Attributes *** // - - this.$supportedProperties.push("activepage", "activepagenr", "length", - "src", "loading", "trans-in", "trans-out"); - - /** - * @property {Number} [SCROLL_LEFT=1] The constant representing the "scroll left" button - * @readonly - */ - /** - * @property {Number} [SCROLL_RIGHT=2] The constant representing the "scroll right" button - * @readonly - */ - /** - * @property {Number} [SCROLL_BOTH=4] The constant representing the "scroll left" and "scroll right" buttons - * @readonly - */ - /** - * @attribute {Number} activepagenr Sets or gets the child number of the active page. - * - * #### Example - * - * This example uses property binding to maintain consistency between a - * dropdown which is used as a menu, and a pages element - * - * ```xml - * - * Home - * General - * Advanced - * - * - * - * - *

Home Page

- *
- * - *

General Page

- *
- * - *

Advanced Page

- *
- *
- * ``` - */ - this.$propHandlers["activepagenr"] = - - /** - * @attribute {String} activepage Sets or gets the name of the active page. - * - * #### Example - * - * ```xml - * - * - * ... - * - * - * ... - * - * - * ... - * - * - * ``` - */ - this.$propHandlers["activepage"] = function(next, prop, force, callback, noEvent) { - if (!this.inited || apf.isNot(next) || next == -1) return; - - if (!callback) { - callback = this.$lastCallback; - delete this.$lastCallback; - } - - var page, info = {}; - page = this.$findPage(next, info); - - if (!page) { - if (this.src) { - if (this.isLoading[next]) - return; - - if (this.$findPage("loading", {})) - this.$propHandlers["activepage"].call(this, "loading"); - - this.setProperty("loading", true); - this.isLoading[next] = true; - - page = this.ownerDocument.createElementNS(apf.ns.apf, "page"); - page.setAttribute("id", next); - this.appendChild(page); - - var _self = this; - page.insertMarkup(this.src, { - page: next, - //@todo apf3.0 change callback arguments in xinclude - callback: function(options) { - delete _self.isLoading[next]; - - if (!options.xmlNode) { - var oError = new Error(apf.formatErrorString(0, null, - "Loading new page", "Could not load new page: " - + _self.src)); - - _self.setProperty("loading", false); - - if (this.dispatchEvent("error", apf.extend({ - error: oError, - bubbles: true - }, options)) === false) - return true; - - throw oError; - } - else { - //for success - _self.setProperty("activepage", next); - - //Needs to be after set - if (callback) - callback(options.amlNode); - - _self.setProperty("loading", false); - } - } - }); - return; - } - - - - return false; - } - - if (page.parentNode != this) { - - - return false; - } - - if (!page.visible || page.disabled) { - - - return false; - } - - //If page is given as first argument, let's use its position - if (next.tagName) { - next = info.position; - this.activepage = page.name || next;//page.type || - } - - //Call the onbeforeswitch event; - if (!noEvent) { - var oEvent = { - previous: this.activepage, - previousId: this.activepagenr, - previousPage: this.$activepage, - next: next, - nextId: info.position, - nextPage: page - }; - - if (this.dispatchEvent("beforeswitch", oEvent) === false) { - //Loader support - if (this.hideLoader) - this.hideLoader(); - - return false; - } - } - - //Maintain an activepagenr property (not reentrant) - this.activepagenr = info.position; - this.setProperty("activepagenr", info.position); - - //Deactivate the current page, if any, and activate the new one - if (this.$activepage) - this.$activepage.$deactivate(); - - page.$activate(); - - - - this.$activepage = page; - - //this.scrollIntoView(page); - - - //Loader support - if (this.hideLoader) { - if (page.$rendered !== false) { - this.hideLoader(); - } - else { - //Delayed rendering support - page.addEventListener("afterrender", function(){ - this.parentNode.hideLoader(); - }); - } - } - - if (!noEvent) { - if (page.$rendered !== false) - this.dispatchEvent("afterswitch", oEvent); - else { - //Delayed rendering support - page.addEventListener("afterrender", function(){ - this.parentNode.dispatchEvent("afterswitch", oEvent); - }); - } - } - - if (typeof callback == "function") - callback(page); - - return true; - }; - - /** - * @attribute {String} buttons Sets or gets the modifier for tab page buttons, seperated by a `|` character - * - * Possible values include: - * - `close`: The button has a close button inside it - * - `scale`: The buttons are scaled to make room for more buttons - * - `scroll`: When the buttons take too much space, scroll buttons are displayed - */ - this.$propHandlers["buttons"] = function(value) { - //this.buttons = value; - this.$scale = value.indexOf("scale") > -1; - this.$scroll = !this.$scale; - this.$order = value.indexOf("order") > -1; - - - //@todo skin change - //@todo buttons on the side - if (this.$scale) { - this.$maxBtnWidth = parseInt(this.$getOption("button", "maxwidth")) || 150; - this.$minBtnWidth = parseInt(this.$getOption("button", "minwidth")) || 10; - this.$setStyleClass(this.$buttons, "scale"); - this.addEventListener("resize", scalersz); - - // this.minwidth = this.$minBtnWidth * this.getPages().length + 10; - // this.$ext.style.minWidth = Math.max(0, this.minwidth - apf.getWidthDiff(this.$ext)) + "px"; - } - else { - this.$setStyleClass(this.$buttons, "", ["scale"]); - this.removeEventListener("resize", scalersz); - } - - }; - - - function visCheck(){ - scalersz.call(this) - } - - this.anims = "add|remove|sync"; - - //Add an element - function animAddTab(tab, callback) { - var t = tab.$button; - - var animateWidth = (t.offsetWidth - - apf.getWidthDiff(t)) < parseInt(apf.getStyle(t, "maxWidth")); - - if (animateWidth) { - var p = tab.parentNode.getPages()[0] == tab - ? null - : tab.previousSibling.$button; - var tb = p - ? (p.offsetWidth - apf.getWidthDiff(p)) - : parseInt(apf.getStyle(t, "maxWidth")); - t.style.maxWidth = "0px"; - } - - t.style.marginTop = (t.offsetHeight + 2) + "px"; - - function animateToTop(){ - t.style.marginTop = "0px"; - - setTimeout(function(){ - t.style[apf.CSSPREFIX + "TransitionProperty"] = ""; - t.style[apf.CSSPREFIX + "TransitionDuration"] = ""; - t.style[apf.CSSPREFIX + "TimingFunction"] = ""; - - t.style.marginTop = ""; - - if (animateWidth) - t.style.maxWidth = ""; - - callback(tab); - }, 150); - } - - setTimeout(function(){ - t.style[apf.CSSPREFIX + "TransitionProperty"] = "margin-top, max-width"; - t.style[apf.CSSPREFIX + "TransitionDuration"] = "100ms, 50ms"; - t.style[apf.CSSPREFIX + "TimingFunction"] = "cubic-bezier(.10, .10, .25, .90), cubic-bezier(.10, .10, .25, .90)"; - - if (animateWidth) { - t.style.maxWidth = tb + "px"; - setTimeout(animateToTop, 50); - } - else animateToTop(); - }); - } - - //Remove an element - function animRemoveTab(tab, isLast, isContracted, callback, isOnly) { - var t = tab.$button; - if (!t) return; - var tb = t.offsetHeight; - - var diff = t.offsetWidth; - - t.style[apf.CSSPREFIX + "TransitionProperty"] = "margin-top, max-width, padding"; - t.style[apf.CSSPREFIX + "TransitionDuration"] = (isOnly ? ".2" : ".15") + "s, .1s, .1s"; - t.style[apf.CSSPREFIX + "TimingFunction"] = "linear, ease-out, ease-out"; - - t.style.marginTop = (tb + 2) + "px"; - - var p = t.parentNode; - if (apf.isGecko) p = p.parentNode; - - p.style[apf.CSSPREFIX + "TransitionProperty"] = "padding-right"; - p.style[apf.CSSPREFIX + "TransitionDuration"] = ".2s"; - p.style[apf.CSSPREFIX + "TimingFunction"] = "ease-out"; - - // if (!isLast && isContracted) { - // t.style.minWidth = "20px" - // t.style.maxWidth = "0px"; - // t.style.padding = 0; - - // end(); - // } - // else { - setTimeout(function(){ - if (isLast) - p.style.paddingRight = ""; - else { - var cur = parseInt(apf.getStyle(p, "paddingRight")); - p.style.paddingRight = (cur + diff - (apf.tabRightDelta || 15)) + "px"; - } - - t.className += " destroyed"; - - end(); - }, isOnly ? 150 : 100); - // } - - function end(){ - setTimeout(function(){ - p.style[apf.CSSPREFIX + "TransitionProperty"] = ""; - p.style[apf.CSSPREFIX + "TransitionDuration"] = ""; - p.style[apf.CSSPREFIX + "TimingFunction"] = ""; - - t.style[apf.CSSPREFIX + "TransitionProperty"] = ""; - t.style[apf.CSSPREFIX + "TransitionDuration"] = ""; - t.style[apf.CSSPREFIX + "TimingFunction"] = ""; - - t.style.display = "none"; - - callback(tab); - }, 150); - } - } - - this.$scaleinit = function(node, type, callback, force) { - var _self = this; - - var pg = this.getPages(); - var l = pg.length; - if (!l) return; - - // this.minwidth = this.$minBtnWidth * l + 10; //@todo padding + margin of button container - // this.$ext.style.minWidth = Math.max(0, this.minwidth - apf.getWidthDiff(this.$ext)) + "px"; - - if (force && !this.$ext.offsetWidth && !this.$ext.offsetHeight - || this.anims.indexOf(type) == -1 || this.skipAnimOnce) { - scalersz.call(this); - - this.skipAnimOnce = false; - - if (type == "add") - node.dispatchEvent("afteropen"); - else if (type == "remove") { - if (node.dispatchEvent("afterclose") !== false) - callback(); - } - - return; - } - - if (!apf.window.vManager.check(this, "tabscale", visCheck)) - return; - - if (!type) - return scalersz.call(this); - - function btnMoHandler(e) { - var pos = apf.getAbsolutePosition(this); - if (e.clientX <= pos[0] || e.clientY <= pos[1] - || e.clientX >= pos[0] + this.offsetWidth - || e.clientY >= pos[1] + this.offsetHeight) { - apf.removeListener(_self.$buttons, "mouseout", btnMoHandler); - delete _self.$waitForMouseOut; - _self.$scaleinit(null, "sync"); -// } -// else if (_self.$waitForMouseOut) -// _self.$waitForMouseOut = 2; - } - } - - if (type == "add") { - animAddTab(node, function(){ - node.dispatchEvent("afteropen"); - }); - } - else if (type == "sync") { - scalersz.call(this); - } - else if (type == "remove") { - var onfinish = function(){ - if (node.dispatchEvent("afterclose") === false) - return; - - callback(); - - if (!isLast && isContracted) { - var pages = _self.getPages(); - for (var i = 0, l = pages.length; i < l; i++) { - var page = pages[i]; - page.$button.style.minWidth = ""; - page.$button.style.maxWidth = ""; - } - } - - if (_self.$waitForMouseOut == 2) { - apf.removeListener(_self.$buttons, "mouseout", btnMoHandler); - delete _self.$waitForMouseOut; - } - else if (isLast) - delete _self.$waitForMouseOut; - }; - - var pages = this.getPages(); - - var lNode = pages[pages.length - 1]; - while (lNode && (!lNode.$button || lNode.$button.style.top)) { - lNode = lNode.previousSibling; - } - if (!lNode || !node.$button) return; - - var isLast = lNode == node; - var isContracted = (node.$button.offsetWidth - apf.getWidthDiff(node.$button) - != parseInt(apf.getStyle(node.$button, "maxWidth"))); - - if (!isLast && isContracted) { - for (var i = 0, l = pages.length; i < l; i++) { - var page = pages[i]; - page.$button.style.minWidth = - page.$button.style.maxWidth = (page.$button.offsetWidth - - (apf.isGecko ? 0 : apf.getWidthDiff(page.$button))) - + "px"; - } - } - - var isCur = this.$activepage == node; - - //Set activetab if the current one is lost - if (_self.nextTabInLine) { - _self.set(_self.nextTabInLine); - delete _self.nextTabInLine; - } - else if (_self.$activepage == node) { - var ln = node.nextSibling; - while (ln && (!ln.$first || !ln.visible)) - ln = ln.nextSibling; - var rn = node.previousSibling; - while (rn && (!rn.$last || !rn.visible)) - rn = rn.previousSibling; - if (ln || rn) - _self.set(ln || rn); - } - - if (isCur) { - apf.setStyleClass(node.$button, "curbtn"); - if (node.$button) - node.$button.style.zIndex = 0; - } - - if (pages.length == 1) - (node.relPage || node).$ext.style.display = "none"; - - animRemoveTab(node, isLast, isContracted, onfinish, pages.length == 1); - - this.$waitForMouseOut = true; - if (!isLast) - apf.addListener(_self.$buttons, "mouseout", btnMoHandler); - } - } - - /* - * Update the size of the tab container - */ - function scalersz(e, excl) { - if (!this.length && !this.getPages().length || this.$waitForMouseOut) - return; - - var p = apf.isGecko ? this.$buttons.parentNode : this.$buttons; - - p.style[apf.CSSPREFIX + "TransitionProperty"] = "padding-right"; - p.style[apf.CSSPREFIX + "TransitionDuration"] = this.length < 4 ? ".4s" : ".2s"; - p.style[apf.CSSPREFIX + "TimingFunction"] = "ease-out"; - - if (apf.isGecko) { - p.style.paddingRight = apf.getWidthDiff(this.$buttons) + "px"; - } - else { - p.style.paddingRight = ""; - } - - setTimeout(function(){ - p.style[apf.CSSPREFIX + "TransitionProperty"] = ""; - p.style[apf.CSSPREFIX + "TransitionDuration"] = ""; - p.style[apf.CSSPREFIX + "TimingFunction"] = ""; - }, 250); - } - - - // *** Public methods *** // - - - - /** - * Retrieves an array of all the page elements of this element. - * @returns {Array} An array of all the {apf.page} elements - */ - this.getPages = function(){ - var r = [], nodes = this.childNodes; - if (!nodes) return r; - for (var i = 0, l = nodes.length; i < l; i++) { - if ("page|case".indexOf(nodes[i].localName) > -1 && nodes[i].visible !== false) - r.push(nodes[i]); - } - return r; - }; - - /** - * Retrieves a page element by its name or child number. - * @param {String | Number} nameOrId The name or child number of the page element to retrieve. - * @return {apf.page} The found page element. - */ - this.getPage = function(nameOrId) { - if (apf.isNot(nameOrId)) - return this.$activepage; - else - return this.$findPage(nameOrId); - }; - - /** - * Adds a new page element - * @param {String} [caption] The text displayed on the button of the page - * @param {String} [name] The name of the page which is can be referenced by - * @param {String} [type] The type of the page - * @param {apf.page} [insertBefore] The page to insert ahead of; `null` means to put it at the end - * @param {Function} [callback] A callback to call and pass the new page to - * @return {apf.page} The created page element. - */ - this.add = function(caption, name, type, before, callback) { - var page = this.ownerDocument.createElementNS(apf.ns.aml, "page"); - if (name) - page.setAttribute("id", name); - if (type) - page.setAttribute("type", type); - if (caption) - page.setAttribute("caption", caption); - - if (callback) - callback(page); - - this.insertBefore(page, before); - - - //this.scrollIntoView(page); - - return page; - }; - - /** - * Removes a page element from this element. This function destroys ALL children - * of this page. To simple remove the page from the DOM tree, use the - * [[apf.AmlNode.removeNode]] method. - * - * @param {Mixed} nameOrId The name or child number of the page element to remove - * @return {apf.page} The removed page element - */ - this.remove = function(nameOrId, force, noAnimation) { - var page = typeof nameOrId == "object" - ? nameOrId - : this.$findPage(nameOrId); - if (!page) - return false; - - var e = {page: page}; - if (typeof force == "object") { - e.htmlEvent = force; - force = false; - } - - if (!force && this.dispatchEvent("close", e) === false) - return; - - if (this.$scale && !noAnimation) { - this.$scaleinit(page, "remove", function(){ - //page.removeNode(); - page.destroy(true, true); - }, true); - } - else - - { - //page.removeNode(); - if (page.dispatchEvent("afterclose") !== false) - page.destroy(true, true); - - - //@todo this is wrong, we can also use removeChild - //this.setScrollerState(); - - } - - return page; - }; - - - /* - var SCROLLANIM = { - scrollOn: false, - steps: 15, - interval: 10, - size: 0, - left: 0, - control: { - stop: false - }, - stopHandle: function() { - bAnimating = false; - } - }, - SCROLL_OFF = 0x0001, - SCROLL_HOVER = 0x0002, - SCROLL_DOWN = 0x0004, - SCROLL_DIS = 0x0008, - SCROLL_L_STATE = SCROLL_OFF, - SCROLL_R_STATE = SCROLL_OFF, - SCROLL_LEFT = 0x0001, - SCROLL_RIGHT = 0x0002, - SCROLL_BOTH = 0x0004, - bAnimating = false, - scrollTimer = null, - keepScrolling = false, - globalDir = SCROLL_LEFT; -*/ - function getButtonsWidth() { - var cId = "cache_" + this.$buttons.childNodes.length; - if (SCROLLANIM[cId]) - return SCROLLANIM[cId]; - - var iWidth = 0; - for (var i = 0, l = this.$buttons.childNodes.length; i < l; i++) { - if (typeof this.$buttons.childNodes[i].offsetWidth != "undefined") - iWidth += this.$buttons.childNodes[i].offsetWidth; - } - - return SCROLLANIM[cId] = iWidth; - } - - function setButtonState(dir, state) { - var bBoth = dir & SCROLL_BOTH; - if (bBoth) - dir = SCROLL_LEFT; - var oBtn = this[dir & SCROLL_LEFT ? "oLeftScroll" : "oRightScroll"]; - if (!(state & SCROLL_DIS)) { - if (dir & SCROLL_LEFT) - SCROLL_L_STATE = state; - else - SCROLL_R_STATE = state; - } - - if (state & SCROLL_OFF) - apf.setStyleClass(oBtn, "", ["disabled", "hover", "down"]); - else if (state & SCROLL_HOVER) - apf.setStyleClass(oBtn, "hover", ["disabled", "down"]); - else if (state & SCROLL_DOWN) - apf.setStyleClass(oBtn, "down", ["disabled", "hover"]); - else if (state & SCROLL_DIS) - apf.setStyleClass(oBtn, "disabled", ["hover", "down"]); - - if (bBoth) - setButtonState(SCROLL_RIGHT, state); - } - - /** - * Sets the state scroller buttons: `enabled`, `disabled` or completely `hidden`, - * depending on the state of the tab buttons - * - * @param {Boolean} [bOn] Indicates whether to turn the scroll buttons on or off - * @param {Number} [iBtns] Specifies the buttons to set the state of. Can be [[apf.BaseTab.SCROLL_LEFT]], [[apf.BaseTab.SCROLL_RIGHT]] or [[apf.BaseTab.SCROLL_BOTH]] - * - */ - this.setScrollerState = function(bOn, iBtns) { - if (!this.ready || !this.$hasButtons || !this.oScroller) return; - - if (typeof bOn == "undefined") { - var scrollerWidth = this.oScroller.offsetWidth - || parseInt(apf.getStyle(this.oScroller, "width").replace(/(px|em|%)/, "")); - bOn = ((getButtonsWidth.call(this) + scrollerWidth) > this.$ext.offsetWidth); - iBtns = SCROLL_BOTH; - } - - if (iBtns & SCROLL_BOTH && bOn !== SCROLLANIM.scrollOn) { - // in case of HIDING the scroller: check if the anim stuff has reverted - SCROLLANIM.scrollOn = bOn; - if (!bOn) { - this.$buttons.style.left = SCROLLANIM.left + "px"; - this.oScroller.style.display = "none"; - } - //else - // TODO: scroll active tab into view if it becomes hidden beneath scroller node(s) - } - else { - this.oScroller.style.display = ""; - } - - this.oScroller.style.display = (iBtns & SCROLL_BOTH && !bOn) - ? "none" - : ""; - if (typeof iBtns == "undefined") - iBtns = SCROLL_BOTH; - if (!bOn) { - if ((iBtns & SCROLL_LEFT) || (iBtns & SCROLL_BOTH)) - setButtonState.call(this, SCROLL_LEFT, SCROLL_DIS); - if ((iBtns & SCROLL_RIGHT) || (iBtns & SCROLL_BOTH)) - setButtonState.call(this, SCROLL_RIGHT, SCROLL_DIS); - } - }; - - /** - * Corrects the state of the scroller buttons when the state of external - * components change, like on a resize event of a window. - * - */ - this.correctScrollState = function() { - if (!this.ready || !this.$hasButtons || !this.oScroller) return; - this.setScrollerState(); - }; - - /** - * Retrieves the utmost left or right boundaries of the tab buttons strip that - * can be scrolled to. The tabs cannot scroll any further than these boundaries - * - * @param {Number} dir Determines which boundary side to look at, either [[apf.BaseTab.SCROLL_LEFT]] or [[apf.BaseTab.SCROLL_RIGHT]] - * @param {Boolean} [useCache] Used only when tabs are draggable. Not implemented. - * @returns {Number} - */ - function getAnimationBoundary(dir, useCache) { - if (SCROLLANIM.size <= 0) { - SCROLLANIM.left = this.$buttons.offsetLeft; - SCROLLANIM.size = Math.round(this.firstChild.$button.offsetWidth); - } - if (dir & SCROLL_LEFT) { - return SCROLLANIM.left; - } - else if (dir & SCROLL_RIGHT) { - // TODO: support Drag n Drop of tabs... - //if (typeof useCache == "undefined") useCache = false; - //if (!tabcontrol.drag) tabcontrol.drag = {}; - //if (useCache && tabcontrol.drag.boundCache) - // return tabcontrol.drag.boundCache; - var oNode = this.$buttons.childNodes[this.$buttons.childNodes.length - 1]; - - return this.$ext.offsetWidth - (oNode.offsetLeft + oNode.offsetWidth - + (this.oScroller.offsetWidth + 4));// used to be tabcontrol.drag.boundCache; - } - } - - /** - * @event scroll Executed when the user presses one of the two scroll buttons - * (left or right). - * - * If the tab-buttons strip can be scrolled, the - * respective behavior is called. - * - * @param {Event} e An event object, usually a mousedown event from a scroller-button - * @param {Number} dir The direction of the scroll, either [[apf.BaseTab.SCROLL_LEFT]] or [[apf.BaseTab.SCROLL_RIGHT]] - * - */ - this.scroll = function(e, dir) { - if (!this.ready || !this.$hasButtons || !this.oScroller) return; - if (!e) - e = window.event; - if (typeof e["type"] == "unknown") //scope expired (prolly GC'ed) - e = {type: "click"}; - if (bAnimating && e.type != "dblclick") return; - var bAnimating = true; - - if (typeof dir == "undefined") - dir = SCROLL_LEFT; - - //apf.tween.clearQueue(this.$buttons, true); - var iCurrentLeft = this.$buttons.offsetLeft, - size = e["delta"] ? Math.round(e.delta * 36) : SCROLLANIM.size, - //get maximum left offset for either direction - iBoundary = getAnimationBoundary.call(this, dir), - _self = this; - if (dir & SCROLL_LEFT) { - setButtonState(SCROLL_LEFT, SCROLL_DOWN); - setButtonState(SCROLL_RIGHT, SCROLL_OFF); - if (iCurrentLeft === iBoundary) { - this.setScrollerState(false, SCROLL_LEFT); - return apf.tween.single(this.$buttons, { - steps: SCROLLANIM.steps, - interval: 20, - from: iCurrentLeft, - to: iCurrentLeft + 12, - type: "left", - anim: apf.tween.EASEOUT, - onstop: SCROLLANIM.stopHandle, - onfinish: function(oNode) { - apf.tween.single(oNode, { - steps: SCROLLANIM.steps, - interval: SCROLLANIM.interval, - from: iCurrentLeft + 12, - to: iCurrentLeft, - type: "left", - anim: apf.tween.EASEIN, - onstop: SCROLLANIM.stopHandle, - onfinish: function() { - bAnimating = false; - if (e.name == "mousescroll") - setButtonState(SCROLL_LEFT, SCROLL_OFF); - } - }); - } - }); - } - //one scroll animation scrolls by a SCROLLANIM.size px. - var iTargetLeft = iCurrentLeft + (e.type == "dblclick" ? size * 3 : size); - if (iTargetLeft > iBoundary) - iTargetLeft = iBoundary; - - if (iTargetLeft === iBoundary) - this.setScrollerState(false, SCROLL_LEFT); - this.setScrollerState(true, SCROLL_RIGHT); - - //start animated scroll to the left - apf.tween.single(this.$buttons, { - steps: SCROLLANIM.steps, - interval: SCROLLANIM.interval, - control: SCROLLANIM.control, - from: iCurrentLeft, - to: iTargetLeft, - type: "left", - anim: apf.tween.NORMAL, - onstop: SCROLLANIM.stopHandle, - onfinish: function() { - bAnimating = false; - if (e.name == "mousescroll") - setButtonState(SCROLL_LEFT, SCROLL_OFF); - if (keepScrolling) - _self.scroll(e, globalDir); - } - }); - } - else if (dir & SCROLL_RIGHT) { - this.setScrollerState(true); - setButtonState(SCROLL_RIGHT, SCROLL_DOWN); - setButtonState(SCROLL_LEFT, SCROLL_OFF); - if (iCurrentLeft === iBoundary) { - this.setScrollerState(false, SCROLL_RIGHT); - return apf.tween.single(this.$buttons, { - steps: SCROLLANIM.steps, - interval: 20, - from: iCurrentLeft, - to: iCurrentLeft - 24, - type: "left", - anim: apf.tween.EASEOUT, - onstop: SCROLLANIM.stopHandle, - onfinish: function(oNode, options) { - apf.tween.single(oNode, { - steps: SCROLLANIM.steps, - interval: SCROLLANIM.interval, - from: iCurrentLeft - 24, - to: iCurrentLeft, - type: "left", - anim: apf.tween.EASEIN, - onstop: SCROLLANIM.stopHandle, - onfinish: function() { - bAnimating = false; - if (e.name == "mousescroll") - setButtonState(SCROLL_RIGHT, SCROLL_OFF); - } - }); - } - }); - } - //one scroll animation scrolls by a SCROLLANIM.size px. - var iTargetLeft = iCurrentLeft - (e.type == "dblclick" ? size * 3 : size); - //make sure we don't scroll more to the right than the - //maximum left: - if (iTargetLeft < iBoundary) - iTargetLeft = iBoundary; - //start animated scroll to the right - apf.tween.single(this.$buttons, { - steps: SCROLLANIM.steps, - interval: SCROLLANIM.interval, - control: SCROLLANIM.control, - from: iCurrentLeft, - to: iTargetLeft, - type: "left", - anim: apf.tween.NORMAL, - onstop: SCROLLANIM.stopHandle, - onfinish: function() { - bAnimating = false; - if (e.name == "mousescroll") - setButtonState(SCROLL_RIGHT, SCROLL_OFF); - if (keepScrolling) - _self.scroll(e, globalDir); - } - }); - } - }; - - /** - * If a tab page is outside of the user's view, this function scrolls that - * tabpage into view smoothly. - * - * @param {apf.page} oPage The page to scroll into view - * - */ - this.scrollIntoView = function(oPage) { - bAnimating = false; - if (!this.ready || !this.$hasButtons || !this.oScroller || !oPage.$drawn) - return; - bAnimating = true; - if (this.$buttons.offsetWidth < this.$ext.offsetWidth) - return this.setScrollerState(false); - - var iTabLeft = oPage.$button.offsetLeft, - iTabWidth = oPage.$button.offsetWidth, - iCurrentLeft = this.$buttons.offsetLeft; - - if (SCROLLANIM.size <= 0) { - SCROLLANIM.left = this.$buttons.offsetLeft; - var p = this.firstChild; - while (!p.$button) - p = p.nextSibling; - SCROLLANIM.size = Math.round(p.$button.offsetWidth); - } - this.$buttons.style.left = iCurrentLeft; - - var iRealWidth = this.$ext.offsetWidth, - iScrollCorr = this.oScroller.offsetWidth + 4, - iTargetLeft = null, - dir; - - if ((iTabLeft + iTabWidth) > ((iRealWidth - iScrollCorr) - iCurrentLeft)) { //scroll to the right - iTargetLeft = (-(iTabLeft - SCROLLANIM.left) - + (iRealWidth - iTabWidth - iScrollCorr)); - dir = SCROLL_RIGHT; - } - else if ((iCurrentLeft + iTabLeft) < SCROLLANIM.left) { //sroll to the left - iTargetLeft = SCROLLANIM.left - iTabLeft; - dir = SCROLL_LEFT; - } - - if (iTargetLeft !== null) { - this.setScrollerState(true); - setButtonState(SCROLL_RIGHT, dir & SCROLL_RIGHT ? SCROLL_DOWN : SCROLL_OFF); - setButtonState(SCROLL_LEFT, dir & SCROLL_LEFT ? SCROLL_DOWN : SCROLL_OFF); - apf.tween.clearQueue(this.$buttons, true); - - apf.tween.single(this.$buttons, { - steps: SCROLLANIM.steps, - interval: SCROLLANIM.interval, - from: iCurrentLeft, - to: iTargetLeft, - type: "left", - anim: apf.tween.NORMAL, - onstop: SCROLLANIM.stopHandle, - onfinish: function() { - bAnimating = false; - setButtonState(SCROLL_RIGHT, SCROLL_OFF); - setButtonState(SCROLL_LEFT, SCROLL_OFF); - } - }); - } - else - bAnimating = false; - }; - - - - // *** DOM Hooks *** // - - this.addEventListener("DOMNodeRemoved", function(e) { - var amlNode = e.currentTarget; - if (e.$doOnlyAdmin || e.relatedNode != this - || amlNode.localName != "page") - return; - - if ((this.activepage || this.activepage == 0) && this.activepage != -1) { - if (!this.getPage(this.nextTabInLine)) - this.nextTabInLine = null; - - if (this.nextTabInLine) - this.set(this.nextTabInLine); - - if (!this.nextTabInLine && this.$activepage == amlNode) { - var ln = amlNode.nextSibling; - while (ln && (!ln.$first || !ln.visible)) - ln = ln.nextSibling; - var rn = amlNode.previousSibling; - while (rn && (!rn.$last || !rn.visible)) - rn = rn.previousSibling; - - if (this.firstChild == amlNode && ln) - ln && ln.$first(); - if (this.lastChild == amlNode && rn) - rn && rn.$last(); - - if (ln || rn) - this.set(ln || rn); - else { - amlNode.$deactivate(); - - - //this.setScrollerState(); - - this.$activepage = - this.activepage = - this.activepagenr = null; - this.setProperty("activepage", null); - } - } - else { - - //if (this.$scroll) - //this.setScrollerState(); - - - if (this.$scale) - this.$scaleinit(); - - } - - delete this.nextTabInLine; - } - - - this.setProperty("length", this.getPages().length - 1); - - }); - - this.addEventListener("DOMNodeInserted",function(e) { - var amlNode = e.currentTarget; - - if (amlNode.localName != "page" || e.relatedNode != this || amlNode.nodeType != 1) - return; - - var pages = this.getPages(); - - if (!e.$beforeNode) { - var lastChild, pg = pages; - if (lastChild = pg[pg.length - 2]) - lastChild.$last(true); - amlNode.$last(); - } - - var p2, p = pages[0]; //@todo $beforeNode doesnt have to be a page - if (amlNode == p) { - if (p2 = this.getPage(1)) - p2.$first(true); - amlNode.$first(); - } - - if (this.$activepage) { - var info = {}; - this.$findPage(this.$activepage, info); - - if (this.activepagenr != info.position) { - if (parseInt(this.activepage) == this.activepage) { - this.activepage = info.position; - this.setProperty("activepage", info.position); - } - this.activepagenr = info.position; - this.setProperty("activepagenr", info.position); - } - } - else if (!this.activepage && !this.$activepage - && !amlNode.render || amlNode.$rendered) { - this.set(amlNode); - } - - - if (this.$scale && amlNode.visible && !e.$isMoveWithinParent) - this.$scaleinit(amlNode, "add"); - else - - { - amlNode.dispatchEvent("afteropen"); - } - - - this.setProperty("length", this.getPages().length); - - }); - - // *** Private state handling functions *** // - - this.$findPage = function(nameOrId, info) { - var node, nodes = this.childNodes; - if (!nodes) return; - - if (nameOrId.localName) { - for (var t = 0, i = 0, l = nodes.length; i < l; i++) { - node = nodes[i]; - if ("page|case".indexOf(node.localName) > -1 && (++t) && node == nameOrId) { - if (info) - info.position = t - 1; - return node; - } - } - } - else { - for (var t = 0, i = 0, l = nodes.length; i < l; i++) { - node = nodes[i]; - if ("page|case".indexOf(node.localName) > -1 && (t++ == nameOrId - || node.name == nameOrId)) { - if (info) - info.position = t - 1; - return node; - } - } - } - - return null; - }; - - this.$enable = function(){ - var nodes = this.childNodes; - for (var i = 0, l = nodes.length; i < l; i++) { - if (nodes[i].enable) - nodes[i].enable(); - } - }; - - this.$disable = function(){ - var nodes = this.childNodes; - for (var i = 0, l = nodes.length; i < l; i++) { - if (nodes[i].disable) - nodes[i].disable(); - } - }; - - // *** Keyboard support *** // - - - - - - - - // *** Init *** // - - this.$loadChildren = function(callback) { - var page = false, - _self = this, - i, j, l, node, nodes; - - this.inited = true; - - if (this.$hasButtons) { - this.$buttons = this.$getLayoutNode("main", "buttons", this.$ext); - this.$buttons.setAttribute("id", this.$uniqueId + "_buttons"); - - if (false && apf.isGecko && !this.$gotContainer) { - var div = this.$ext.appendChild(document.createElement("div")); - div.style.backgroundImage = apf.getStyle(this.$buttons, "backgroundImage"); - div.style.backgroundColor = apf.getStyle(this.$buttons, "backgroundColor"); - div.style.position = "absolute"; - div.style.left = 0; - div.style.top = 0; - div.style.right = 0; - div.style.overflow = "hidden"; - div.style.height = this.$buttons.offsetHeight + "px"; - div.appendChild(this.$buttons); - this.$buttons.style.width = "100%"; - div.style.paddingRight = apf.getWidthDiff(this.$buttons) + "px"; - - this.$gotContainer = true; - } - } - - this.oPages = this.$getLayoutNode("main", "pages", this.$ext); - - - // add scroller node(s) - /*this.oScroller = this.$getLayoutNode("main", "scroller", this.oPages); - if (this.oScroller) { - function startTimer(e, dir) { - clearTimeout(scrollTimer); - globalDir = dir; - scrollTimer = $setTimeout(function() { - keepScrolling = true; - _self.scroll(e, dir); - }, 500); - } - function stopTimer() { - clearTimeout(scrollTimer); - keepScrolling = false; - } - - this.oScroller.onmouseout = function(e) { - SCROLLANIM.control.stop = true; - setButtonState(SCROLL_BOTH, SCROLL_OFF); - }; - - - /*apf.addEventListener("mousescroll", function(e) { - var found = (e.target == _self.$buttons); - while (!found && e.target != document.body) { - e.target = e.target.offsetParent; - found = (e.target == _self.$buttons); - } - if (!found) return; - var dir = e.delta > 0 ? SCROLL_LEFT : SCROLL_RIGHT; - e.delta = Math.abs(e.delta); - _self.scroll(e, dir); - });* / - - - this.oLeftScroll = apf.getNode(this.oScroller, [0]); - this.oRightScroll = apf.getNode(this.oScroller, [1]); - - ["oLeftScroll", "oRightScroll"].forEach(function(sBtn) { - var dir = sBtn == "oLeftScroll" ? SCROLL_LEFT : SCROLL_RIGHT, - revDir = sBtn == "oLeftScroll" ? SCROLL_RIGHT : SCROLL_LEFT; - - _self[sBtn].ondbclick = - _self[sBtn].onmousedown = function(e) { - SCROLLANIM.control.stop = false; - var state = dir & SCROLL_LEFT ? SCROLL_L_STATE : SCROLL_R_STATE; - if (this.className.indexOf("disabled") != -1 - || state & SCROLL_DOWN) return; - e = e || event; - _self.scroll(e, dir); - startTimer(e, dir); - if (!apf.isSafariOld) - this.onmouseout(); - }; - _self[sBtn].onmouseover = function() { - SCROLLANIM.control.stop = false; - var state = dir & SCROLL_LEFT ? SCROLL_L_STATE : SCROLL_R_STATE; - if (this.className.indexOf("disabled") != -1 - || state & SCROLL_DOWN) return; - setButtonState(dir, SCROLL_HOVER); - setButtonState(revDir, SCROLL_OFF); - globalDir = dir; - }; - _self[sBtn].onmouseout = function() { - var state = dir & SCROLL_LEFT ? SCROLL_L_STATE : SCROLL_R_STATE; - if (this.className.indexOf("disabled") != -1 - || state & SCROLL_DOWN) return; - setButtonState(dir, SCROLL_OFF); - }; - _self[sBtn].onmouseup = function() { - if (this.className.indexOf("disabled") == -1) { - setButtonState(dir, SCROLL_OFF); - } - stopTimer(); - SCROLLANIM.control.stop = true; - }; - }); - } - - - apf.layout.setRules(this.$ext, this.$uniqueId + "_tabscroller", - "var o = apf.all[" + this.$uniqueId + "]; o && o.correctScrollState()"); - apf.layout.queue(this.$ext);*/ - - - - //Skin changing support - if (this.$int) { - //apf.AmlParser.replaceNode(this.oPages, oPages); - this.$int = this.oPages; - page = true; - - //@todo apf3.0 skin change? - nodes = this.childNodes; - for (i = 0; i < nodes.length; i++) { - node = nodes[i]; - if (node.nodeType != 1) - continue; - node.$draw(true); - if (node.$skinchange) - node.$skinchange(); - node.$loadAml(); - } - } - else { - this.$int = this.oPages; - - //Build children - nodes = this.getPages(); - if (nodes.length) { - nodes[0].$first(); - (node = nodes[nodes.length - 1]).$last(); - } - } - - //Set active page - if (node) { - this.activepage = (typeof this.activepage != "undefined" - ? this.activepage - : this.activepagenr) || 0; - page = this.getPage(this.activepage); - if (!page.render || page.$rendered) - this.$propHandlers.activepage.call(this, this.activepage); - } - else { - this.isPages = false; - } - - - this.setProperty("length", this.getPages().length); - - - this.ready = true; - - /*window.setTimeout(function() { - _self.setScrollerState(); - }, 0);*/ - - - if (!this.activepage && this.getAttribute("src")) { - this.src = this.getAttribute("src"); - this.$propHandlers["activepage"].call(this); - } - }; - - this.$destroy = function(bSkinChange) { - if (bSkinChange || !this.oScroller) - return; - - - /*apf.layout.removeRule(this.$ext, this.$uniqueId + "_tabscroller"); - - [this.oLeftScroll, this.oRightScroll].forEach(function(oBtn) { - oBtn.onmousedown = oBtn.ondblclick = oBtn.onmouseover = - oBtn.onmouseout = oBtn.onmouseup = null; - });*/ - - }; -}).call(apf.BaseTab.prototype = new apf.Presentation()); - - - - - -/** - * An element displaying a page and several buttons allowing a - * user to switch between the pages. Each page can contain - * arbitrary AML. Each page can render its content during - * startup of the application, or when the page is activated. - * - * #### Example - * - * ```xml, demo - * - * - * - * - * Example - * Example - * - * - * Test checkbox - * Test checkbox - * Test checkbox - * - * - * This ok? - * This better? - * - * - * - * - * ``` - * - * @class apf.tab - * @define tab - * @container - * @allowchild page - * - * - * @author Ruben Daniels (ruben AT ajax DOT org) - * @version %I%, %G% - * @since 0.1 - * - * @inherits apf.BaseTab - */ - -apf["switch"] = function(struct, tagName) { - this.$hasButtons = false; - this.$init(tagName || "switch", apf.NODE_VISIBLE, struct); -}; - -apf.pages = function(struct, tagName) { - this.$hasButtons = false; - this.$init(tagName || "pages", apf.NODE_VISIBLE, struct); - - this.$focussable = false; -}; - -apf.tab = function(struct, tagName) { - this.$hasButtons = true; - this.$init(tagName || "tab", apf.NODE_VISIBLE, struct); -}; - -(function(){ - this.$focussable = apf.KEYBOARD; // This object can get the focus from the keyboard - - // *** Init *** // - - this.$draw = function(bSkinChange) { - //Build Main Skin - this.$ext = this.$getExternal(); - this.$loadChildren(); - }; -}).call(apf.tab.prototype = new apf.BaseTab()); - -apf["switch"].prototype = -apf.pages.prototype = apf.tab.prototype; - -apf.aml.setElement("switch", apf["switch"]); -apf.aml.setElement("pages", apf.pages); -apf.aml.setElement("tab", apf.tab); - - - -}; -}); \ No newline at end of file diff --git a/plugins/c9.ide.ui/lib/splitbox.js b/plugins/c9.ide.ui/lib/splitbox.js deleted file mode 100644 index febfabb3..00000000 --- a/plugins/c9.ide.ui/lib/splitbox.js +++ /dev/null @@ -1,1568 +0,0 @@ -define(function(require, module, exports) { -return function(apf) { -var $setTimeout = setTimeout; -var $setInterval = setInterval; - - -/** - * This abstraction is using for resizing block elements. Resizing is allowed - * with square elements in vertical, horizontal or both planes. Symmetric - * resizing is possible with SHIFT button. - * - * @private - * @default_private - * @constructor - * - * @author Lukasz Lipinski - * @version %I%, %G% - * @since 1.0 - * - */ - -apf.resize = function() { - /** - * {Boolean} scalex resizing in horizontal plane, default is true - * Possible values: - * true resizing in horizontal plane is allowed - * false resizing in horizontal plane is not allowed - * {Boolean} scaley resizing in vertical plane, default is true - * Possible values: - * true resizing in vertical plane is allowed - * false resizing in vertical plane is not allowed - * {Boolean} scaleratio resizing in horizontal or vertical plane only is not allowed. Resizing in two dimensions plane at the same time is allowed. - * Possible values: - * true resizing in two dimensions plane at the same time is allowed - * false Resizing in two dimensions plane at the same time is not allowed - * {Number} dwidth the minimal horizontal size of Block element, default is 56 pixels - * {Number} dheight the minimal vertical size of Block element, default is 56 pixels - */ - this.scales = { - scalex: false, - scaley: false, - scaleratio: false, - dwidth: 0, - dheight: 0, - snap: false, - gridW: 48, - gridH: 48 - }; - - /* - * html representation of resized block element - */ - this.htmlElement; - - /** - * store object representations of inputs elements - */ - var squares = []; - - this.init = function() { - squares = [ - new apf.resize.square("top", "left", this), - new apf.resize.square("top", "middle", this), - new apf.resize.square("top", "right", this), - new apf.resize.square("middle", "left", this), - new apf.resize.square("middle", "right", this), - new apf.resize.square("bottom", "left", this), - new apf.resize.square("bottom", "middle", this), - new apf.resize.square("bottom", "right", this)]; - }; - - /** - * Links block element with resize feature - * - * @param {HTMLElement} oHtml html representation of block element - * @param {Object} scales blocks scale settings - */ - this.grab = function(oHtml, scales) { - this.htmlElement = oHtml; - this.scales = scales; - - if (!squares.length) - this.init(); - this.show(); - }; - - /** - * Hides all block squares - */ - this.hide = function() { - for (var i = 0, l = squares.length; i < l; i++) { - squares[i].visible = false; - squares[i].repaint(); - } - }; - - /** - * Shows all block squares - */ - this.show = function() { - var sx = this.scales.scalex; - var sy = this.scales.scaley; - var sr = this.scales.scaleratio; - - for (var i = 0, l = squares.length, s; i < l; i++) { - s = squares[i]; - s.visible = sx && sy - ? true - : (sy && !sx - ? (s.posX == "middle" - ? true - : false) - : (sx && !sy - ? (s.posY == "middle" - ? true - : false) - : (sr - ? ((s.posY == "top" || s.posY == "bottom") - && s.posX !== "middle" - ? true - : false) - : false))); - - s.repaint(); - } - }; - - /** - * Destroys all block squares - */ - this.destroy = function(){ - for (var i = 0; i < squares.length; i++) { - squares[i].destroy(); - } - }; -}; - -/* - * Creates html and object representation for square element. Square is used for - * resizing block elements. - * - * @param {String} posY square vertical align relative to resized block element - * Possible values: - * top square is on top of resized block element - * middle square is in the middle of the resized block element - * bottom square is on the bottom of resized block element - * @param {String} posX square vertical align relative to resized block element - * Possible values: - * left square is on the left of resized block element - * middle square is in the middle of the resized block element - * right square is on the right of resized block element - * @param {Object} objResize object of resize class - * @constructor - */ -apf.resize.square = function(posY, posX, objResize) { - /* - * Square visibility - */ - this.visible = true; - /* - * square vertical align relative to resized block element - */ - this.posX = posX; - /* - * square vertical align relative to resized block element - */ - this.posY = posY; - - var margin = 0; - var _self = this; - - /* - * html represenation of square element - */ - this.htmlElement = objResize.htmlElement.parentNode.appendChild(document.createElement('div')); - apf.setStyleClass(this.htmlElement, "square"); - - /* - * Repaints square - */ - this.repaint = function() { - if (this.visible) { - var block = objResize.htmlElement; - this.htmlElement.style.display = "block"; - - var bw = parseInt(block.style.width) + apf.getDiff(block)[0]; - var bh = parseInt(block.style.height) + apf.getDiff(block)[1]; - var bt = parseInt(block.style.top); - var bl = parseInt(block.style.left); - - var sw = this.htmlElement.offsetWidth; - var sh = this.htmlElement.offsetHeight; - - var t = posY == "top" - ? bt - margin - sh - : posY == "middle" - ? bt + bh/2 - sh/2 - : bt + bh + margin; - var l = posX == "left" - ? bl - margin - sw - : posX == "middle" - ? bl + bw/2 - sw/2 - : bl + bw + margin; - - var c = (posY == "middle" - ? "w-resize" - : (posX == "middle" - ? "n-resize" - : (posY + posX == "topleft" - || posY + posX == "bottomright") - ? "nw-resize" - : "ne-resize")); - - this.htmlElement.style.top = (t - 1) + "px"; - this.htmlElement.style.left = (l - 1) + "px"; - this.htmlElement.style.cursor = c; - } - else { - //IE bug - var sw = this.htmlElement.offsetWidth; - this.htmlElement.style.display = 'none'; - } - }; - - this.destroy = function(){ - apf.destroyHtmlNode(this.htmlElement); - }; - - /* Events */ - this.htmlElement.onmouseover = function(e) { - apf.setStyleClass(_self.htmlElement, "squareHover"); - }; - - this.htmlElement.onmouseout = function(e) { - apf.setStyleClass(_self.htmlElement, "", ["squareHover"]); - }; - - this.htmlElement.onmousedown = function(e) { - e = (e || event); - - var block = objResize.htmlElement, - - sx = e.clientX, - sy = e.clientY, - - pt = block.parentNode.offsetTop, - pl = block.parentNode.offsetLeft, - - dw = objResize.scales.dwidth, - dh = objResize.scales.dheight, - - snap = objResize.scales.snap, - gridH = objResize.scales.gridH, - gridW = objResize.scales.gridW, - - objBlock = apf.flow.isBlock(block), - r = objBlock.other.ratio, - - posX = _self.posX, - posY = _self.posY, - - width, height, top, left, dx, dy, - prev_w, prev_h, - - l = parseInt(block.style.left), - t = parseInt(block.style.top), - w = parseInt(block.style.width), - h = parseInt(block.style.height), - resized = false; - - objResize.onresizedone(w, h, t, l); - - if (e.preventDefault) { - e.preventDefault(); - } - - document.onmousemove = function(e) { - e = (e || event); - - dx = e.clientX - sx; - dy = e.clientY - sy; - var shiftKey = e.shiftKey, - proportion = r; - - if (shiftKey) { - if (posX == "right" && posY == "bottom") { - width = w + dx; - height = width/proportion; - left = l; - top = t; - } - else if (posX == "right" && posY == "top") { - width = w + dx; - height = width/proportion; - left = l; - top = t - dx/proportion; - } - else if (posX == "left" && posY == "bottom") { - width = w - dx; - height = width/proportion; - left = l + dx; - top = t; - } - else if (posX == "left" && posY == "top") { - width = w - dx; - height = width/proportion; - left = l + dx; - top = t + dx/proportion; - } - - /* Keep minimal size */ - if (width >= dw && height >= dh) { - width = prev_w = Math.max(dw, width); - height = prev_h = Math.max(dh, height); - } - else { - width = prev_w; - height = prev_h; - return false; - } - } - else { - width = posX == "right" - ? w + dx - : (posX == "left" - ? w - dx - : w); - height = posY == "bottom" - ? h + dy - : (posY == "top" - ? h - dy - : h); - left = posX == "right" - ? l - : (posX == "left" - ? Math.min(l + w - dw, l + dx) - : l); - top = posY == "bottom" - ? t - : (posY == "top" - ? Math.min(t + h - dh, t + dy) - : t); - - /* Keep minimal size */ - width = Math.max(dw, width); - height = Math.max(dh, height); - } - - if (snap) { - left = Math.floor(left / gridW) * gridW; - top = Math.floor(top / gridH) * gridH; - width = Math.ceil(width / gridW) * gridW; - height = Math.ceil(height / gridH) * gridH; - } - - if (objResize.onresize) { - objResize.onresize(block, top, left, width, height); - } - - objResize.show(); - - resized = true; - }; - - document.onmouseup = function(e) { - document.onmousemove = null; - if (objResize.onresizedone && resized) { - objResize.onresizedone(width, height, top, left); - objBlock.other.ratio = width / height; - resized = false; - } - }; - }; -}; - - - - -/** - * - * A container that stacks two children vertically. - * - * Programatically, this is identical to a regular [[vbox]], except that it can - * only accept two children, and uses absolute positioning. Because of this, there - * is more work required to construct AML that matches a regular ``; however, - * the performance improvements in using a `` are massive. - * - * @class apf.vsplitbox - * @define vsplitbox - * @layout - * - * @inheritDoc apf.hsplitbox - * - * @author Ruben Daniels (ruben AT ajax DOT org) - * @version %I%, %G% - * @since 3.0 - * @see element.hsplitbox - */ -/** - * - * A container that stacks two children horizontally. - * - * Programatically, this is identical to a regular [[apf.hbox]], except that it can - * only accept two children, and uses absolute positioning. Because of this, there - * is more work required to construct AML that matches a regular ``; however, - * the performance improvements in using a `` are massive. - * - * @class apf.hsplitbox - * @define hsplitbox - * @layout - * @inherits apf.GuiElement - * - * @author Ruben Daniels (ruben AT ajax DOT org) - * @version %I%, %G% - * @since 3.0\ - * @see element.vsplitbox - */ -apf.hsplitbox = function(struct, tagName) { - this.$init(tagName || "hsplitbox", apf.NODE_VISIBLE, struct); -}; -apf.vsplitbox = function(struct, tagName) { - this.$init(tagName || "vsplitbox", apf.NODE_VISIBLE, struct); -}; - -(function(){ - this.minwidth = 0; - this.minheight = 0; - - this.padding = 0; - this.edge = 0; - this.$edge = [0,0,0,0]; - - // *** Properties and Attributes *** // - - this.$focussable = false; - this.$useLateDom = true; - this.$box = true; - this.$layout = true; - - /** - * @attribute {String} [padding="2"] Sets or gets the space between each element. - */ - /** - * @attribute {String} [edge="5 5 5 5"] Sets or gets the space between the container and the elements, space seperated in pixels for each side. Similar to CSS in the sequence of `top right bottom left`. - */ - this.$booleanProperties["splitter"] = true; - this.$supportedProperties.push("padding", "edge", "splitter"); - - this.$propHandlers["padding"] = function(value) { - this.padding = parseInt(value); - - if (!this.$amlLoaded) - return; - - if (this.$handle) - this.$handle.$ext.style[this.$vbox ? "height" : "width"] = value + "px"; - - var firstChild = this.getFirstChild(); - var lastChild = this.getSecondChild(); - - if (this.$vbox) { - //Two flex children - if (this.flexChild2) { - if (firstChild.height) { - - // This is not needed because with bottom: xx% it already works - - // if (String(firstChild.height).indexOf("%") == -1) { - lastChild.$ext.style.marginTop = firstChild.visible - ? value + "px" // + apf.getHeightDiff(firstChild.$ext) - : 0; - // } - } - else { - firstChild.$ext.style.marginBottom = lastChild.visible - ? (value - + apf.getHeightDiff(lastChild.$ext)) + "px" - : 0; - } - } - else if (this.fixedChild && this.fixedChild.visible) { - //One flex child (first) - if (this.flexChild1 == firstChild) { - if (this.fixedChild.visible) { - this.flexChild1.$ext.style.bottom = - (parseInt(this.fixedChild.height) + value + this.$edge[2]) + "px"; - } - } - - //One flex child (last) - else if (lastChild && this.flexChild1 == lastChild) { - this.flexChild1.$ext.style.top = - (parseInt(this.fixedChild.height) + value + this.$edge[2]) + "px"; - } - } - } - else { - //Two flex children - if (this.flexChild2) { - if (firstChild.width) { - lastChild.$ext.style.marginLeft = - (value - + apf.getWidthDiff(firstChild.$ext) - + apf.getMargin(firstChild.$ext)[0]) + "px"; - } - else { - firstChild.$ext.style.marginRight = - (value + (lastChild - ? apf.getWidthDiff(lastChild.$ext) - + apf.getMargin(lastChild.$ext)[0] - : 0)) + "px"; - } - } - else if (this.fixedChild && this.fixedChild.visible) { - //One flex child (first) - if (this.flexChild1 == firstChild) { - this.flexChild1.$ext.style.right = - (parseInt(this.fixedChild.width) + value + this.$edge[1]) + "px"; - } - - //One flex child (last) - else if (lastChild && this.flexChild1 == lastChild) { - this.flexChild1.$ext.style.left = - (parseInt(this.fixedChild.width) + value + this.$edge[3]) + "px"; - } - } - } - } - - this.$propHandlers["splitter"] = function(value) { - if (value) { - if (this.$handle) - this.$handle.show(); - else { - this.$handle = this.insertBefore( - this.ownerDocument.createElementNS(apf.ns.aml, "splitter"), - this.lastChild); - } - } - else { - this.$handle && this.$handle.hide();//destroy(true, true); - } - } - - this.$propHandlers["edge"] = function(value, setSize) { - this.$edge = apf.getBox(value); - - if (!this.$amlLoaded) - return; - - var fNode = this.getFirstVisibleChild(); - if (!fNode) { - this.hide(); - return false; - } - fNode.$ext.style.left = (this.$edge[3] + fNode.$margin[3]) + "px"; - fNode.$ext.style.top = (this.$edge[0] + fNode.$margin[0]) + "px"; - if (this.$vbox) - fNode.$ext.style.right = (this.$edge[1] + fNode.$margin[1]) + "px"; - else - fNode.$ext.style.bottom = (this.$edge[2] + fNode.$margin[2]) + "px"; - - var lNode = this.getSecondVisibleChild(); - if (lNode && lNode.visible && lNode.$ext) { - lNode.$ext.style.right = (this.$edge[1] + lNode.$margin[1]) + "px"; - lNode.$ext.style.bottom = (this.$edge[2] + lNode.$margin[2]) + "px"; - if (this.$vbox) { - var isPercentage; - - lNode.$ext.style.left = (this.$edge[3] + lNode.$margin[3]) + "px"; - if (fNode.height || fNode.height === 0) { - isPercentage = String(fNode.height).indexOf("%") > -1; - lNode.$ext.style.top = isPercentage - ? fNode.height - : ((parseInt(fNode.height) + this.padding - + this.$edge[0] + fNode.$margin[0]) + "px"); - if (isPercentage) { - fNode.$ext.style.height = ""; - fNode.$ext.style.bottom = (100 - parseFloat(fNode.height)) + "%"; - } - - if (this.$handle) { - this.$handle.$ext.style.top = isPercentage - ? fNode.height - : ((parseInt(fNode.height) + this.$edge[0]) + "px"); -// this.$handle.$ext.style.marginTop = isPercentage -// ? this.padding + "px" -// : "0"; - } - } - else { - isPercentage = String(lNode.height).indexOf("%") > -1; - lNode.$ext.style.top = ""; - fNode.$ext.style.bottom = isPercentage - ? lNode.height - : ((parseInt(lNode.height) + this.padding - + this.$edge[2] + lNode.$margin[2]) + "px"); - if (isPercentage) { - lNode.$ext.style.height = ""; - lNode.$ext.style.top = (100 - parseFloat(lNode.height)) + "%"; - } - - if (this.$handle) { - this.$handle.$ext.style.bottom = isPercentage - ? lNode.height - : ((parseInt(lNode.height) + this.$edge[0]) + "px"); -// this.$handle.$ext.style.marginBottom = isPercentage -// ? this.padding + "px" -// : "0"; - } - } - - if (this.$handle) { - this.$handle.$ext.style.left = this.$edge[3] + "px"; - this.$handle.$ext.style.right = this.$edge[1] + "px"; - } - } - else { - lNode.$ext.style.top = this.$edge[0] + lNode.$margin[0] + "px"; - - if (fNode.width || fNode.width === 0) { - var isPercentage = String(fNode.width).indexOf("%") > -1; - lNode.$ext.style.left = isPercentage - ? fNode.width - : ((parseInt(fNode.width) + this.padding - + this.$edge[3] + fNode.$margin[3]) + "px"); - if (isPercentage) { - fNode.$ext.style.width = ""; - fNode.$ext.style.right = (100 - parseFloat(fNode.width)) + "%"; - } - - if (this.$handle) { - this.$handle.$ext.style.left = isPercentage - ? fNode.width - : ((parseInt(fNode.width) + this.$edge[3]) + "px"); -// this.$handle.$ext.style.marginLeft = isPercentage -// ? this.padding + "px" -// : "0"; - } - } - else { - var isPercentage = String(lNode.width).indexOf("%") > -1; - lNode.$ext.style.left = ""; - fNode.$ext.style.right = isPercentage - ? lNode.width - : ((parseInt(lNode.width) + this.padding - + this.$edge[1] + lNode.$margin[1]) + "px"); - if (isPercentage) { - lNode.$ext.style.width = ""; - lNode.$ext.style.left = (100 - parseFloat(lNode.width)) + "%"; - } - - if (this.$handle) { - this.$handle.$ext.style.right = isPercentage - ? lNode.width - : ((parseInt(lNode.width) + this.$edge[3]) + "px"); -// this.$handle.$ext.style.marginRight = isPercentage -// ? this.padding + "px" -// : "0"; - } - } - - if (this.$handle) { - this.$handle.$ext.style.top = this.$edge[0] + "px"; - this.$handle.$ext.style.bottom = this.$edge[2] + "px"; - } - } - - if (this.$handle) - this.$handle.$ext.style.position = "absolute"; - } - else { - if (!this.$vbox) { - fNode.$ext.style.right = (this.$edge[1] + fNode.$margin[1]) + "px"; - fNode.$ext.style.width = ""; - } - else { - fNode.$ext.style.bottom = (this.$edge[2] + fNode.$margin[2]) + "px"; - fNode.$ext.style.height = ""; - } - - if (this.$handle) - this.$handle.hide(); - } - - if (setSize === true) { - var size = this.$vbox ? "height" : "width"; - fNode.$propHandlers[size].call(fNode, fNode[size]); - } - }; - - this.getFirstChild = function(startNode) { - var node = startNode || this.firstChild; - while (node && node.$splitter) { - node = node.nextSibling; - } - return node || false; - } - this.getSecondChild = function(){ - var node = this.getFirstChild(); - if (!node) - return false; - return node.nextSibling && this.getFirstChild(node.nextSibling); - } - - this.getFirstVisibleChild = function(startNode) { - var node = startNode || this.firstChild; - while (node && (!node.visible || node.$splitter)) { - node = node.nextSibling; - } - if (node && node.visible) - return node; - return false; - } - - this.getSecondVisibleChild = function(){ - var node = this.getFirstVisibleChild(); - if (!node) - return false; - return node.nextSibling && this.getFirstVisibleChild(node.nextSibling); - } - - function visibleHandler(e) { - if (this.parentNode.$handle) { - if (!e.value || this.parentNode.childNodes.length < 3) - this.parentNode.$handle.hide(); - else - this.parentNode.$handle.show(); - } - - if (e.value && !this.parentNode.visible) - this.parentNode.show(); - - this.parentNode.$propHandlers.edge - .call(this.parentNode, this.parentNode.edge, true); - - // apf.layout.forceResize(this.parentNode.$int); - - //Change margin - this.parentNode.$propHandlers.padding - .call(this.parentNode, this.parentNode.padding) - } - - var handlers = { - "width" : function(value, isLast) { - //@todo this should check the largest and only allow that one - //if (this.parentNode.$vbox && this.parentNode.align == "stretch") - //return; - - //@todo change fixedChild flexChild1 and flexChild2 based on this - - this.$ext.style.width = !apf.isNot(value) - ? (parseFloat(value) == value - ? (value - apf.getWidthDiff(this.$ext)) + "px" - : value) - : ""; - - //This can be optimized - if (this.$amlLoaded && isLast !== false) - this.parentNode.$propHandlers["edge"].call(this.parentNode, - this.parentNode.edge); - }, - - "height" : function(value, isLast) { - //@todo this should check the largest and only allow that one - //if (!this.parentNode.$vbox && this.parentNode.align == "stretch") - //return; - - //@todo change fixedChild flexChild1 and flexChild2 based on this - - this.$ext.style.height = !apf.isNot(value) - ? (parseFloat(value) == value - ? (value - apf.getHeightDiff(this.$ext)) + "px" - : value) - : ""; - - //This can be optimized - if (this.$amlLoaded && isLast !== false) - this.parentNode.$propHandlers["edge"].call(this.parentNode, - this.parentNode.edge); - }, - - "margin" : function(value, isLast) { - this.$margin = apf.getBox(value); - - //This can be optimized - if (this.$amlLoaded && isLast !== false) - this.parentNode.$propHandlers["edge"].call(this.parentNode, this.parentNode.edge); - } - } - - this.register = function(amlNode, insert) { - if (amlNode.$splitter || amlNode.nodeFunc != apf.NODE_VISIBLE) - return; - - amlNode.$margin = [0, 0, 0, 0]; - - amlNode.$propHandlers["left"] = - amlNode.$propHandlers["top"] = - amlNode.$propHandlers["right"] = - amlNode.$propHandlers["bottom"] = apf.K; - - for (var prop in handlers) { - amlNode.$propHandlers[prop] = handlers[prop]; - } - - var fNode = this.getFirstChild(); - var sNode = fNode && this.getSecondChild(); - var prop = this.$vbox ? "height" : "width"; - - this.flexChild1 = this.flexChild2 = this.fixedChild = null; - - if (!fNode[prop] || ~String(fNode[prop]).indexOf("%")) - this.flexChild1 = fNode; - else - this.fixedChild = fNode; - - if (sNode) { - if (!sNode[prop] || ~String(sNode[prop]).indexOf("%")) - this[this.flexChild1 ? "flexChild2" : "flexChild1"] = sNode; - else - this.fixedChild = sNode; - } - - // if (this.flexChild1 && this.flexChild1 == amlNode){ } - // else if (this.$vbox) { - // if (!amlNode.height || String(amlNode.height).indexOf("%") > -1) - // this[!this.flexChild1 ? "flexChild1" : "flexChild2"] = amlNode; - // else - // this.fixedChild = amlNode; - // } - // else { - // if (!amlNode.width || String(amlNode.width).indexOf("%") > -1) - // this[!this.flexChild1 ? "flexChild1" : "flexChild2"] = amlNode; - // else - // this.fixedChild = amlNode; - // } - - amlNode.addEventListener("prop.visible", visibleHandler); - amlNode.$ext.style.position = "absolute"; - amlNode.$ext.style.margin = ""; - - if (amlNode.height) - handlers.height.call(amlNode, amlNode.height, false); - if (amlNode.width) - handlers.width.call(amlNode, amlNode.width, false); - if (amlNode.margin) - handlers.margin.call(amlNode, amlNode.margin, false); - - var isLast = this.lastChild.$amlLoaded || this.lastChild == amlNode; - if (isLast) { - this.$propHandlers["padding"].call(this, this.padding); - this.$propHandlers["edge"].call(this, this.edge, - !amlNode[this.$vbox ? "height" : "width"]); - } - - if (this.$handle && this.childNodes.length > 2) - this.$handle.show(); - } - - this.unregister = function(amlNode) { - if (amlNode.$splitter || amlNode.nodeFunc != apf.NODE_VISIBLE) - return; - - delete amlNode.$margin; - - amlNode.$propHandlers["left"] = - amlNode.$propHandlers["top"] = - amlNode.$propHandlers["right"] = - amlNode.$propHandlers["bottom"] = null; - - for (var prop in handlers) { - delete amlNode.$propHandlers[prop]; - } - - if (this.fixedChild == amlNode) - delete this.fixedChild; - else if (this.flexChild1 == amlNode) - delete this.flexChild1; - else if (this.flexChild2 == amlNode) - delete this.flexChild2; - - //Clear css properties and set layout - amlNode.removeEventListener("prop.visible", visibleHandler); - amlNode.$ext.style.display = amlNode.visible ? "block" : "none"; - - if (amlNode.width) - amlNode.$ext.style.width = ""; - if (amlNode.height) - amlNode.$ext.style.height = ""; - amlNode.$ext.style.position = - amlNode.$ext.style.margin = - amlNode.$ext.style.left = - amlNode.$ext.style.top = - amlNode.$ext.style.right = - amlNode.$ext.style.bottom = ""; - - if (this.$handle) - this.$handle.hide(); - } - - // *** DOM Hooks *** // - - this.addEventListener("DOMNodeRemoved", function(e) { - if (e.$doOnlyAdmin || e.currentTarget == this) - return; - - if (e.relatedNode == this) { - this.unregister(e.currentTarget); - //e.currentTarget.$setLayout(); - } - }); - - this.addEventListener("DOMNodeInserted", function(e) { - if (e.currentTarget == this) { - return; - } - - if (e.currentTarget.nodeType != 1 - || e.currentTarget.nodeFunc != apf.NODE_VISIBLE) - return; - -// if (this.$handle) { -// var _self = this; -// setTimeout(function(){ -// if (_self.$handle.nextSibling != _self.lastChild) -// _self.insertBefore(_self.$handle, _self.lastChild); -// }); -// } - - if (e.relatedNode == this && !e.$isMoveWithinParent) { - e.currentTarget.$setLayout(this.localName, true); - - if (e.currentTarget.$altExt) { - - return false; - } - } - }); - - this.$draw = function(){ - var doc = this.$pHtmlNode.ownerDocument; - this.$ext = this.$pHtmlNode.appendChild(doc.createElement("div")); - if (this.getAttribute("style")) - this.$ext.setAttribute("style", this.getAttribute("style")); - this.$ext.className = this.localName; - - this.$vbox = this.localName == "vsplitbox"; - this.$int = this.$ext; - this.$ext.host = this; - - if (this.getAttribute("class")) - apf.setStyleClass(this.$ext, this.getAttribute("class")); - }; - - this.$loadAml = function(x) { - }; -}).call(apf.vsplitbox.prototype = new apf.GuiElement()); - -apf.hsplitbox.prototype = apf.vsplitbox.prototype; - -apf.aml.setElement("hsplitbox", apf.hsplitbox); -apf.aml.setElement("vsplitbox", apf.vsplitbox); - - -/** - * @constructor - * @private - */ -apf.splitter = function(struct, tagName) { - this.$init(tagName || "splitter", apf.NODE_VISIBLE, struct); -}; - -(function() { - this.minwidth = 0; - this.minheight = 0; - - this.$scale = 0; // 0 both, 1 left/top, 2 right/bottom - - this.$focussable = false; // This object can get the focus - this.$splitter = true; - - this.$booleanProperties["realtime"] = true; - - this.$propHandlers["realtime"] = function(value) { - this.$setStyleClass(this.$ext, value && (this.$baseCSSname + "Realtime") || "", - [this.$baseCSSname + "Realtime"]); - }; - - this.$propHandlers["scale"] = function(value) { - this.$scale = value == "left" || value == "top" - ? 1 : (value == "right" || "bottom " - ? 2 : 0); - }; - - this.$propHandlers["parent"] = function(value) { - this.$parent = typeof value == "object" ? value : self[value]; - }; - - this.$propHandlers["type"] = function(value) { - this.$setStyleClass(this.$ext, value, - [value == "horizontal" ? "vertical" : "horizontal"]); - - if (value == "vertical") - this.$setStyleClass(this.$ext, "w-resize", ["n-resize"]); - else - this.$setStyleClass(this.$ext, "n-resize", ["w-resize"]); - - //Optimize this to not recalc for certain cases - if (value == "horizontal") { - this.$info = { - pos: "top", - opos: "left", - size: "width", - osize: "height", - offsetPos: "offsetTop", - offsetSize: "offsetHeight", - oOffsetPos: "offsetLeft", - oOffsetSize: "offsetWidth", - clientPos: "clientY", - d1 : 1, - d2 : 0, - x1 : 0, - x2 : 2 - }; - } - else { - this.$info = { - pos: "left", - opos: "top", - size: "height", - osize: "width", - offsetPos: "offsetLeft", - offsetSize: "offsetWidth", - oOffsetPos: "offsetTop", - oOffsetSize: "offsetHeight", - clientPos: "clientX", - d1 : 0, - d2 : 1, - x1 : 3, - x2 : 1 - }; - } - }; - - this.addEventListener("DOMNodeInserted", function(e) { - if (e.currentTarget != this) - return; - - /*if (e.$oldParent) { - e.$oldParent.removeEventListener("DOMNodeInserted", this.$siblingChange); - e.$oldParent.removeEventListener("DOMNodeRemoved", this.$siblingChange); - }*/ - - this.init && this.init(); - }); - - /*this.$siblingChange = function(e) { - //if (e.currentTarget - - //this.init(); - }*/ - - this.$draw = function(){ - //Build Main Skin - this.$ext = this.$getExternal(); - - var template = "vbox|hbox".indexOf(this.parentNode.localName) > -1 - ? "box" : "splitbox"; - - apf.extend(this, apf.splitter.templates[template]); - this.decorate(); - }; - - this.$loadAml = function(x) { - if (this.realtime !== false) - this.$propHandlers.realtime.call(this, this.realtime = true); - }; -}).call(apf.splitter.prototype = new apf.Presentation()); - -apf.splitter.templates = { - box: { - update: function(newPos, finalPass) { - with (this.$info) { - //var pos = Math.ceil(apf.getAbsolutePosition(this.$ext, this.parentNode.$int)[d1] - posPrev[d1]); - var max = this.$previous - ? this.$previous.$ext[offsetSize] + this.$next.$ext[offsetSize] - : (this.parentNode).getWidth(); - var method = finalPass ? "setAttribute" : "setProperty"; - if (apf.hasFlexibleBox) - newPos -= this.$previous ? apf.getAbsolutePosition(this.$previous.$ext, this.parentNode.$int)[d1] : 0; - - //Both flex - if (this.$previous && this.$next && (this.$previous.flex || this.$previous.flex === 0) && (this.$next.flex || this.$next.flex === 0)) { - if (!finalPass && !this.realtime) - newPos -= this.$ext[offsetSize]; - - //var totalFlex = this.$previous.flex + this.$next.flex - (finalPass && !this.realtime ? this.parentNode.padding : 0); - if (!this.$scale || this.$scale == 1) - this.$previous[method]("flex", newPos); - if (!this.$scale || this.$scale == 2) - this.$next[method]("flex", this.$totalFlex - newPos); - } - //Fixed - else { - if (this.$next && !this.$next.flex && (!this.$scale || this.$scale == 2)) - this.$next[method](osize, max - newPos); - if (this.$previous && !this.$previous.flex && (!this.$scale || this.$scale == 1)) - this.$previous[method](osize, newPos); - } - } - - if (apf.hasSingleResizeEvent) - apf.layout.forceResize(this.$ext.parentNode); - }, - - $setSiblings: function(){ - this.$previous = this.previousSibling; - while (this.$previous && (this.$previous.nodeType != 1 - || this.$previous.visible === false - || this.$previous.nodeFunc != apf.NODE_VISIBLE)) - this.$previous = this.$previous.previousSibling; - this.$next = this.nextSibling; - while (this.$next && (this.$next.nodeType != 1 - || this.$next.visible === false - || this.$next.nodeFunc != apf.NODE_VISIBLE)) - this.$next = this.$next.nextSibling; - }, - - init: function(size, refNode, oItem) { - //this.parentNode.addEventListener("DOMNodeInserted", this.$siblingChange); - //this.parentNode.addEventListener("DOMNodeRemoved", this.$siblingChange); - - this.$setSiblings(); - - this.$thickness = null; - if (this.parentNode && this.parentNode.$box) { - this.setProperty("type", this.parentNode.localName == "vbox" - ? "horizontal" - : "vertical"); - this.$thickness = parseInt(this.parentNode.padding); - } - - if (!this.$previous || !this.$next) - return this; - - with (this.$info) { - var diff = apf.getDiff(this.$ext); - if (!this.parentNode.$box) { - var iSize = Math.max( - this.$previous.$ext[offsetSize], this.$next.$ext[offsetSize]); - this.$ext.style[size] = (iSize - diff[d1]) + "px"; - } - - var iThick = this[osize] = this.$thickness - || (this.$next[oOffsetPos] - this.$previous[oOffsetPos] - - this.$previous[oOffsetSize]); - - this.$ext.style[osize] = (iThick - diff[d2]) + "px"; - } - - return this; - }, - - decorate: function(){ - var _self = this; - this.$ext.onmousedown = function(e) { - if (!e) - e = event; - - if (_self.dispatchEvent("dragstart") === false) - return; - - apf.dragMode = true; //prevent selection - - _self.$setSiblings(); - - var changedPosition, pHtml = _self.parentNode.$int, diff = 0; - if ("absolute|fixed|relative".indexOf(apf.getStyle(pHtml, "position")) == -1) { - pHtml.style.position = "relative"; - changedPosition = true; - } - - _self.$totalFlex = 0; - with (_self.$info) { - if (_self.$parent) { - if (!_self.$previous) { - var posNext = apf.getAbsolutePosition(_self.$next.$ext, _self.parentNode.$int); - var wd = _self.$parent.getWidth(); - - if (_self.$scale == 2) { - var max = posNext[d1] + _self.$next.$ext[offsetSize] - this[offsetSize]; - diff = (_self.parentNode.$int[offsetSize] - max); - var min = max - wd - diff; - } - } - else if (!_self.$next) { - //@todo - } - } - else { - if (_self.$previous) { - var posPrev = apf.getAbsolutePosition(_self.$previous.$ext, _self.parentNode.$int); - var min = _self.$scale - ? 0 - : (posPrev[d1] || 0) + (parseInt(_self.$previous.minwidth) || 0); - } - if (_self.$next) { - var posNext = apf.getAbsolutePosition(_self.$next.$ext, _self.parentNode.$int); - var max = posNext[d1] + _self.$next.$ext[offsetSize] - - this[offsetSize] - (parseInt(_self.$next.minwidth) || 0); - } - } - - //Set flex to pixel sizes - if (_self.$previous && _self.$next) { - if ((_self.$previous.flex || _self.$previous.flex === 0) - && (_self.$next.flex || _self.$next.flex === 0)) { - var set = [], nodes = _self.parentNode.childNodes, padding = 0; - for (var node, i = 0, l = nodes.length; i < l; i++) { - if ((node = nodes[i]).visible === false - || node.nodeFunc != apf.NODE_VISIBLE || node.$splitter) - continue; - - if (node.flex) - set.push(node, node.$ext[offsetSize] - + (apf.hasFlexibleBox && !_self.realtime && node == _self.$previous - ? 2 * _self.parentNode.padding : 0)); - } - for (var i = 0, l = set.length; i < l; i+=2) { - set[i].setAttribute("flex", set[i+1]); - } - } - - _self.$totalFlex += _self.$next.flex + _self.$previous.flex; - } - - var startPos, startOffset; - if (apf.hasFlexibleBox) { - var coords = apf.getAbsolutePosition(this); - startPos = e[clientPos] - coords[d1]; - - if (!_self.realtime) { - if (_self.$previous.flex && !_self.$next.flex) { - var mBox = apf.getBox(_self.$next.margin); - mBox[x1] = _self.parentNode.padding; - _self.$next.$ext.style.margin = mBox.join("px ") + "px"; - } - else { - var mBox = apf.getBox(_self.$previous.margin); - mBox[x2] = _self.parentNode.padding; - _self.$previous.$ext.style.margin = mBox.join("px ") + "px"; - } - - var diff = apf.getDiff(this); - this.style.left = coords[0] + "px"; - this.style.top = coords[1] + "px"; //(apf.getHtmlTop(this) - Math.ceil(this.offsetHeight/2)) - this.style.width = (this.offsetWidth - diff[0]) + "px"; - this.style.height = (this.offsetHeight - diff[1]) + "px"; - this.style.position = "absolute"; - } - } - else { - var coords = apf.getAbsolutePosition(this.offsetParent); - startOffset = apf.getAbsolutePosition(_self.$previous.$ext)[d1]; - startPos = e[clientPos] - coords[d1]; - - if (!_self.realtime) { - this.style.left = "0px"; - this.style.top = "0px"; - this.style.position = "relative"; - } - min = -1000; //@todo - } - } - - //e.returnValue = false; - //e.cancelBubble = true; - //apf.stopEvent(e); - - - apf.plane.show(this); - - - _self.$setStyleClass(this, _self.$baseCSSname + "Moving"); - - _self.$setStyleClass(document.body, - _self.type == "vertical" ? "w-resize" : "n-resize", - [_self.type == "vertical" ? "n-resize" : "w-resize"]); - - //@todo convert to proper way - document.onmouseup = function(e) { - if (!e) e = event; - - with (_self.$info) { - var newPos; - if (e[clientPos] >= 0) { - var coords = apf.getAbsolutePosition(_self.$ext.offsetParent); - newPos = (Math.min(max, Math.max(min, (e[clientPos] - coords[d1]) - - (apf.hasFlexibleBox ? startPos : startOffset)))) + diff; - } - } - - _self.$setStyleClass(_self.$ext, "", [_self.$baseCSSname + "Moving"]); - _self.$setStyleClass(document.body, "", ["n-resize", "w-resize"]); - - if (changedPosition) - pHtml.style.position = ""; - - if (apf.hasFlexibleBox && !_self.realtime) - (_self.$previous.flex && !_self.$next.flex - ? _self.$next : _self.$previous).$ext.style.margin - = apf.getBox(_self.$previous.margin).join("px ") + "px"; - - if (newPos) - _self.update(newPos, true); - - - apf.plane.hide(); - - - if (!_self.realtime) { - _self.$ext.style.left = ""; - _self.$ext.style.top = ""; - _self.$ext.style[_self.$info.size] = ""; - _self.$ext.style.position = ""; - } - - _self.dispatchEvent("dragdrop"); - - document.onmouseup = - document.onmousemove = null; - - apf.dragMode = false; //return to default selection policy - }; - - //@todo convert to proper way - document.onmousemove = function(e) { - if (!e) e = event; - - with (_self.$info) { - var newPos; - if (e[clientPos] >= 0) { - var coords = apf.getAbsolutePosition(_self.$ext.offsetParent); - newPos = (Math.min(max, Math.max(min, (e[clientPos] - coords[d1]) - - (apf.hasFlexibleBox || !_self.realtime ? startPos : startOffset)))) + diff; - - if (_self.realtime) - _self.update(newPos); - else { - _self.$ext.style[pos] = newPos + "px"; - } - } - } - - apf.stopEvent(e); - //e.returnValue = false; - //e.cancelBubble = true; - - _self.dispatchEvent("dragmove"); - }; - } - - apf.queue.add("splitter" + this.$uniqueId, function(){ - _self.init(); - }); - } - }, - - splitbox: { - update: function(newPos, finalPass) { - this[this.parentNode.$vbox ? "updateV" : "updateH"](newPos, finalPass); - }, - - updateV: function(newPos, finalPass) { - var method = finalPass ? "setAttribute" : "setProperty"; - - var pNode = this.$parent || this.parentNode; - var firstChild = pNode.firstChild; - if (firstChild.localName == "splitter") - firstChild = firstChild.nextSibling; - if (pNode.fixedChild) { - if (pNode.fixedChild == pNode.firstChild) { - pNode.fixedChild[method]("height", newPos - pNode.$edge[0]); - } - else { - pNode.fixedChild[method]("height", - apf.getHtmlInnerHeight(pNode.$int) - newPos - - pNode.padding - pNode.$edge[1]); - } - } - else if (firstChild.height) { - var total = apf.getHtmlInnerHeight(pNode.$int); - firstChild[method]("height", - ((newPos - pNode.$edge[0])/total*100) + "%"); - } - else { - var total = apf.getHtmlInnerHeight(pNode.$int) ; - pNode.lastChild[method]("height", - ((total - newPos - pNode.$edge[2] - pNode.padding)/total*100) + "%"); - } - - apf.dispatchEvent("splitter.resize", { splitter: this, final: finalPass }); - }, - - updateH: function(newPos, finalPass) { - var method = finalPass ? "setAttribute" : "setProperty"; - - var pNode = this.$parent || this.parentNode; - var firstChild = pNode.firstChild; - if (firstChild.localName == "splitter") - firstChild = firstChild.nextSibling; - if (pNode.fixedChild) { - if (pNode.fixedChild == pNode.firstChild) { - pNode.fixedChild[method]("width", newPos - pNode.$edge[3]); - } - else { - pNode.fixedChild[method]("width", - apf.getHtmlInnerWidth(pNode.$int) - newPos - - pNode.padding - pNode.$edge[2]); - } - } - else if (firstChild.width) { - var total = apf.getHtmlInnerWidth(pNode.$int); - firstChild[method]("width", - ((newPos - pNode.$edge[3])/total*100) + "%"); - } - else { - var total = apf.getHtmlInnerWidth(pNode.$int) ; - pNode.lastChild[method]("width", - ((total - newPos - pNode.$edge[1] - pNode.padding)/total*100) + "%"); - } - - apf.dispatchEvent("splitter.resize", { splitter: this, final: finalPass }); - }, - - $setSiblings: function(){ - this.$previous = this.parentNode.firstChild; - this.$next = this.parentNode.lastChild; - }, - - decorate: function(){ - var _self = this; - - if (this.parentNode && this.parentNode.$box) { - this.setProperty("type", this.parentNode.$vbox - ? "horizontal" - : "vertical"); - } - - this.$ext.onmousedown = function(e) { - if (!e) - e = event; - - if (_self.dispatchEvent("dragstart") === false) - return; - - apf.dragMode = true; //prevent selection - - _self.$setSiblings(); - - var pNode = _self.$parent || _self.parentNode; - var firstChild = pNode.firstChild.$splitter ? pNode.childNodes[1] : pNode.firstChild; - if (pNode.$vbox) { - var min = parseInt(firstChild.minheight) + pNode.$edge[0]; - var max = apf.getHtmlInnerHeight(pNode.$ext) - pNode.lastChild.minheight - - pNode.$edge[2] - pNode.padding; - var offset = _self.$ext.getBoundingClientRect().top - e.clientY; - } - else { - var min = parseInt(firstChild.minwidth) + pNode.$edge[3]; - var max = apf.getHtmlInnerWidth(pNode.$ext) - pNode.lastChild.minwidth - - pNode.$edge[1] - pNode.padding; - var offset = _self.$ext.getBoundingClientRect().left - e.clientX; - } - - function update(e, final) { - if (!_self.$ext.offsetParent) - return; - - var newPos, coords; - if (pNode.$vbox) { - if (e.clientY >= 0) { - coords = apf.getAbsolutePosition(_self.$parent ? _self.$parent.$ext : _self.$ext.offsetParent); - newPos = Math.min(max, Math.max(min, (e.clientY - coords[1] - offset))); - } - } - else { - if (e.clientX >= 0) { - coords = apf.getAbsolutePosition(_self.$parent ? _self.$parent.$ext : _self.$ext.offsetParent); - newPos = Math.min(max, Math.max(min, (e.clientX - coords[0] - offset))); - } - } - - if (!newPos) return; - - if (_self.realtime || final) - _self.update(newPos, final); - else { - _self.$ext.style[pNode.$vbox ? "top" : "left"] = newPos + "px"; - } - } - - - apf.plane.show(this); - - - _self.$setStyleClass(this, _self.$baseCSSname + "Moving"); - - _self.$setStyleClass(document.body, - _self.type == "vertical" ? "w-resize" : "n-resize", - [_self.type == "vertical" ? "n-resize" : "w-resize"]); - - //@todo convert to proper way - document.onmouseup = function(e) { - if (!e) e = event; - - _self.$setStyleClass(_self.$ext, "", [_self.$baseCSSname + "Moving"]); - _self.$setStyleClass(document.body, "", ["n-resize", "w-resize"]); - - update(e, true); - - - apf.plane.hide(); - - // Return all pointer events to iframes - var frames = document.getElementsByTagName("iframe"); - for (var i = 0; i < frames.length; i++) - frames[i].style.pointerEvents = ""; - - if (!_self.realtime) { - _self.$ext.style.left = ""; - _self.$ext.style.top = ""; - _self.$ext.style[_self.$info.size] = ""; - _self.$ext.style.position = ""; - } - - _self.dispatchEvent("dragdrop"); - - document.onmouseup = - document.onmousemove = null; - - apf.dragMode = false; //return to default selection policy - }; - - //@todo convert to proper way - document.onmousemove = function(e) { - if (!e) e = event; - - update(e); - - apf.stopEvent(e); - //e.returnValue = false; - //e.cancelBubble = true; - - _self.dispatchEvent("dragmove"); - }; - } - } - } -}; - -apf.aml.setElement("splitter", apf.splitter); - - - -}; -}); \ No newline at end of file diff --git a/plugins/c9.ide.ui/lib_apf.js b/plugins/c9.ide.ui/lib_apf.js index 3566c786..499835e9 100644 --- a/plugins/c9.ide.ui/lib_apf.js +++ b/plugins/c9.ide.ui/lib_apf.js @@ -1,5 +1,5 @@ -define(["require", "module", "exports", "./lib/menu/menu", - "./lib/page", "./lib/dropdown", "./lib/splitbox", "./lib/flexbox"], +define(["require", "module", "exports", "ui/menu/menu", + "ui/page", "ui/dropdown", "ui/splitbox", "ui/flexbox"], function(require, module, exports) { main.consumes = ["ext"]; main.provides = ["apf"] @@ -17034,10 +17034,10 @@ apf.config.$inheritProperties["validgroup"] = 1; -require("./lib/dropdown")(apf); +require("ui/dropdown")(apf); -require("./lib/splitbox")(apf); +require("ui/splitbox")(apf); @@ -37110,9 +37110,9 @@ apf.aml.setProcessingInstruction("livemarkup", apf.LiveMarkupPi); -require("./lib/menu/menu")(apf); -require("./lib/flexbox")(apf); -require("./lib/page")(apf); +require("ui/menu/menu")(apf); +require("ui/flexbox")(apf); +require("ui/page")(apf); diff --git a/plugins/c9.login.client/bootstrap.js b/plugins/c9.login.client/bootstrap.js new file mode 100644 index 00000000..9f62d313 --- /dev/null +++ b/plugins/c9.login.client/bootstrap.js @@ -0,0 +1,283 @@ +(function(global) { +"use strict"; + +var token = ""; + +var auth = global.auth = function(options) { + // can only be called once + global.auth = null; + + var onLoad = options.onLoad; + var preload = options.preload || noop; + var authorized = options.authorized || noop; + var background = options.background || noop; + + importCssString("html.fulliframe, body.fulliframe {\ + overflow: hidden;\ + margin: auto;\ + height: 100%;\ + width: 100%;\ + }"); + + function noop(callback) { callback(); } + + if (onLoad) { + auth.parallel([ + background, + auth.serial([ + auth.parallel([ + preload, + login + ]), + authorized, + ]) + ])(done); + } + + function login(callback, errback) { + var oauth = new Auth(options.clientId, options.authorizationUrl, options.loginHint); + + oauth.authorize(true, function(err, _token) { + if (err) + return iframeLogin(); + + token = _token.access_token; + callback(null, token); + }); + + function iframeLogin() { + errback && errback(); + oauth.authorize(false, function(err, _token) { + if (err) return callback(err); + token = _token.access_token; + callback(null, token); + }); + } + + return function cancel() { + oauth.cancel(); + }; + } + + function done(err) { + onLoad(err, token); + } + + return { + login: login + }; +}; + +function bindScript(script) { + if (typeof script == "function") + return script; + else + return loadScript.bind(null, script, token); +} + +auth.serial = function(list) { + return function(callback) { + serial(list.map(bindScript), callback); + }; +}; + +auth.parallel = function(list) { + return function(callback) { + parallel(list.map(bindScript), callback); + }; +}; + +function loadScript(path, token, callback) { + var head = document.head || document.getElementsByTagName("head")[0] || document.documentElement; + var s = document.createElement('script'); + + var and = path.indexOf("?") >= 0 ? "&" : "?"; + s.src = path + (token ? and + "access_token=" + encodeURIComponent(token) : ""); + head.appendChild(s); + + s.onload = s.onreadystatechange = function(_, isAbort) { + if (isAbort || !s.readyState || s.readyState == "loaded" || s.readyState == "complete") { + s = s.onload = s.onreadystatechange = null; + if (!isAbort) + callback(); + } + }; +} + +// copied from ace/lib/dom +function importCssString(cssText) { + var style; + + if (document.createStyleSheet) { + style = document.createStyleSheet(); + style.cssText = cssText; + } else { + style = document.createElementNS + ? document.createElementNS("http://www.w3.org/1999/xhtml", "style") + : document.createElement("style"); + + style.appendChild(document.createTextNode(cssText)); + + (document.head || document.getElementsByTagName("head")[0] || document.documentElement).appendChild(style); + } +} + +function serial(handlers, callback) { + (function loop(i) { + if (i >= handlers.length) return callback(); + + handlers[i](function(err) { + if (err) return callback(err); + + loop(i+1); + }); + })(0); +} + +function parallel(handlers, callback) { + var hadErr = false; + var count = 0; + handlers.forEach(function(handler) { + handler(function(err) { + if (hadErr) return; + if (err) { + hadErr = true; + return callback(err); + } + count += 1; + if (count == handlers.length) + return callback(); + }); + }); +} + +// install exactly one global listener +var listeners = {}; +window.addEventListener("message", function(e) { + var token = e.data.token; + if (token) { + for (var url in listeners) { + if (url.indexOf(e.origin) === 0) { + var callback = listeners[url][token.state]; + delete listeners[url][token.state]; + if (callback) callback(null, token); + + // make sure later listeners can't steal the token + e.data.token = null; + break; + } + } + } +}, true); + + +function Auth(clientId, authorizationUrl, loginHint) { + this.clientId = clientId; + this.authorizationUrl = authorizationUrl; + this.loginHint = loginHint; + listeners[authorizationUrl] = {}; +} + +Auth.prototype.authorize = function(immediate, callback) { + if (typeof immediate == "function") + return this.authorize({}, immediate); + + immediate = immediate || false; + + var that = this; + this.state = uid(15); + + var url = this.authorizationUrl + + "?response_type=postmessage" + + "&client_id=" + encodeURIComponent(this.clientId) + + "&state=" + encodeURIComponent(this.state) + + "&style=overlay"; + + if (this.loginHint) + url += "&login_hint=" + encodeURIComponent(this.loginHint || ""); + + if (immediate) + url += "&immediate=1"; + + var frame = this._createFrame(url, immediate); + var timeout = immediate ? 3000 : 0; + + if (timeout) { + var timer = setTimeout(function() { + that._unpoll(); + callback(new Error("Login timed out")); + }, timeout); + } + + this._removeFrame = function() { + clearTimeout(timer); + + frame.parentNode.removeChild(frame); + document.documentElement.className = document.documentElement.className.replace(/\bfulliframe\b/, ""); + document.body.className = document.body.className.replace(/\bfulliframe\b/, ""); + that._removeFrame = null; + }; + + this._poll(function(err, token) { + if (that._removeFrame) + that._removeFrame(); + + if (err) + return callback(err); + + if (token.error) { + err = new Error(token.error); + err.code = token.error_code; + return callback(err); + } + + that.token = token; + return callback(null, token); + }); +}; + +Auth.prototype.cancel = function() { + this._unpoll(); + if (this._removeFrame) + this._removeFrame(); +}; + +Auth.prototype._createFrame = function(url, hidden) { + var frame = document.createElement("iframe"); + frame.setAttribute("src", url); + frame.setAttribute("frameborder", "0"); + if (hidden) { + frame.style.width = "1000px"; + frame.style.height = "1000px"; + frame.style.left = "-10000px"; + } + else { + frame.style.width = "100%"; + frame.style.height = "100%"; + frame.style.zIndex = "300000"; + document.documentElement.className += " fulliframe"; + document.body.className += " fulliframe"; + } + frame.style.position = "absolute"; + document.body.appendChild(frame); + return frame; +}; + +Auth.prototype._poll = function(callback) { + listeners[this.authorizationUrl][this.state] = callback; +}; + +Auth.prototype._unpoll = function() { + delete listeners[this.authorizationUrl][this.state]; +}; + +function uid(length) { + var buf = new Uint8Array(new ArrayBuffer(length)); + (window.crypto || window.msCrypto).getRandomValues(buf); + + return btoa(Array.prototype.reduce.call(buf, function(s, c) { + return s + String.fromCharCode(c); + }, "")).slice(0, length); +} + +})(this); \ No newline at end of file diff --git a/plugins/c9.vfs.client/vfs_client.js b/plugins/c9.vfs.client/vfs_client.js index 266974e7..22a5916f 100644 --- a/plugins/c9.vfs.client/vfs_client.js +++ b/plugins/c9.vfs.client/vfs_client.js @@ -30,9 +30,8 @@ define(function(require, exports, module) { var Plugin = imports.Plugin; var auth = imports.auth; var vfsEndpoint = imports["vfs.endpoint"]; - var errorDialog = imports["dialog.error"]; - var showError = errorDialog.show; - var hideError = errorDialog.hide; + var showError = imports["dialog.error"].show; + var hideError = imports["dialog.error"].hide; var showAlert = imports["dialog.alert"].show; var eio = require("engine.io"); @@ -50,9 +49,6 @@ define(function(require, exports, module) { var plugin = new Plugin("Ajax.org", main.consumes); var emit = plugin.getEmitter(); - // Give reference to vfs to plugin - errorDialog.vfs = plugin; - var buffer = []; var installChecked = false; var withInstall = options.withInstall; @@ -334,22 +330,6 @@ define(function(require, exports, module) { }); plugin.on("unload", function(){ loaded = false; - - id = null; - buffer = []; - installChecked = false; - region = null; - vfsBaseUrl = null; - homeUrl = null; - projectUrl = null; - pingUrl = null; - serviceUrl = null; - eioOptions = null; - consumer = null; - vfs = null; - showErrorTimer = null; - showErrorTimerMessage = null; - lastError = null; }); /***** Register and define API *****/ diff --git a/plugins/c9.vfs.server/vfs.js b/plugins/c9.vfs.server/vfs.js index d75a8adc..9562eef9 100644 --- a/plugins/c9.vfs.server/vfs.js +++ b/plugins/c9.vfs.server/vfs.js @@ -19,11 +19,9 @@ function Vfs(vfs, master, options) { this.vfs = vfs; this.master = master; this.debug = options.debug || false; - this.logger = options.logger || {log: function(){}}; this.readonly = options.readonly || false; this.public = options.public || false; this.vfsOptions = options.vfsOptions || {}; - this.pid = this.vfsOptions.pid; var extendToken = options.extendToken; this.homeDir = options.homeDir; @@ -169,12 +167,6 @@ Vfs.prototype._createEngine = function(vfs, options) { that.socket.disconnect(); that.socket = socket; - socket.on('close', function (reason, description) { - var logMetadata = {collab: options.collab, reason: reason, description: description, id: that.id, sid: socket.id, pid: that.pid}; - console.log("Socket closed", logMetadata); - logMetadata.message = "Socket closed"; - that.logger.log(logMetadata); - }); var transport = new smith.EngineIoTransport(socket, true); var worker = new VfsWorker(vfs); @@ -193,10 +185,7 @@ Vfs.prototype._createEngine = function(vfs, options) { } worker.on("disconnect", function() { - var logMetadata = {collab: options.collab, id: that.id, sid: socket.id, pid: that.pid}; - console.log("VFS socket disconnect:", logMetadata); - logMetadata.message = "VFS socket disconnect"; - that.logger.log(logMetadata); + console.log("VFS socket disconnect:", options.collab, that.id, socket.id); if (options.collab) { if (collabApi) return disposeCollabClient(); diff --git a/scripts/install-sdk.sh b/scripts/install-sdk.sh deleted file mode 100755 index 18bf05d4..00000000 --- a/scripts/install-sdk.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/bash -ex - -cd `dirname $0`/.. -SOURCE=`pwd` - -uname="$(uname -a)" -os= -arch="$(uname -m)" -case "$uname" in - Linux\ *) os=linux ;; - Darwin\ *) os=darwin ;; - SunOS\ *) os=sunos ;; - FreeBSD\ *) os=freebsd ;; - CYGWIN*) os=windows ;; - MINGW*) os=windows ;; -esac -case "$uname" in - *x86_64*) arch=x64 ;; - *i*86*) arch=x86 ;; - *armv6l*) arch=arm-pi ;; -esac - -if ! [[ -f ~/.c9/installed ]] && ! [[ $os == "windows" ]]; then - curl https://raw.githubusercontent.com/c9/install/master/install.sh | bash -fi - - -installC9Package() { - name=$1 - - REPO=https://github.com/c9/$name - - echo "checking out $REPO" - - if ! [[ -d ./plugins/$name ]]; then - mkdir -p ./plugins/$name - fi - - pushd ./plugins/$name - if ! [[ -d .git ]]; then - git init - # git remote rm origin || true - git remote add origin $REPO - fi - - version=`node -e 'console.log((require("../../package.json").c9plugins["'$name'"].substr(1) || "origin/master"))'`; - rev=`git rev-parse --revs-only $version` - - if [ "$rev" == "" ]; then - git fetch origin - fi - - status=`git status --porcelain --untracked-files=no` - if [ "$status" == "" ]; then - git reset $version --hard - else - echo "$name contains uncommited changes. Skipping..." - fi - popd -} - -c9packages=(`node -e 'console.log(Object.keys(require("./package.json").c9plugins).join(" "))'`); -count=${#c9packages[@]} -i=0 -for m in ${c9packages[@]}; do echo $m; - i=$(($i + 1)) - echo "updating plugin $i of $count" - installC9Package $m || true -done - - -# deps=`node -e 'console.log(Object.keys(require("./package.json").dependencies).join(" "))'`; -# for m in $deps; do echo $m; -# npm install $m || true -# done -npm install || true - - -echo "Success!" -echo "run 'node server.js -p 8181 -l 0.0.0.0' to launch Cloud9" \ No newline at end of file From 2f7ed5b419e2763d67309441e127c488b60b09ed Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 13 Feb 2015 21:52:45 +0100 Subject: [PATCH 2/2] Fixed server_handler_wrapper stacking callbacks --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 57cec873..9923882d 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "c9.ide.language.javascript.eslint": "#8fbaa9cc96", "c9.ide.language.javascript.tern": "#3d678a103a", "c9.ide.language.javascript.infer": "#1ae097af44", - "c9.ide.language.jsonalyzer": "#45a20496be", + "c9.ide.language.jsonalyzer": "#8d9c59e65d", "c9.ide.collab": "#08536cf0fe", "c9.ide.local": "#d5c324ee5b", "c9.ide.find": "#be3bca94b7",