diff --git a/.sencha/package/build-impl.xml b/.sencha/package/build-impl.xml new file mode 100644 index 0000000..e242abd --- /dev/null +++ b/.sencha/package/build-impl.xml @@ -0,0 +1,358 @@ + + + + + + + + Using Sencha Cmd from ${cmd.dir} for ${ant.file} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.sencha/package/build.properties b/.sencha/package/build.properties new file mode 100644 index 0000000..83e7e5f --- /dev/null +++ b/.sencha/package/build.properties @@ -0,0 +1,8 @@ +# ============================================================================= +# This file provides an override point for default variables defined in +# defaults.properties. +# +# IMPORTANT - Sencha Cmd will merge your changes with its own during upgrades. +# To avoid potential merge conflicts avoid making large, sweeping changes to +# this file. +# ============================================================================= diff --git a/.sencha/package/codegen.json b/.sencha/package/codegen.json new file mode 100644 index 0000000..3f0798e --- /dev/null +++ b/.sencha/package/codegen.json @@ -0,0 +1,118 @@ +{ + "sources": { + "package.json.tpl.merge": { + "12322b2f0769f491000df8ec0e012dd2d78a7eaf": "eJx1zrEKwjAQgOG9TxE6S9HV2VFcFBdxONJrDW1y8XIRSum7m2pTJzMd/3cHGQuVXunAYrlX5ei79pTmqdx8QQa/wiXNK2hGEOLZwJEbLMWQLURrgYfZzg9iUTksXqOA6bE+YNBsvBhy8+6RXKvqX1PUKA+6gxbz5Qs5LNu7altt19+Q9SDXP9oQW5BPzqknDX0qwhGXxPiMhjGkersXU/EGNatRVA\u003d\u003d" + }, + "theme.html.tpl.merge": { + "79ec5194c052d6cc313e842c4e2763fa562c7b70": "eJydVE1vEzEQve+vmO4JEF6nhQNKd3MJRYCQqEg4cHS8k9jFay/2JE1U9b/X3l1KWoKIYln74Tdv3vh55PLs/dfp/Mf1FShqzCQrzxjLAGDq2p3XK0XwQr6Ei9H5BYuPNwXM0Eol4JOVRcZYJAw8haKeJCaUpMng5GpL8HkGc4UNwkfhLYZQ8h7r4xokEWWpZfhrrTdVPnWW0BKb71rMQfZ/VU64JZ5kLiFK+4BUfZ9/YO/ySdYnGmqGL07UIIwBnxJ6rMFo+zOAsDUE6XVLoYtLdXfEBMdgU+WBdgaDQqQcKMoPqjKEHJTHZZXjVjStwaJb4kOCPus+40ZsRL+aQ/CyyouCx3kXsW9orgWpex6/WY2L9aq4Cfmk5H388SkXzlEgL9pn/ANukEKYGS3RRz89HrbhCEmPtkb/f73kfiOsXmKgwXjlPMn1CZq9c3Emvyj1EVuIgDyIEPhwHPy32IlWHqXxuIUTReQ6kGsOmtf1XZ8pjeJW1ysklnpfaBtP7e4RS6MRfqXtGM5H7fbyCRKJpMbwdvQX0rqgSbvIip0uSG/wKe426JfG3Y5ho4NemD34PvtT2ZaFro8YxRqQXv8TgVfPinatkJp2ser9zN32+bD/ku/dHwtX76JR3Ssh3QXzACi8aeE\u003d" + }, + "build.properties.merge": { + "8b81315dbe73ce9c08478f4c1d2df84f456efcf5": "eJytkEtOxEAMRPdzipKyhbkBCzQrFnzE5AKetJNYdOzI3UnE7XGA3GC8K5f9/GnwdM84NWhHKeglM2a3VRIXkMJWdg+B2UQrenMk7mnJFSu50C1HXWREOUEUAfr3yzk4M3sVLudTE8bL68f7Z/v81uIRV9ZuJFymhE1yxsQ+ML5tcUReh6BuUkdILbBNkRYXHbDMg1P6BaI10GqSYrXKWoUOSmfaZ+mi88+f6GvvzRTmA8rGPO/6mFMtYPW4fiff97U/al6C1w\u003d\u003d" + }, + "config.rb.tpl.merge": { + "33f446bd02c3fd24eb27891582eff6a2e789796b": "eJxLLi2KT8ksUrBVcMvMSdUDMvMSc1M14uPdPH1c4+M1ufJLSwpKS+KLSypzUoGqrPJSi0tSU7gALskTcA\u003d\u003d" + }, + "all.scss.merge": { + "da39a3ee5e6b4b0d3255bfef95601890afd80709": "eJwDAAAAAAE\u003d" + }, + "custom.js.merge": { + "199e99bbd15c3c0415569425cb21e77c95e9042a": "eJxlj0FOxDAMRfdziq9ZwUjTHIAlYslquIAncdtA4lSxC8PtSdoREmIV6f/4+dmdDjjhbY6KMSZGeycWrmQcQAqCGlWLMmEpUQzXb1xY/Ex4zgFnRMNXTAlSWseovCTybbbUDl6XsJHa1FH3sYX8B03cqqlS4OPQ//2V8CQ7K5fPriEBNjPU17gYjCZE6UnmYbacfj/GsaUNslUIhbVzu5lwq/2qVjIohGixCCVkkjiyWrOFzqWaXw0sViPr0IRYGVQ7yq+55X2HdObg7meo45udt4XnKyk7Je0Z5SWxqyyB6/Cu/Uh3ODj3crNhN28ar/f1D49P/7rLXUd7+QPuPI9g" + }, + "testing.properties.merge": { + "e65f969c42eb4f355c850fc58fea852582f20db8": "eJyVkUFywyAQBO9+xVb5oIutH/gX+QCCkbUOAooFOf59FsmqpHKKOFEwOzM0Z7r9f53O9DGx0Mge5DBygFDKMSEX1m0VOBpepLqhsndXnpPvv2Z/oefEdiKdLRNoMAJqdyqMI5lAJiXP1hSOQbbZ5msh0mskmuOvnDHHWY32JjbmDEkxOCqxBai6K5DC4d693RAWzjHMCOVCkmB5ZLhW9EWdINjJtBJv9T7cU0vXsk/2rWwxn9AisHA6AooLcgNhqi8riYXdimAn0P+07vXsCOuD8rNimLWaiDKkmBrK7UOUyR0B2RRQdzXedyp+CMVaUi0rQn3ninMxvurPspjBQ/54jjHvYLbHycGKG5Fm2SIf0u/ut9M3l43NIg\u003d\u003d" + }, + "sencha.cfg.tpl.merge": { + "6d1982cce48163a98dc46012d1d0cdfa209fbda6": "eJzFVdFq2zAUfc9XXNzBWmicvg0GgXZlD9vDWsge96LIcqJFljRJTuqN/vuOJNvJmqbbYLASim1dnXPuuVdXZ/R5LUizRpCpKeDZMr5hK0FT8mvTqooaFvg6LRUxriDrjBUudCQ1lbM+vvzqjZ6cTYbXGDr/YTerT3h4nEzORiKPEEHB0G4tE7D0A+lrT4ubxYK4cQ5xRle+TPsqUbNWBdoy1UalgCveP4SCGsG0BwYLSWEtlfBZl2fez7zjdM50NbxvmbvoE+IKH1IwwOLehOeMCXuVJX3QZLDoaGdc5S8Ta2mZFqq8j/+hgFkIMADZc85SxCxFlJ57X2I5WmDIh06Jkbx2piGmuz3lJXkRsimwaKmY3kBFnb/gl75cZsk6hmTAY12daSE6FtC2IduaUj1QiT3PKR1rGAPLUdkc4bmOWUn0jJumYVMvLHMsiIqU9CE2Um1UJZxHiYXbZ4uaykqkinoBpOF702LXUpB4sEpyGVSH0G+tdECMJggo56qt8IrSRt5lK1V1KDVBWRbW81df2qurN++GhUq6x5js3yrHo9kK5yAYPXij1L63YimAFtloJ7ECgSu5RTkYBbaKW4ue/m6AKPpuBztrg0ELSs6U6oAzpgroldAi62EWbbXs8pGEcULz/ogCQkkdPYlBOHdv4cMZEc1m1z0WXT/lP7BqTOtZq8bVp4ZlY/qKpnYqYGvRFzWd0xzsBXN8jRRq42hxu1gAKHuXjul+nHxkW7bgTtowdkLrsa/PGsmhG2CU0UdNCebTxe7b+w8SwDjw/ykDUL+cAQLGDCL5UeuQbKxxITUOTkkkTFmls4GehHjRp3jELsJv/EPADCRpJJwwEv3XT3BDVsWZXsQuXbM8zeMdkqQUZNqAGXQoIp9f0D2rIK0OV0bPlpznRgcmMfDFA2us+sXhc3+R6nV4nxxS9lv8SdYh4EVi3y6n/S4/HscBhu40yoI7SubWKWqH4YmbY1OkeZEv1tDZX4QBEtfkaV0DHXTN/+kfNN1i8uIGus83ukROU7pPteRpRX5P3mISCWV2B8MHNWdbgzwb4VZplhtdoxQBM19iLmqxg3eYJT5un/wEixPeXA\u003d\u003d" + } + }, + "targets": { + ".sencha/package/testing.properties": { + "source": "testing.properties.merge", + "version": "e65f969c42eb4f355c850fc58fea852582f20db8", + "parameters": { + "pkgName": "ext.ux.data.proxy.websocket", + "senchadir": ".sencha", + "touchRelPath": "../${touch.dir}", + "extRelPath": "../../ext", + "pkgType": "code" + } + }, + ".sencha/package/build.properties": { + "source": "build.properties.merge", + "version": "8b81315dbe73ce9c08478f4c1d2df84f456efcf5", + "parameters": { + "pkgName": "ext.ux.data.proxy.websocket", + "senchadir": ".sencha", + "touchRelPath": "../${touch.dir}", + "extRelPath": "../../ext", + "pkgType": "code" + } + }, + "package.json": { + "source": "package.json.tpl.merge", + "version": "12322b2f0769f491000df8ec0e012dd2d78a7eaf", + "parameters": { + "pkgName": "ext.ux.data.proxy.websocket", + "senchadir": ".sencha", + "touchRelPath": "../${touch.dir}", + "extRelPath": "../../ext", + "pkgType": "code" + } + }, + "sass/example/theme.html": { + "source": "theme.html.tpl.merge", + "version": "79ec5194c052d6cc313e842c4e2763fa562c7b70", + "parameters": { + "pkgName": "ext.ux.data.proxy.websocket", + "senchadir": ".sencha", + "touchRelPath": "../${touch.dir}", + "extRelPath": "../../ext", + "pkgType": "code" + } + }, + "sass/example/custom.js": { + "source": "custom.js.merge", + "version": "199e99bbd15c3c0415569425cb21e77c95e9042a", + "parameters": { + "pkgName": "ext.ux.data.proxy.websocket", + "senchadir": ".sencha", + "touchRelPath": "../${touch.dir}", + "extRelPath": "../../ext", + "pkgType": "code" + } + }, + "sass/etc/all.scss": { + "source": "all.scss.merge", + "version": "da39a3ee5e6b4b0d3255bfef95601890afd80709", + "parameters": { + "pkgName": "ext.ux.data.proxy.websocket", + "senchadir": ".sencha", + "touchRelPath": "../${touch.dir}", + "extRelPath": "../../ext", + "pkgType": "code" + } + }, + "sass/config.rb": { + "source": "config.rb.tpl.merge", + "version": "33f446bd02c3fd24eb27891582eff6a2e789796b", + "parameters": { + "pkgName": "ext.ux.data.proxy.websocket", + "senchadir": ".sencha", + "touchRelPath": "../${touch.dir}", + "extRelPath": "../../ext", + "pkgType": "code" + } + }, + ".sencha/package/sencha.cfg": { + "source": "sencha.cfg.tpl.merge", + "version": "6d1982cce48163a98dc46012d1d0cdfa209fbda6", + "parameters": { + "pkgName": "ext.ux.data.proxy.websocket", + "senchadir": ".sencha", + "touchRelPath": "../${touch.dir}", + "extRelPath": "../../ext", + "pkgType": "code" + } + } + } +} \ No newline at end of file diff --git a/.sencha/package/defaults.properties b/.sencha/package/defaults.properties new file mode 100644 index 0000000..dab58b1 --- /dev/null +++ b/.sencha/package/defaults.properties @@ -0,0 +1,155 @@ +# ============================================================================= +# This file defines properties used by build-impl.xml and the associated +# *-impl.xml files (sass-impl.xml, js-impl.xml, etc.), which are the core of +# the applications build process. +# +# IMPORTANT - This file is not modifiable by a package, and will be overwritten +# during each app upgrade. Please use build.properties for defining package +# customizations to these properties. +# ============================================================================= + +# =========================================== +# properties defining various directory +# locations +# =========================================== +build.dir=${package.build.dir} +build.resources.dir=${build.dir}/resources +package.resources.dir=${package.dir}/resources +package.sass.dir=${package.dir}/sass +package.licenses.dir=${package.dir}/licenses + +# =========================================== +# definitions of various file name patterns +# used for output artifacts +# =========================================== +build.name.prefix=${build.dir}/${package.name} +build.name.css.prefix=${build.resources.dir}/${package.name} +build.name.ruby=config.rb + +build.debug.suffix=-debug +build.all.suffix=-all +build.rtl.suffix=-rtl + +build.all.debug.suffix=${build.all.suffix}${build.debug.suffix} +build.all.rtl.suffix=${build.all.suffix}${build.rtl.suffix} +build.all.rtl.debug.suffix=${build.all.suffix}${build.rtl.suffix}${build.debug.suffix} + +# =========================================== +# define the output js file names for dev, +# debug, and compressed (no suffix) +# =========================================== +build.all.js=${build.name.prefix}.js +build.all.debug.js=${build.name.prefix}${build.debug.suffix}.js + +# =========================================== +# output file names for the scss files +# =========================================== +build.all.scss=${build.name.prefix}${build.all.debug.suffix}.scss +build.all.rtl.scss=${build.name.prefix}${build.all.rtl.debug.suffix}.scss + +# =========================================== +# output file names for the css files +# generated from the scss files by running +# a compass compilation +# =========================================== +build.all.css.debug.prefix=${package.name}${build.all.debug.suffix} +build.all.css.debug=${build.resources.dir}/${build.all.css.debug.prefix}.css +build.all.rtl.css.debug.prefix=${package.name}${build.all.rtl.debug.suffix} +build.all.rtl.css.debug=${build.resources.dir}/${build.all.rtl.css.debug.prefix}.css +build.all.css.prefix=${package.name}${build.all.suffix} +build.all.css=${build.resources.dir}/${build.all.css.prefix}.css +build.all.rtl.css.prefix=${package.name}${build.all.rtl.suffix} +build.all.rtl.css=${build.resources.dir}/${build.all.rtl.css.prefix}.css + +build.all.ruby=${build.dir}/${build.name.ruby} + +# =========================================== +# options to pass to the 'sencha fs slice' command +# =========================================== +build.slice.options= + +# =========================================== +# preprocessor options used when generating +# concatenated js output files +# =========================================== +build.compile.js.debug.options=debug:true +build.compile.js.options=debug:false + +# enables / disables removing text references from +# package js build files +build.remove.references=false + +# This property can be modified to change general build options +# such as excluding files from the set. The format expects newlines +# for each argument, for example: +# +# build.operations=\ +# exclude\n \ +# -namespace=Ext\n +# +# NOTE: modifications to build.operations are intended to be +# placed in an override of the "-after-init" target, where it +# can be calculated based on other +# ant properties +# +# build.operations= + +# =========================================== +# compression option used to generate '-all' +# js output file +# =========================================== +build.compile.js.compress=+yui + +# =========================================== +# selector count threshold to use when +# splitting a single css file into multiple +# css files (IE selector limit workaround) +# =========================================== +build.css.selector.limit=4095 + +# controls the ruby command used to execute compass. a full path +# to ruby may be specified rather than allowing the system shell +# to resolve the command +build.ruby.path=ruby + +# controls the working directory of the child compass process +# and the output location for the .sass-cache folder +compass.working.dir=${build.dir} + +# enables / disables console highlighting for compass +compass.compile.boring=false + +# enables / disables forced rebuilds for compass +compass.compile.force=true + +# enables / disables stack traces in compass failure output +compass.compile.trace=true + +# =========================================== +# Options for sub-packages + +# Set to true/1 to enable build.version inheritance by sub-pacakges +build.subpkgs.inherit.version=0 + +# =========================================== +# theme slicing example page settings +# =========================================== +package.example.dir=${package.dir}/sass/example +package.example.base=${build.all.rtl.css.debug.prefix} +package.example.css.rel=resources/${package.example.base}.css +package.example.css=${build.dir}/${package.example.css.rel} +package.example.scss=${build.dir}/${package.example.base}.scss +package.example.theme.html=${package.example.dir}/theme.html + +bootstrap.base.path=${package.example.dir} +bootstrap.example.js=${package.example.dir}/bootstrap.js + + +# =========================================== +# options controlling output packaging +# operations for output '.pkg' file +# =========================================== +pkg.build.dir=${workspace.build.dir}/${package.name} +pkg.file.name=${package.name}.pkg +pkg.includes=**/* +pkg.excludes=package.json diff --git a/.sencha/package/find-cmd-impl.xml b/.sencha/package/find-cmd-impl.xml new file mode 100644 index 0000000..55d6826 --- /dev/null +++ b/.sencha/package/find-cmd-impl.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + source ~/.bash_profile; sencha which -p cmd.dir -o '$cmddir$' + + + + + + \ No newline at end of file diff --git a/.sencha/package/init-impl.xml b/.sencha/package/init-impl.xml new file mode 100644 index 0000000..a561649 --- /dev/null +++ b/.sencha/package/init-impl.xml @@ -0,0 +1,266 @@ + + + + + + + + + + + + + + + + + + + Switch package version to ${build.version} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.sencha/package/js-impl.xml b/.sencha/package/js-impl.xml new file mode 100644 index 0000000..50e6992 --- /dev/null +++ b/.sencha/package/js-impl.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.sencha/package/plugin.xml b/.sencha/package/plugin.xml new file mode 100644 index 0000000..d57eba8 --- /dev/null +++ b/.sencha/package/plugin.xml @@ -0,0 +1,32 @@ + + + + + + diff --git a/.sencha/package/resources-impl.xml b/.sencha/package/resources-impl.xml new file mode 100644 index 0000000..19e2d48 --- /dev/null +++ b/.sencha/package/resources-impl.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + Merging resources from base package ${base.path} + + + + + + + + + + + + + + + + Merging resources from current package ${package.resources.dir} + + + + + \ No newline at end of file diff --git a/.sencha/package/sass-impl.xml b/.sencha/package/sass-impl.xml new file mode 100644 index 0000000..d86e2d6 --- /dev/null +++ b/.sencha/package/sass-impl.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.sencha/package/sencha.cfg b/.sencha/package/sencha.cfg new file mode 100644 index 0000000..b074305 --- /dev/null +++ b/.sencha/package/sencha.cfg @@ -0,0 +1,60 @@ +# The name of the package - should match the "name" property in ./package.json +# +package.name=ext.ux.data.proxy.websocket + +# The namespace to which this package's SASS corresponds. The default value of +# "Ext" means that the files in ./sass/src (and ./sass/var) match classes in +# the Ext" root namespace. In other words, "Ext.panel.Panel" maps to +# ./sass/src/panel/Panel.scss. +# +# To style classes from any namespace, set this to blank. If this is blank, +# then to style "Ext.panel.Panel" you would put SASS in +# ./sass/src/Ext/panel/Panel.scss. +# +package.sass.namespace=Ext + +# This is the comma-separated list of folders where classes reside. These +# classes must be explicitly required to be included in the build. +# +package.classpath=${package.dir}/src + +# This is the comma-separated list of folders of overrides. All files in this +# path will be given a tag of "packageOverrides" which is automatically +# required in generated apps by the presence of this line in app.js: +# +# //@require @packageOverrides +# +package.overrides=${package.dir}/overrides + +# This is the folder where SASS "src" resides. This is searched for SCSS +# files that match the JavaScript classes used by the application. +# +package.sass.srcpath=${package.dir}/sass/src + +# This is the folder where SASS "vars" resides. This is searched for SCSS +# files that match the JavaScript classes used by the application. +# +package.sass.varpath=${package.dir}/sass/var + +# This file is automatically imported into the SASS build before "vars". +# +package.sass.etcpath=${package.dir}/sass/etc/all.scss + +# This is the folder in which to place "sencha packaage build" output. +# +package.build.dir=${package.dir}/build + +# The folder that contains example application(s) for this package. +# +package.examples.dir=${package.dir}/examples + +# The folder that contains sub-packages of this package. Only valid for "framework" +# package type. +# +package.subpkgs.dir=${package.dir}/packages + +#============================================================================== +# Custom Properties - Place customizations below this line to avoid merge +# conflicts with newer versions + +package.cmd.version=4.0.4.84 diff --git a/.sencha/package/slice-impl.xml b/.sencha/package/slice-impl.xml new file mode 100644 index 0000000..8ca45dc --- /dev/null +++ b/.sencha/package/slice-impl.xml @@ -0,0 +1,111 @@ + + + + +/** + * This file is generated by Sencha Cmd and should NOT be edited. It is + * provided to support globbing requires, custom xtypes, and other + * metadata-driven class system features + */ + + + + + + + + + + + +/* + * This file is generated by Sencha Cmd and should NOT be edited. It redirects + * to the most recently built CSS file for the application to allow theme.html + * to load properly for image slicing (required to support non-CSS3 browsers + * such as IE9 and below). + */ +@import '${package.example.css.path}'; + + + + + + Capture theme image to ${build.dir}/theme-capture.png + + + + + + + Slicing theme images to ${build.resources.dir} + + + + + + + + + + \ No newline at end of file diff --git a/.sencha/package/sub-builds.xml b/.sencha/package/sub-builds.xml new file mode 100644 index 0000000..90f648e --- /dev/null +++ b/.sencha/package/sub-builds.xml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + package + upgrade + + + + + + + + + + Building example in @{example-dir} + + + + + + + + + Upgrading example in @{example-dir} + + + app + upgrade + + + + + + + + + + Cleaning example in @{example-dir} + + + + + \ No newline at end of file diff --git a/.sencha/package/testing.properties b/.sencha/package/testing.properties new file mode 100644 index 0000000..60749a3 --- /dev/null +++ b/.sencha/package/testing.properties @@ -0,0 +1,17 @@ +# =========================================== +# This file defines properties used by +# build-impl.xml, which is the base impl +# of an applications build process. The +# properties from this file correspond to the +# 'testing' build environment, specified +# by 'sencha app build testing'. These will +# take precedence over defaults provided by +# build.properties. +# =========================================== + +# =========================================== +# compression option used to generate '-all' +# js output file. this value disables +# compression for testing builds +# =========================================== +build.compile.js.compress= diff --git a/Gruntfile.js b/Gruntfile.js index 6f3fa6e..8eaf14c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -5,7 +5,7 @@ module.exports = function (grunt) { uglify: { dist: { files: { - 'WebSocket.min.js': 'WebSocket.js' + 'WebSocketProxy.min.js': 'WebSocketProxy.js' } } } , @@ -22,7 +22,7 @@ module.exports = function (grunt) { smarttabs: true , loopfunc: true } , - src: ['WebSocket.js'] + src: ['WebSocketProxy.js'] } } }); diff --git a/architect/WebSocket.Definition.js b/architect/WebSocket.Definition.js new file mode 100644 index 0000000..7b9d41a --- /dev/null +++ b/architect/WebSocket.Definition.js @@ -0,0 +1,9 @@ +{ + "className": "Ext.ux.WebSocket", + "classAlias": "widget.websocket", + "toolbox": { + "name": "WebSocket Wrapper", + "category": "Data Proxies", + "groups": ["Networking"] + } +} \ No newline at end of file diff --git a/architect/WebSocketProxy.Definition.js b/architect/WebSocketProxy.Definition.js new file mode 100644 index 0000000..7831b1f --- /dev/null +++ b/architect/WebSocketProxy.Definition.js @@ -0,0 +1,10 @@ +{ + "className": "Ext.ux.data.proxy.WebSocket", + "classAlias": "proxy.websocket", + "inherits": "Ext.data.proxy.Proxy", + "toolbox": { + "name": "WebSocket Proxy", + "category": "Data Proxies", + "groups": ["Data"] + } +} \ No newline at end of file diff --git a/bower.json b/bower.json index b046efd..4c7eef6 100644 --- a/bower.json +++ b/bower.json @@ -7,7 +7,7 @@ ], "description": "An easy-to-use implementation of the ExtJS/Sencha Touch proxy, using HTML5 WebSocket", "main": [ - "WebSocket.js" + "WebSocketProxy.js" ], "keywords": [ "sencha", diff --git a/demo/demo.js b/demo/demo.js index 4b6033a..82e782c 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -2,7 +2,7 @@ Ext.Loader.setConfig({ enabled: true, paths: { 'Ext.ux.WebSocket': '../bower_components/ext.ux.websocket/WebSocket.js', - 'Ext.ux.data.proxy.WebSocket': '../WebSocket.js' + 'Ext.ux.data.proxy.WebSocket': '../WebSocketProxy.js' } }); diff --git a/package.json b/package.json index 948d277..4459fd7 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "Ext.ux.data.proxy.WebSocket", "version": "0.0.0", "description": "An easy-to-use implementation of the ExtJS/Sencha Touch proxy, using HTML5 WebSocket", - "main": "WebSocket.js", + "main": "WebSocketProxy.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, @@ -26,10 +26,36 @@ "homepage": "https://github.com/wilk/Ext.ux.data.proxy.WebSocket", "devDependencies": { "Faker": "~0.7.0", - "ws": "~0.4.31", - "load-grunt-tasks": "~0.2.1", - "grunt-contrib-uglify": "~0.3.0", + "connect-livereload": "^0.4.0", "grunt": "~0.4.2", - "grunt-contrib-jshint": "~0.8.0" + "grunt-bower-verify": "^2.0.0", + "grunt-contrib-jshint": "~0.8.0", + "grunt-contrib-uglify": "~0.3.0", + "load-grunt-tasks": "~0.2.1", + "ws": "~0.4.31" + }, + "architect": { + "compatFrameworks": [ + "ext42" + ], + "classes": [ + { + "definition": "WebSocketProxy.Definition.js", + "className": "Ext.ux.data.proxy.WebSocket", + "js": [ + "WebSocketProxy.js" + ], + "css": [] + }, + { + "definition": "WebSocket.Definition.js", + "className": "Ext.ux.WebSocket", + "js": [ + "WebSocketManager.js", + "WebSocket.js" + ], + "css": [] + } + ] } } diff --git a/src/Readme.md b/src/Readme.md new file mode 100644 index 0000000..cf8f142 --- /dev/null +++ b/src/Readme.md @@ -0,0 +1,4 @@ +# ext.ux.data.proxy.websocket/src + +This folder contains source code that will automatically be added to the classpath when +the package is used. diff --git a/src/WebSocket.js b/src/WebSocket.js new file mode 100644 index 0000000..4f6478e --- /dev/null +++ b/src/WebSocket.js @@ -0,0 +1,552 @@ +/** + * @class Ext.ux.WebSocket + * @author Vincenzo Ferrari + * + * Wrapper for HTML5 WebSocket + * + * This class provide an interface for HTML5 WebSocket. + * + *

Pure text communication

+ * The communication is text-only, without objects or any other kind of data. + * + * var websocket = Ext.create ('Ext.ux.WebSocket', { + * url: 'ws://localhost:8888' , + * listeners: { + * open: function (ws) { + * console.log ('The websocket is ready to use'); + * ws.send ('This is a simple text'); + * } , + * close: function (ws) { + * console.log ('The websocket is closed!'); + * } , + * error: function (ws, error) { + * Ext.Error.raise (error); + * } , + * message: function (ws, message) { + * console.log ('A new message is arrived: ' + message); + * } + * } + * }); + * + *

Pure event-driven communication

+ * The communication is event-driven: an event and a String or Object are sent and the websocket handles different events. + * + * var websocket = Ext.create ('Ext.ux.WebSocket', { + * url: 'ws://localhost:8888' , + * listeners: { + * open: function (ws) { + * console.log ('The websocket is ready to use'); + * ws.send ('init', 'This is a simple text'); + * ws.send ('and continue', { + * 'my': 'data' , + * 'your': 'data' + * }); + * } , + * close: function (ws) { + * console.log ('The websocket is closed!'); + * } + * } + * }); + * + * // A 'stop' event is sent from the server + * // 'data' has 'cmd' and 'msg' fields + * websocket.on ('stop', function (data) { + * console.log ('Command: ' + data.cmd); + * console.log ('Message: ' + data.msg); + * }); + * + *

Mixed event-driven and text communication

+ * The communication is mixed: it can handles text-only and event-driven communication. + * + * var websocket = Ext.create ('Ext.ux.WebSocket', { + * url: 'ws://localhost:8888' , + * listeners: { + * open: function (ws) { + * console.log ('The websocket is ready to use'); + * ws.send ('This is only-text message'); + * ws.send ('init', 'This is a simple text'); + * ws.send ('and continue', { + * 'my': 'data' , + * 'your': 'data' + * }); + * } , + * close: function (ws) { + * console.log ('The websocket is closed!'); + * } , + * message: function (ws, message) { + * console.log ('Text-only message arrived is: ' + message); + * } + * } + * }); + * + * // A 'stop' event is sent from the server + * // 'data' has 'cmd' and 'msg' fields + * websocket.on ('stop', function (data) { + * console.log ('Command: ' + data.cmd); + * console.log ('Message: ' + data.msg); + * }); + */ + +Ext.define ('Ext.ux.WebSocket', { + alias: 'widget.websocket' , + + mixins: { + observable: 'Ext.util.Observable' + } , + + requires: ['Ext.util.TaskManager', 'Ext.util.Memento'] , + + config: { + /** + * @cfg {String} url (required) The URL to connect + */ + url: '' , + + /** + * @cfg {String} protocol The protocol to use in the connection + */ + protocol: null , + + /** + * @cfg {String} communicationType The type of communication. 'both' (default) for event-driven and pure-text communication, 'event' for only event-driven and 'text' for only pure-text. + */ + communicationType: 'both' , + + /** + * @cfg {Boolean} autoReconnect If the connection is closed by the server, it tries to re-connect again. The execution interval time of this operation is specified in autoReconnectInterval + */ + autoReconnect: true , + + /** + * @cfg {Int} autoReconnectInterval Execution time slice of the autoReconnect operation, specified in milliseconds. + */ + autoReconnectInterval: 5000 , + + /** + * @cfg {Boolean} lazyConnection Connect the websocket after the initialization with the open method + */ + lazyConnection: false , + + /** + * @cfg {Boolean} keepUnsentMessages Keep unsent messages and try to send them back after the connection is open again + */ + keepUnsentMessages: false + } , + + /** + * @property {Number} CONNECTING + * @readonly + * The connection is not yet open. + */ + CONNECTING: 0 , + + /** + * @property {Number} OPEN + * @readonly + * The connection is open and ready to communicate. + */ + OPEN: 1 , + + /** + * @property {Number} CLOSING + * @readonly + * The connection is in the process of closing. + */ + CLOSING: 2 , + + /** + * @property {Number} CLOSED + * @readonly + * The connection is closed or couldn't be opened. + */ + CLOSED: 3 , + + /** + * @property {Object} memento + * @private + * Internal memento + */ + memento: {} , + + /** + * @property {Array} memento + * @private + * Internal queue of unsent messages + */ + messageQueue: [] , + + /** + * Creates new WebSocket + * @param {String/Object} config The configuration options may be specified as follows: + * + * // with a configuration set + * var config = { + * url: 'your_url' , + * protocol: 'your_protocol' + * }; + * + * var ws = Ext.create ('Ext.ux.WebSocket', config); + * + * // or with websocket url only + * var ws = Ext.create ('Ext.ux.WebSocket', 'ws://localhost:30000'); + * + * @return {Ext.ux.WebSocket} An instance of Ext.ux.WebSocket or null if an error occurred. + */ + constructor: function (cfg) { + var me = this; + + // Raises an error if no url is given + if (Ext.isEmpty (cfg)) { + Ext.Error.raise ('URL for the websocket is required!'); + return null; + } + + // Allows initialization with string + // e.g.: Ext.create ('Ext.ux.WebSocket', 'ws://localhost:8888'); + if (typeof cfg === 'string') { + cfg = { + url: cfg + }; + } + + me.initConfig (cfg); + me.mixins.observable.constructor.call (me, cfg); + + me.addEvents ( + /** + * @event open + * Fires after the websocket has been connected. + * @param {Ext.ux.WebSocket} this The websocket + */ + 'open' , + + /** + * @event error + * Fires after an error occured + * @param {Ext.ux.WebSocket} this The websocket + * @param {Object} error The error object to display + */ + 'error' , + + /** + * @event close + * Fires after the websocket has been disconnected. + * @param {Ext.ux.WebSocket} this The websocket + */ + 'close' , + + /** + * @event message + * Fires after a message is arrived from the server. + * @param {Ext.ux.WebSocket} this The websocket + * @param {String/Object} message The message arrived + */ + 'message' + ); + + try { + // Initializes internal websocket + if (!me.getLazyConnection ()) me.initWebsocket (); + + me.memento = Ext.create ('Ext.util.Memento'); + me.memento.capture ('autoReconnect', me); + } + catch (err) { + Ext.Error.raise (err); + return null; + } + + return me; + } , + + /** + * @method isReady + * Returns if the websocket connection is up or not + * @return {Boolean} True if the connection is up, False otherwise + */ + isReady: function () { + return this.getStatus () === this.OPEN; + } , + + /** + * @method getStatus + * Returns the current status of the websocket + * @return {Number} The current status of the websocket (0: connecting, 1: open, 2: closed) + */ + getStatus: function () { + return this.ws.readyState; + } , + + /** + * @method close + * Closes the websocket and kills the autoreconnect task, if exists + * @return {Ext.ux.WebSocket} The websocket + */ + close: function () { + var me = this; + + if (me.autoReconnectTask) { + Ext.TaskManager.stop (me.autoReconnectTask); + delete me.autoReconnectTask; + } + // Deactivate autoReconnect until the websocket is open again + me.setAutoReconnect (false); + + me.ws.close (); + + return me; + } , + + /** + * @method open + * Re/Open the websocket + * @return {Ext.ux.WebSocket} The websocket + */ + open: function () { + var me = this; + + // Restore autoReconnect initial value + me.memento.restore ('autoReconnect', false, me); + me.initWebsocket (); + + return me; + } , + + /** + * @method send + * Sends a message. + * This method is bind at run-time level because it changes on the websocket initial configuration. + * It supports three kind of communication: + * + * 1. text-only + * Syntax: ws.send (string); + * Example: ws.send ('hello world!'); + * 2. event-driven + * Syntax: ws.send (event, string/object); + * Example 1: ws.send ('greetings', 'hello world!'); + * Example 2: ws.send ('greetings', {text: 'hello world!'}); + * 3. hybrid (text and event) + * It uses both: see examples above + * @param {String/Object} message Can be a single text message or an association of event/message. + */ + send: function () {} , + + /** + * @method initWebsocket + * Internal websocket initialization + * @private + */ + initWebsocket: function () { + var me = this; + + me.ws = Ext.isEmpty (me.getProtocol ()) ? new WebSocket (me.getUrl ()) : new WebSocket (me.getUrl (), me.getProtocol ()); + + me.ws.onopen = function (evt) { + // Kills the auto reconnect task + // It will be reactivated at the next onclose event + if (me.autoReconnectTask) { + Ext.TaskManager.stop (me.autoReconnectTask); + delete me.autoReconnectTask; + } + + // Flush unset messages + if (me.getKeepUnsentMessages () && me.messageQueue.length > 0) { + while (me.messageQueue.length > 0) { + // Avoid infinite loop into safeSend method + if (me.isReady ()) me.safeSend (me.messageQueue.shift ()); + else break; + } + } + + me.fireEvent ('open', me); + }; + + me.ws.onerror = function (error) { + me.fireEvent ('error', me, error); + }; + + me.ws.onclose = function (evt) { + me.fireEvent ('close', me); + + // Setups the auto reconnect task, just one + if (me.getAutoReconnect () && (typeof me.autoReconnectTask === 'undefined')) { + me.autoReconnectTask = Ext.TaskManager.start ({ + run: function () { + // It reconnects only if it's disconnected + if (me.getStatus () === me.CLOSED) { + me.initWebsocket (); + } + } , + interval: me.getAutoReconnectInterval () + }); + } + }; + + if (me.getCommunicationType () === 'both') { + me.ws.onmessage = Ext.bind (me.receiveBothMessage, this); + me.send = Ext.bind (me.sendBothMessage, this); + } + else if (me.getCommunicationType () === 'event') { + me.ws.onmessage = Ext.bind (me.receiveEventMessage, this); + me.send = Ext.bind (me.sendEventMessage, this); + } + else { + me.ws.onmessage = Ext.bind (me.receiveTextMessage, this); + me.send = Ext.bind (me.sendTextMessage, this); + } + } , + + /** + * @method flush + * It sends every message given to the websocket, checking first if is there any connection + * If there's no connection, it enqueues the message and flushes it later + * @param {String} Data to send + * @return {Ext.ux.WebSocket} The websocket + * @private + */ + safeSend: function (data) { + var me = this; + + if (me.isReady ()) me.ws.send (data); + else if (me.getKeepUnsentMessages ()) me.messageQueue.push (data); + + return me; + } , + + /** + * @method receiveBothMessage + * It catches every event-driven and pure text messages incoming from the server + * @param {Object} message Message incoming from the server + * @private + */ + receiveBothMessage: function (message) { + var me = this; + + try { + /* + message.data : JSON encoded message + msg.event : event to be raise + msg.data : data to be handle + */ + var msg = Ext.JSON.decode (message.data); + me.fireEvent (msg.event, me, msg.data); + me.fireEvent ('message', me, msg); + } + catch (err) { + if (Ext.isString (message.data)) me.fireEvent (message.data, me, message.data); + // Message event is always sent + me.fireEvent ('message', me, message.data); + } + } , + + /** + * @method receiveEventMessage + * It catches every event-driven messages incoming from the server + * @param {Object} message Message incoming from the server + * @private + */ + receiveEventMessage: function (message) { + var me = this; + + try { + var msg = Ext.JSON.decode (message.data); + me.fireEvent (msg.event, me, msg.data); + me.fireEvent ('message', me, msg); + } + catch (err) { + Ext.Error.raise (err); + } + } , + + /** + * @method receiveTextMessage + * It catches every pure text messages incoming from the server + * @param {Object} message Message incoming from the server + * @private + */ + receiveTextMessage: function (message) { + var me = this; + + try { + me.fireEvent (message, me, message); + // Message event is always sent + me.fireEvent ('message', me, message); + } + catch (err) { + Ext.Error.raise (err); + } + } , + + /** + * @method sendBothMessage + * It sends both pure text and event-driven messages to the server + * @param {String/String[]} events Message(s) or event(s) to send to the server + * @param {String/Object} data Message to send to the server, associated to its event + * @return {Ext.ux.WebSocket} The websocket + * @private + */ + sendBothMessage: function (events, data) { + var me = this; + + // Treats it as normal message + if (arguments.length === 1) { + if (Ext.isString (events)) me.safeSend (events); + else Ext.Error.raise ('String expected!'); + } + // Treats it as event-driven message + else if (arguments.length >= 2) { + events = Ext.isString (events) ? [events] : events; + + for (var i=0; i + * @singleton + * + * Manager of Ext.ux.WebSocket + * + * This singleton provide some useful functions to use for many websockets. + * + * var ws1 = Ext.create ('Ext.ux.WebSocket', { + * url: 'ws://localhost:8888' + * }); + * + * Ext.ux.WebSocketManager.register (ws1); + * + * var ws2 = Ext.create ('Ext.ux.WebSocket', { + * url: 'ws://localhost:8900' + * }); + * + * Ext.ux.WebSocketManager.register (ws2); + * + * var ws3 = Ext.create ('Ext.ux.WebSocket', { + * url: 'ws://localhost:8950' + * }); + * + * Ext.ux.WebSocketManager.register (ws3); + * + * Ext.ux.WebSocketManager.listen ('system shutdown', function (ws, data) { + * Ext.Msg.show ({ + * title: 'System Shutdown' , + * msg: data , + * icon: Ext.Msg.WARNING , + * buttons: Ext.Msg.OK + * }); + * }); + * + * Ext.ux.WebSocketManager.broadcast ('system shutdown', 'BROADCAST: the system will shutdown in few minutes.'); + * + * Ext.ux.WebSocketManager.closeAll (); + * + * Ext.ux.WebSocketManager.unregister (ws1); + * Ext.ux.WebSocketManager.unregister (ws2); + * Ext.ux.WebSocketManager.unregister (ws3); + */ +Ext.define ('Ext.ux.WebSocketManager', { + singleton: true , + + /** + * @property {Ext.util.HashMap} wsList + * @private + */ + wsList: Ext.create ('Ext.util.HashMap') , + + /** + * @method register + * Registers one or more Ext.ux.WebSocket + * @param {Ext.ux.WebSocket/Ext.ux.WebSocket[]} websockets WebSockets to register. Could be only one. + */ + register: function (websockets) { + var me = this; + + // Changes websockets into an array in every case + if (Ext.isObject (websockets)) websockets = [websockets]; + + Ext.each (websockets, function (websocket) { + if (!Ext.isEmpty (websocket.url)) me.wsList.add (websocket.url, websocket); + }); + } , + + /** + * @method contains + * Checks if a websocket is already registered or not + * @param {Ext.ux.WebSocket} websocket The WebSocket to find + * @return {Boolean} True if the websocket is already registered, False otherwise + */ + contains: function (websocket) { + return this.wsList.containsKey (websocket.url); + } , + + /** + * @method get + * Retrieves a registered websocket by its url + * @param {String} url The url of the websocket to search + * @return {Ext.ux.WebSocket} The websocket or undefined + */ + get: function (url) { + return this.wsList.get (url); + } , + + /** + * @method each + * Executes a function for each registered websocket + * @param {Function} fn The function to execute + */ + each: function (fn) { + this.wsList.each (function (url, websocket, len) { + fn (websocket); + }); + } , + + /** + * @method unregister + * Unregisters one or more Ext.ux.WebSocket + * @param {Ext.ux.WebSocket/Ext.ux.WebSocket[]} websockets WebSockets to unregister + */ + unregister: function (websockets) { + var me = this; + + if (Ext.isObject (websockets)) websockets = [websockets]; + + Ext.each (websockets, function (websocket) { + if (me.wsList.containsKey (websocket.url)) me.wsList.removeAtKey (websocket.url); + }); + } , + + /** + * @method broadcast + * Sends a message to each websocket + * @param {String} event The event to raise + * @param {String/Object} message The data to send + */ + broadcast: function (event, message) { + this.multicast ([], event, message); + } , + + /** + * @method multicast + * Sends a message to each websocket, except those specified + * @param {Ext.ux.WebSocket/Ext.ux.WebSocket[]} websockets An array of websockets to take off the communication + * @param {String} event The event to raise + * @param {String/Object} data The data to send + */ + multicast: function (websockets, event, data) { + this.getExcept(websockets).each (function (url, websocket, len) { + if (websocket.isReady ()) { + if (Ext.isEmpty (data)) websocket.send (event); + else websocket.send (event, data); + } + }); + } , + + /** + * @method listen + * Adds an handler for events given to each registered websocket + * @param {String/String[]} events Events to listen + * @param {Function} handler The events' handler + */ + listen: function (events, handler) { + if (Ext.isString (events)) events = [events]; + + this.wsList.each (function (url, websocket, len) { + Ext.each (events, function (event) { + websocket.on (event, handler); + }); + }); + } , + + /** + * @method listenExcept + * Adds an handler for events given to each registered websocket, except websockets given + * @param {String/String[]} events Events to listen + * @param {Ext.ux.WebSocket/Ext.ux.WebSocket[]} websockets WebSockets to exclude + * @param {Function} handler The events' handler + */ + listenExcept: function (events, websockets, handler) { + if (Ext.isString (events)) events = [events]; + + this.getExcept(websockets).each (function (url, websocket, len) { + Ext.each (events, function (event) { + websocket.on (event, handler); + }); + }); + } , + + /** + * @method getExcept + * Retrieves registered websockets except the input + * @param {Ext.ux.WebSocket/Ext.ux.WebSocket[]} websockets WebSockets to exclude + * @return {Ext.util.HashMap} Registered websockets except the input + * @private + */ + getExcept: function (websockets) { + if (Ext.isObject (websockets)) websockets = [websockets]; + + var list = this.wsList.clone (); + + // Exclude websockets from the communication + Ext.each (websockets, function (websocket) { + list.removeAtKey (websocket.url); + }); + + return list; + } , + + /** + * @method closeAll + * Closes any registered websocket + */ + closeAll: function () { + var me = this; + + me.wsList.each (function (url, websocket, len) { + websocket.close (); + me.unregister (websocket); + }); + } +}); \ No newline at end of file diff --git a/WebSocket.js b/src/WebSocketProxy.js similarity index 100% rename from WebSocket.js rename to src/WebSocketProxy.js