Explorar o código

HUE-21
pulling HtmlTable parser sort fix
tabs! and a lint fix.
integrating behavior.js
adding overtext filter
Implementing OverText data filter everywhere.
updating widgets and more
adding art input behaviors
implementing ArtInput behavior across all apps
adding FitText filter
adding filter for art-buttons
fixing a typo in the SplitView html tests
fixing hidden state for overtext/artinput elements
adding behavior for sidebyside select

Aaron Newton %!s(int64=15) %!d(string=hai) anos
pai
achega
6f7337b7a1
Modificáronse 37 ficheiros con 441 adicións e 1031 borrados
  1. 4 0
      apps/beeswax/src/beeswax/templates/beeswax_components.mako
  2. 6 3
      apps/beeswax/src/beeswax/templates/choose_file.mako
  3. 12 8
      apps/beeswax/src/beeswax/templates/create_table_manually.mako
  4. 2 1
      apps/beeswax/src/beeswax/templates/describe_table.mako
  5. 6 6
      apps/beeswax/src/beeswax/templates/execute.mako
  6. 2 2
      apps/beeswax/src/beeswax/templates/watch_results.mako
  7. 1 1
      apps/beeswax/static/js/Source/Beeswax/CCS.Beeswax.js
  8. 1 1
      apps/filebrowser/src/filebrowser/static/js/Source/FileBrowser/CCS.FileViewer.js
  9. 1 1
      apps/filebrowser/src/filebrowser/templates/display.mako
  10. 2 2
      apps/filebrowser/src/filebrowser/templates/listdir_components.mako
  11. 25 13
      apps/jframegallery/src/jframegallery/templates/art-button.html
  12. 13 4
      apps/jframegallery/src/jframegallery/templates/art-inputs.html
  13. 1 1
      apps/jframegallery/src/jframegallery/templates/filter.input.html
  14. 3 615
      apps/jframegallery/src/jframegallery/templates/fit.text.(table).html
  15. 1 1
      apps/jframegallery/src/jframegallery/templates/fit.text.html
  16. 32 0
      apps/jframegallery/src/jframegallery/templates/overtext.html
  17. 1 1
      apps/jframegallery/src/jframegallery/templates/split-view-with-header.html
  18. 1 1
      apps/jframegallery/src/jframegallery/templates/splitview.(vertical).html
  19. 1 1
      apps/jframegallery/src/jframegallery/templates/splitview.html
  20. 2 2
      apps/jobbrowser/src/jobbrowser/templates/jobs.mako
  21. 1 1
      apps/jobbrowser/src/jobbrowser/templates/tasks.mako
  22. 2 2
      apps/jobsub/src/jobsub/templates/list.html
  23. 1 2
      apps/useradmin/static/js/Source/UserAdmin/CCS.UserAdmin.js
  24. 3 3
      desktop/core/static/css/shared.css
  25. 110 0
      desktop/core/static/js/Source/BehaviorFilters/Behavior.FitText.js
  26. 29 0
      desktop/core/static/js/Source/BehaviorFilters/Behavior.SideBySideSelect.js
  27. 38 0
      desktop/core/static/js/Source/CCS/CCS.Desktop.Config.js
  28. 7 1
      desktop/core/static/js/Source/CCS/CCS.JBrowser.js
  29. 86 30
      desktop/core/static/js/Source/CCS/CCS.JFrame.js
  30. 6 173
      desktop/core/static/js/Source/JFrameFilters/CCS.JFrame.ArtButtons.js
  31. 27 104
      desktop/core/static/js/Source/JFrameFilters/CCS.JFrame.FitText.js
  32. 4 17
      desktop/core/static/js/Source/JFrameFilters/CCS.JFrame.Input.js
  33. 3 20
      desktop/core/static/js/Source/JFrameFilters/CCS.JFrame.OverText.js
  34. 2 11
      desktop/core/static/js/Source/JFrameFilters/CCS.JFrame.SideBySideSelect.js
  35. 3 1
      desktop/core/static/js/package.yml
  36. 1 1
      ext/thirdparty/js/art-widgets.hash
  37. 1 1
      ext/thirdparty/js/mootools-more.hash

+ 4 - 0
apps/beeswax/src/beeswax/templates/beeswax_components.mako

@@ -20,6 +20,7 @@
 <%def name="field(
   field, 
   render_default=False, 
+  data_filters=None,
   hidden=False, 
   notitle=False, 
   tag='input', 
@@ -52,6 +53,9 @@
   if not render_default:
     attrs.setdefault('type', 'text')
 
+  if data_filters:
+    attrs.data_filters = data_filters
+
   classes = []
   if klass:
     classes.append(klass)

+ 6 - 3
apps/beeswax/src/beeswax/templates/choose_file.mako

@@ -33,7 +33,8 @@ ${wrappers.head('Choose a File')}
               <p>Enter the name of your new table and a file (compressed files are okay) to base this new table definition on.</p>
               <dl class="clearfix">
                 ${comps.field(file_form["name"], attrs=dict(
-                  klass='overtext required bw-validate-name',
+                  klass='required bw-validate-name',
+                  data_filters="OverText",
                   alt='table_name',
                 ),
                 help="Name of the new table.  Table names must be globally unique.  Table names tend to correspond as well to the directory where the data will be stored.",
@@ -42,13 +43,15 @@ ${wrappers.head('Choose a File')}
                 )
               )}
               ${comps.field(file_form["comment"], attrs=dict(
-                  klass='overtext bw-table-comment',
+                  klass='bw-table-comment',
+                  data_filters="OverText",
                   alt='Optional'
                 ),
                 help="Use a table comment to describe your table.  For example, you might mention the data's provenance, and any caveats users of this table should expect.")}
 
                 ${comps.field(file_form["path"], attrs=dict(
-                    klass='overtext required bw-validate-file',
+                    klass='required bw-validate-file',
+                    data_filters="OverText",
                     alt="/user/user_name/data_dir",
                   ),
                   help="The HDFS path to the file that you would like to base this new table definition on.  It can be compressed (gzip) or not.")}

+ 12 - 8
apps/beeswax/src/beeswax/templates/create_table_manually.mako

@@ -37,7 +37,8 @@ ${wrappers.head('Create a Table', toolbar=has_tables, section='new table')}
           <p>Let's start with a name and description for where we'll store your data.</p>
           <dl class="clearfix">
             ${comps.field(table_form["name"], attrs=dict(
-                klass='overtext required bw-validate-name',
+                klass='required bw-validate-name',
+                data_filters="OverText",
                 alt='table_name',
               ),
               help="Name of the new table.  Table names must be globally unique.  Table names tend to correspond as well to the directory where the data will be stored.",
@@ -46,7 +47,8 @@ ${wrappers.head('Create a Table', toolbar=has_tables, section='new table')}
               )
             )}
             ${comps.field(table_form["comment"], attrs=dict(
-                klass='overtext bw-table-comment',
+                klass='bw-table-comment',
+                data_filters="OverText",
                 alt='Optional'
               ),
               help="Use a table comment to describe your table.  For example, you might mention the data's provenance, and any caveats users of this table should expect.")}
@@ -117,7 +119,8 @@ ${wrappers.head('Create a Table', toolbar=has_tables, section='new table')}
                 ${comps.field(table_form["serde_name"],
                   help="Enter the Java Classname of your SerDe. <em>e.g.</em>, org.apache.hadoop.hive.contrib.serde2.RegexSerDe",
                   attrs=dict(
-                    klass='overtext required',
+                    klass='required',
+                    data_filters="OverText",
                     alt='com.acme.hive.SerDe',
                   )
                 )}
@@ -128,7 +131,7 @@ ${wrappers.head('Create a Table', toolbar=has_tables, section='new table')}
                 ${comps.field(table_form["serde_properties"],
                   help=help,
                   attrs=dict(
-                    klass='overtext',
+                    data_filters="OverText",
                     alt=r'"prop" = "value", "prop2" = "value2"'
                   )
                 )}
@@ -158,14 +161,14 @@ ${wrappers.head('Create a Table', toolbar=has_tables, section='new table')}
               ${comps.field(table_form["input_format_class"],
                 help="Java Class to read data",
                 attrs=dict(
-                  klass='overtext',
+                  data_filters="OverText",
                   alt='com.acme.data.MyInputFormat'
                 )
               )}
               ${comps.field(table_form["output_format_class"],
                 help="Java Class used to write data",
                 attrs=dict(
-                  klass='overtext',
+                  data_filters="OverText",
                   alt='com.acme.data.MyOutputFormat'
                 )
               )}
@@ -190,7 +193,8 @@ ${wrappers.head('Create a Table', toolbar=has_tables, section='new table')}
               ${comps.field(table_form["external_location"],
                 help="Enter the path (on HDFS) to your table's data location",
                 attrs=dict(
-                  klass='overtext required',
+                  klass='required',
+                  data_filters="OverText",
                   alt='/user/user_name/data_dir'
                 )
               )}<a class="ccs-choose_file ccs-art_button" data-icon-styles="{'width': 16, 'height': 16, 'top': 3, 'left': 6 }" data-chooseFor="table-external_location">Choose File</a>
@@ -208,7 +212,7 @@ ${wrappers.head('Create a Table', toolbar=has_tables, section='new table')}
             <%def name="render_column(form, is_partition_form=False)">
               <div class="bw-column">
                 <dt class="bw-column_header bw-inactive">
-                  <input name="${form["column_name"].html_name | n}" value="${form["column_name"].data or ''}" class="overtext required bw-column_name" alt="Column Name"/>
+                  <input name="${form["column_name"].html_name | n}" value="${form["column_name"].data or ''}" class="required bw-column_name" alt="Column Name" data-filters="OverText"/>
                   <p class="ccs-help_text" data-help-direction="1">
                     Column name must be single words that start
                     with a letter or a digit.

+ 2 - 1
apps/beeswax/src/beeswax/templates/describe_table.mako

@@ -63,7 +63,8 @@ ${wrappers.head("Beeswax Table Metadata: " + table.tableName, section='tables')}
                         <div class="bw-overwriteLabel">Overwrite existing data ? <input type="checkbox" name="overwrite" class="bw-overwriteCheckbox"/></div>
                         ##Path (on HDFS) of files to load.
                         ${comps.field(load_form["path"], title_klass='bw-pathLabel', attrs=dict(
-                          klass='overtext bw-loadPath',
+                          klass='bw-loadPath',
+                          data_filters="OverText",
                           alt='/user/data'))}
                         <div class="ccs-button_bar">
                           <a class="ccs-choose_file ccs-art_button" 

+ 6 - 6
apps/beeswax/src/beeswax/templates/execute.mako

@@ -55,7 +55,7 @@
       </ul>
     </dt>
     <dd class="bw-query_bottom default" style="height: auto;">
-      <textarea class="overtext bw-query-field ccs-simple-posteditor"
+      <textarea class="bw-query-field ccs-simple-posteditor" data-filters="OverText"
         alt="Example: SELECT * FROM tablename" name="${form.query["query"].html_name | n}">${extract_field_data(form.query["query"]) or ''}</textarea>
         % if len(form.query["query"].errors):
           <div class="validation-advice">
@@ -112,7 +112,7 @@ ${wrappers.head('Hive Query', section='query')}
                 % endif
                 ${comps.field(f['key'], attrs=dict(
                   alt="mapred.reduce.tasks",
-                  klass="overtext"
+                  data_filters="OverText"
                 ))}
                 <div class="bw-query_settings_delete">
                   ${comps.field(f['_deleted'], tag="button", button_text="x", notitle=True, attrs=dict(
@@ -123,7 +123,7 @@ ${wrappers.head('Hive Query', section='query')}
 
                 ${comps.field(f['value'], attrs=dict(
                   alt="1",
-                  klass="overtext"
+                  data_filters="OverText",
                 ))}
                 ${comps.field(f['_exists'], hidden=True)}
               % endfor
@@ -151,7 +151,7 @@ ${wrappers.head('Hive Query', section='query')}
 
                 ${comps.field(f['path'], attrs=dict(
                   alt="/user/foo/udf.jar",
-                  klass="overtext"
+                  data_filters="OverText",
                 ))}
                 <div class="ccs-button_subbar_below clearfix">
                   <a class="ccs-art_button ccs-choose_file" data-chooseFor="${f['path'].html_name | n}" data-icon-styles="{'width':16, 'height':16, 'top':2}">Choose a File</a>
@@ -174,7 +174,7 @@ ${wrappers.head('Hive Query', section='query')}
 
                 ${comps.field(f['name'], attrs=dict(
                   alt="myFunction",
-                  klass="overtext"
+                  data_filters="OverText",
                 ))}
                 <div class="bw-query_settings_delete">
                   ${comps.field(f['_deleted'], tag="button", button_text="x", notitle=True, attrs=dict(
@@ -185,7 +185,7 @@ ${wrappers.head('Hive Query', section='query')}
 
                 ${comps.field(f['class_name'], attrs=dict(
                   alt="com.acme.example",
-                  klass="overtext"
+                  data_filters="OverText",
                 ))}
                 ${comps.field(f['_exists'], hidden=True)}
               % endfor

+ 2 - 2
apps/beeswax/src/beeswax/templates/watch_results.mako

@@ -39,7 +39,7 @@ ${wrappers.head("Beeswax: Query Results", section='query')}
                         <label for="id_save_target_0">In a new table</label>
                       </div>
                       ${comps.field(save_form['target_table'], notitle=True, klass="target", attrs=dict(
-                        klass="overtext",
+                        data_filters="OverText",
                         alt="table_name"
                       ))}
                       <div>
@@ -48,7 +48,7 @@ ${wrappers.head("Beeswax: Query Results", section='query')}
                       </div>
                       <div class="target">
                         ${comps.field(save_form['target_dir'], notitle=True, attrs=dict(
-                        klass="overtext",
+                        data_filters="OverText",
                         alt="/user/dir"
                         ))}
                         <a class="ccs-art_button ccs-choose_file" data-icon-styles="{'width': 16, 'height': 16, 'top': 1, 'left': 4 }" data-chooseFor="target_dir">Choose File</a>

+ 1 - 1
apps/beeswax/static/js/Source/Beeswax/CCS.Beeswax.js

@@ -181,7 +181,7 @@ ART.Sheet.define('splitview.bw-editor', {
 					if (!container.get('html').contains('ccs-select-with-other')) return;
 					container.getElements('.ccs-select-with-other').each(function(el) {
 						//get the 'other' input
-						var other = el.getElement('input').set('alt', 'Enter a custom value').addClass('overtext required');
+						var other = el.getElement('input').set('alt', 'Enter a custom value').addClass('required').addDataFilter('OverText');
 						//create hint text
 						var ot = new OverText(other);
 						//get the select input

+ 1 - 1
apps/filebrowser/src/filebrowser/static/js/Source/FileBrowser/CCS.FileViewer.js

@@ -47,7 +47,7 @@ CCS.FileViewer = new Class({
 		this.addEvents({
 			load: function(){
 				//Set width of input fields to width of biggest value that could be required, using totalBytes span as guide
-				$(this).getElements("input.overtext").setStyle('width', $(this).getElement('.totalBytes').getWidth());
+				$(this).getElements("input[data-filter*=OverText]").setStyle('width', $(this).getElement('.totalBytes').getWidth());
 				//Set events on anchors to edit and cancel changes to byte values
 				$(this).getElement('.fv-editBytes').addEvent('click', function() {
 					$(this).getElement('.fv-navChange').show();

+ 1 - 1
apps/filebrowser/src/filebrowser/templates/display.mako

@@ -72,7 +72,7 @@
         <div class="fv-navChange ccs-hidden">
           <span class="fv-bold">Enter Bytes</span><a class="ccs-inline fv-cancelChangeBytes" title="Cancel Entry"></a><p/>
           <form class="fv-changeBytesForm" action="${url('filebrowser.views.view', path=path_enc)}" method="GET">
-           <input class="overtext" alt="${view['offset'] + 1}" name="begin"/>-<input class="overtext" alt="${view['end']}" name="end"><p/>
+           <input data-filters="OverText" alt="${view['offset'] + 1}" name="begin"/>-<input data-filters="OverText" alt="${view['end']}" name="end"><p/>
             % if view['mode']:
               <input type="hidden" name="mode" value="${view['mode']}"/><br/>
             % endif

+ 2 - 2
apps/filebrowser/src/filebrowser/templates/listdir_components.mako

@@ -60,9 +60,9 @@ from django.template.defaultfilters import urlencode, stringformat, filesizeform
           <td class="fb-name">
             <div class="fb-name-container">
               % if "dir" == file['type']:
-                <a class="fb-item fb-dir jframe_ignore" href="${url('filebrowser.views.'+view, path=path_enc)}?file_filter=${file_filter}">${display_name}</a>
+                <a data-filters="FitText" class="fb-item fb-dir jframe_ignore" href="${url('filebrowser.views.'+view, path=path_enc)}?file_filter=${file_filter}">${display_name}</a>
               % else:
-                <a class="fb-item fb-file jframe_ignore" target="FileViewer" href="${url('filebrowser.views.'+view, path=path_enc)}?file_filter=${file_filter}">${display_name}</a>
+                <a data-filters="FitText" class="fb-item fb-file jframe_ignore" target="FileViewer" href="${url('filebrowser.views.'+view, path=path_enc)}?file_filter=${file_filter}">${display_name}</a>
               % endif
               % if ".." != file['name']:
                 <ul class="fb-item-actions context-menu">

+ 25 - 13
apps/jframegallery/src/jframegallery/templates/art-button.html

@@ -22,27 +22,39 @@ limitations under the License.
 	</head>
 	<body>
 		<div class="jframe_padded">
-			<a class="ccs-art_button" href="/jframegallery/" style="background: url(/static/art/info.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button!</a>
-			<a class="ccs-art_button" href="/jframegallery/" style="background: url(/static/art/error.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button too!</a>
+			<a data-filters="ArtButton" href="/jframegallery/" style="background: url(/static/art/info.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button!</a>
+			<a data-filters="ArtButton" href="/jframegallery/" style="background: url(/static/art/error.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button too!</a>
 			
-			<div class="ccs-button_bar" style="clear:both">
-				<a class="ccs-art_button" href="/jframegallery/" style="background: url(/static/art/info.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button!</a>
-				<a class="ccs-art_button" href="/jframegallery/" style="background: url(/static/art/error.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button too!</a>
-				<a class="ccs-art_button" href="/jframegallery/" style="background: url(/static/art/info.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button!</a>
-				<a class="ccs-art_button" href="/jframegallery/" style="background: url(/static/art/error.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button too!</a>
+			<div data-filters="ArtButtonBar" style="clear:both">
+				<a data-filters="ArtButton" href="/jframegallery/" style="background: url(/static/art/info.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button!</a>
+				<a data-filters="ArtButton" href="/jframegallery/" style="background: url(/static/art/error.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button too!</a>
+				<a data-filters="ArtButton" href="/jframegallery/" style="background: url(/static/art/info.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button!</a>
+				<a data-filters="ArtButton" href="/jframegallery/" style="background: url(/static/art/error.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button too!</a>
 			</div>
-			<div class="ccs-button_subbar_above" style="clear:both">
-				<a class="ccs-art_button" href="/jframegallery/" style="background: url(/static/art/info.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button!</a>
-				<a class="ccs-art_button" href="/jframegallery/" style="background: url(/static/art/error.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button too!</a>
-				<a class="ccs-art_button" href="/jframegallery/" style="background: url(/static/art/info.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button!</a>
-				<a class="ccs-art_button" href="/jframegallery/" style="background: url(/static/art/error.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button too!</a>
+			<div data-filters="ArtButtonBar" data-bar-position="above" style="clear:both">
+				<a data-filters="ArtButton" href="/jframegallery/" style="background: url(/static/art/info.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button!</a>
+				<a data-filters="ArtButton" href="/jframegallery/" style="background: url(/static/art/error.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button too!</a>
+				<a data-filters="ArtButton" href="/jframegallery/" style="background: url(/static/art/info.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button!</a>
+				<a data-filters="ArtButton" href="/jframegallery/" style="background: url(/static/art/error.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button too!</a>
+			</div>
+			<div data-filters="ArtButtonBar" data-bar-position="below" style="clear:both">
+				<a data-filters="ArtButton" href="/jframegallery/" style="background: url(/static/art/info.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button!</a>
+				<a data-filters="ArtButton" href="/jframegallery/" style="background: url(/static/art/error.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button too!</a>
+				<a data-filters="ArtButton" href="/jframegallery/" style="background: url(/static/art/info.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button!</a>
+				<a data-filters="ArtButton" href="/jframegallery/" style="background: url(/static/art/error.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button too!</a>
 			</div>
-			<div class="ccs-button_subbar_below" style="clear:both">
+			
+			<hr style="clear:both"/>
+			<p>
+				The example below uses a deprecated method for creating buttons. You should see console warnings if you enable dbug.
+			</p>
+			<div class="ccs-button_bar" style="clear:both">
 				<a class="ccs-art_button" href="/jframegallery/" style="background: url(/static/art/info.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button!</a>
 				<a class="ccs-art_button" href="/jframegallery/" style="background: url(/static/art/error.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button too!</a>
 				<a class="ccs-art_button" href="/jframegallery/" style="background: url(/static/art/info.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button!</a>
 				<a class="ccs-art_button" href="/jframegallery/" style="background: url(/static/art/error.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px 0px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button too!</a>
 			</div>
+			
 		</div>
 	</body>
 </html>

+ 13 - 4
apps/jframegallery/src/jframegallery/templates/art-inputs.html

@@ -22,10 +22,19 @@ limitations under the License.
 	</head>
 	<body>
 		<div class="jframe_padded">
-			
-			<input type="input" placeholder="search..." class="ccs-search" />
-			<input type="input" placeholder="your name here..." class="ccs-input" />
-			<input type="input" placeholder="your last name here..." class="ccs-input" />
+			<p>
+				The inputs below should look rather slick with blue outlines when you focus them. They should have hint text that disappears when you focus them and reappear when you move your cursor out of them. Tabbing through them should work appropriately. The first input should have round corners and a search icon. This is achieved by adding the ArtInput data-filter. To get the search input, the property data-art-input-type set to "search".
+			</p>
+
+			<input type="input" title="search..." data-filters="ArtInput, OverText" data-art-input-type="search"/>
+			<input type="input" title="your name here..." data-filters="ArtInput, OverText"/>
+
+			<hr/>
+			<p>
+				The examples below should work exactly the same way but they are using the deprecated methods of having 
+			</p>
+			<input type="input" title="search..." data-filters="OverText, ArtInput" data-art-input-type="search" />
+			<input type="input" title="your name here..." class="ccs-input" data-filters="OverText"/>
 			
 		</div>
 	</body>

+ 1 - 1
apps/jframegallery/src/jframegallery/templates/filter.input.html

@@ -22,7 +22,7 @@ limitations under the License.
 	</head>
 	<body>
 	<div class="toolbar" style="position: absolute; top: 57px; left: 70px">
-		<input type="text" class="ccs-search headlamp_name_filter ccs-hidden overtext" 
+		<input type="text" class="headlamp_name_filter ccs-hidden" data-filters="OverText, ArtInput" data-art-input-type="search"
 			title="Filter by Name"
 			data-filter-elements="td.city" data-filter-parents="tr" value="New York"/>
 		</div>

+ 3 - 615
apps/jframegallery/src/jframegallery/templates/fit.text.(table).html

@@ -18,12 +18,12 @@ limitations under the License.
 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
 <html>
 	<head>
-		<title>HtmlTable</title>
+		<title>FitText (table)</title>
 	</head>
 	<body>
-	<h1 class="ccs-hidden">HtmlTable</h1>
+	<h1 class="ccs-hidden">FitText (table)</h1>
 	
-	<table class="ccs-data_table selectable sortable" cellpadding="0" cellspacing="0" data-fit-text="td">
+	<table class="ccs-data_table selectable sortable" data-filters="FitText-Children" cellpadding="0" cellspacing="0" data-fit-text="td">
 		<thead>
 			<tr>
 				<th>
@@ -85,618 +85,6 @@ limitations under the License.
 					-122.433
 				</td>
 			</tr>
-			<tr>
-				<td>
-					24
-				</td>
-				<td>
-					Boston
-				</td>
-				<td>
-					America/New_York
-				</td>
-				<td>
-					42.3583
-				</td>
-				<td>
-					-71.0603
-				</td>
-			</tr>
-			<tr>
-				<td>
-					25
-				</td>
-				<td>
-					Denver
-				</td>
-				<td>
-					America/Denver
-				</td>
-				<td>
-					39.734
-				</td>
-				<td>
-					-105.026
-				</td>
-			</tr>
-			<tr>
-				<td>
-					31
-				</td>
-				<td>
-					Washington, DC
-				</td>
-				<td>
-					America/New_York
-				</td>
-				<td>
-					38.8964
-				</td>
-				<td>
-					-77.0447
-				</td>
-			</tr>
-			<tr>
-				<td>
-					32
-				</td>
-				<td>
-					Chicago
-				</td>
-				<td>
-					America/Chicago
-				</td>
-				<td>
-					41.8858
-				</td>
-				<td>
-					-87.6181
-				</td>
-			</tr>
-			<tr>
-				<td>
-					33
-				</td>
-				<td>
-					Philadelphia
-				</td>
-				<td>
-					America/New_York
-				</td>
-				<td>
-					39.8694
-				</td>
-				<td>
-					-75.2731
-				</td>
-			</tr>
-			<tr>
-				<td>
-					34
-				</td>
-				<td>
-					Los Angeles
-				</td>
-				<td>
-					America/Los_Angeles
-				</td>
-				<td>
-					34.0443
-				</td>
-				<td>
-					-118.251
-				</td>
-			</tr>
-			<tr>
-				<td>
-					37
-				</td>
-				<td>
-					Portland
-				</td>
-				<td>
-					America/Los_Angeles
-				</td>
-				<td>
-					45.527
-				</td>
-				<td>
-					-122.685
-				</td>
-			</tr>
-			<tr>
-				<td>
-					38
-				</td>
-				<td>
-					San Diego
-				</td>
-				<td>
-					America/Los_Angeles
-				</td>
-				<td>
-					32.7153
-				</td>
-				<td>
-					-117.156
-				</td>
-			</tr>
-			<tr>
-				<td>
-					39
-				</td>
-				<td>
-					Miami
-				</td>
-				<td>
-					America/New_York
-				</td>
-				<td>
-					25.7323
-				</td>
-				<td>
-					-80.2436
-				</td>
-			</tr>
-			<tr>
-				<td>
-					41
-				</td>
-				<td>
-					Seattle
-				</td>
-				<td>
-					America/Los_Angeles
-				</td>
-				<td>
-					47.6036
-				</td>
-				<td>
-					-122.326
-				</td>
-			</tr>
-			<tr>
-				<td>
-					42
-				</td>
-				<td>
-					Austin
-				</td>
-				<td>
-					America/Chicago
-				</td>
-				<td>
-					30.2669
-				</td>
-				<td>
-					-97.7428
-				</td>
-			</tr>
-			<tr>
-				<td>
-					43
-				</td>
-				<td>
-					Dallas / Fort Worth
-				</td>
-				<td>
-					America/Chicago
-				</td>
-				<td>
-					32.7887
-				</td>
-				<td>
-					-96.7676
-				</td>
-			</tr>
-			<tr>
-				<td>
-					46
-				</td>
-				<td>
-					Atlanta
-				</td>
-				<td>
-					America/New_York
-				</td>
-				<td>
-					33.7525
-				</td>
-				<td>
-					-84.3888
-				</td>
-			</tr>
-			<tr>
-				<td>
-					47
-				</td>
-				<td>
-					Detroit
-				</td>
-				<td>
-					America/New_York
-				</td>
-				<td>
-					42.3333
-				</td>
-				<td>
-					-83.0484
-				</td>
-			</tr>
-			<tr>
-				<td>
-					48
-				</td>
-				<td>
-					Houston
-				</td>
-				<td>
-					America/Chicago
-				</td>
-				<td>
-					29.7594
-				</td>
-				<td>
-					-95.3594
-				</td>
-			</tr>
-			<tr>
-				<td>
-					49
-				</td>
-				<td>
-					Las Vegas
-				</td>
-				<td>
-					America/Los_Angeles
-				</td>
-				<td>
-					36.1721
-				</td>
-				<td>
-					-115.122
-				</td>
-			</tr>
-			<tr>
-				<td>
-					51
-				</td>
-				<td>
-					Minneapolis / St. Paul
-				</td>
-				<td>
-					America/Chicago
-				</td>
-				<td>
-					44.9609
-				</td>
-				<td>
-					-93.2642
-				</td>
-			</tr>
-			<tr>
-				<td>
-					52
-				</td>
-				<td>
-					New Orleans
-				</td>
-				<td>
-					America/Chicago
-				</td>
-				<td>
-					29.9544
-				</td>
-				<td>
-					-90.075
-				</td>
-			</tr>
-			<tr>
-				<td>
-					53
-				</td>
-				<td>
-					Phoenix
-				</td>
-				<td>
-					America/Phoenix
-				</td>
-				<td>
-					33.4483
-				</td>
-				<td>
-					-112.073
-				</td>
-			</tr>
-			<tr>
-				<td>
-					56
-				</td>
-				<td>
-					Amsterdam
-				</td>
-				<td>
-					Europe/Amsterdam
-				</td>
-				<td>
-					52.3789
-				</td>
-				<td>
-					4.90067
-				</td>
-			</tr>
-			<tr>
-				<td>
-					57
-				</td>
-				<td>
-					Raleigh / Durham
-				</td>
-				<td>
-					America/New_York
-				</td>
-				<td>
-					35.7797
-				</td>
-				<td>
-					-78.6434
-				</td>
-			</tr>
-			<tr>
-				<td>
-					59
-				</td>
-				<td>
-					Vancouver
-				</td>
-				<td>
-					America/Vancouver
-				</td>
-				<td>
-					49.259515
-				</td>
-				<td>
-					-123.103867
-				</td>
-			</tr>
-			<tr>
-				<td>
-					60
-				</td>
-				<td>
-					Pittsburgh
-				</td>
-				<td>
-					America/New_York
-				</td>
-				<td>
-					40.4405
-				</td>
-				<td>
-					-79.9961
-				</td>
-			</tr>
-			<tr>
-				<td>
-					61
-				</td>
-				<td>
-					London
-				</td>
-				<td>
-					Europe/London
-				</td>
-				<td>
-					51.50714
-				</td>
-				<td>
-					-0.126171
-				</td>
-			</tr>
-			<tr>
-				<td>
-					62
-				</td>
-				<td>
-					St. Louis
-				</td>
-				<td>
-					America/Chicago
-				</td>
-				<td>
-					38.627491
-				</td>
-				<td>
-					-90.198417
-				</td>
-			</tr>
-			<tr>
-				<td>
-					63
-				</td>
-				<td>
-					Baltimore
-				</td>
-				<td>
-					America/New_York
-				</td>
-				<td>
-					39.294255
-				</td>
-				<td>
-					-76.614275
-				</td>
-			</tr>
-			<tr>
-				<td>
-					64
-				</td>
-				<td>
-					Cleveland
-				</td>
-				<td>
-					America/New_York
-				</td>
-				<td>
-					41.499819
-				</td>
-				<td>
-					-81.693716
-				</td>
-			</tr>
-			<tr>
-				<td>
-					65
-				</td>
-				<td>
-					Kansas City
-				</td>
-				<td>
-					America/Chicago
-				</td>
-				<td>
-					39.090431
-				</td>
-				<td>
-					-94.583644
-				</td>
-			</tr>
-			<tr>
-				<td>
-					66
-				</td>
-				<td>
-					Indianapolis
-				</td>
-				<td>
-					America/New_York
-				</td>
-				<td>
-					39.767016
-				</td>
-				<td>
-					-86.156255
-				</td>
-			</tr>
-			<tr>
-				<td>
-					67
-				</td>
-				<td>
-					Milwaukee
-				</td>
-				<td>
-					America/Chicago
-				</td>
-				<td>
-					43.038902
-				</td>
-				<td>
-					-87.906474
-				</td>
-			</tr>
-			<tr>
-				<td>
-					68
-				</td>
-				<td>
-					San Antonio
-				</td>
-				<td>
-					America/Chicago
-				</td>
-				<td>
-					29.424122
-				</td>
-				<td>
-					-98.493628
-				</td>
-			</tr>
-			<tr>
-				<td>
-					69
-				</td>
-				<td>
-					Toronto
-				</td>
-				<td>
-					America/New_York
-				</td>
-				<td>
-					43.670233
-				</td>
-				<td>
-					-79.386755
-				</td>
-			</tr>
-			<tr>
-				<td>
-					70
-				</td>
-				<td>
-					Montreal
-				</td>
-				<td>
-					America/New_York
-				</td>
-				<td>
-					45.545447
-				</td>
-				<td>
-					-73.639076
-				</td>
-			</tr>
-			<tr>
-				<td>
-					71
-				</td>
-				<td>
-					Salt Lake City
-				</td>
-				<td>
-					America/Denver
-				</td>
-				<td>
-					40.760779
-				</td>
-				<td>
-					-111.891047
-				</td>
-			</tr>
-			<tr>
-				<td>
-					72
-				</td>
-				<td>
-					Omaha
-				</td>
-				<td>
-					America/Chicago
-				</td>
-				<td>
-					41.254006
-				</td>
-				<td>
-					-95.999258
-				</td>
-			</tr>
-			<tr>
-				<td>
-					73
-				</td>
-				<td>
-					Richmond
-				</td>
-				<td>
-					America/New_York
-				</td>
-				<td>
-					37.542979
-				</td>
-				<td>
-					-77.469092
-				</td>
-			</tr>
 		</tbody>
 	</table>
 	

+ 1 - 1
apps/jframegallery/src/jframegallery/templates/fit.text.html

@@ -23,7 +23,7 @@ limitations under the License.
 	<body>
 		<div class="jframe_padded">
 			<p>the text below will be truncated to fit the window as it is resized because it has the class .ccs-truncate on it.</p>
-			<p class="ccs-truncate">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec elementum, ligula in iaculis posuere, tortor arcu sodales lorem, vitae congue enim nulla sed sapien. Ut at dui odio. Mauris mattis felis a ipsum bibendum ullamcorper eu eget libero. In dictum auctor interdum.</p>
+			<p data-filters="FitText">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec elementum, ligula in iaculis posuere, tortor arcu sodales lorem, vitae congue enim nulla sed sapien. Ut at dui odio. Mauris mattis felis a ipsum bibendum ullamcorper eu eget libero. In dictum auctor interdum.</p>
 		</div>
 	</body>
 </html>

+ 32 - 0
apps/jframegallery/src/jframegallery/templates/overtext.html

@@ -0,0 +1,32 @@
+{% comment %}
+Licensed to Cloudera, Inc. under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  Cloudera, Inc. licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+{% endcomment %}
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<html>
+	<head>
+		<title>Overtext</title>
+	</head>
+	<body>
+		<div class="jframe_padded">
+			<p>The input below should have some placeholder text that disappears when you click inside the input. If you enter a value and click out, it should not return. If you empty the value and click out, it should reappear. To achieve this, the input has "OverText" as a value in its data-filters property.</p>
+			<input name="test" data-filters="OverText" title="example text..."/>
+			<hr/>
+			<p>This is the same thing using the deprecated .overtext css class (there should be a dbug.warning for it).</p>
+			<input name="test" class="overtext" title="example text..."/>
+		</div>
+	</body>
+</html>

+ 1 - 1
apps/jframegallery/src/jframegallery/templates/split-view-with-header.html

@@ -18,7 +18,7 @@ limitations under the License.
 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
 <html>
 	<head>
-		<title>Spitview with Header</title>
+		<title>Splitview with Header</title>
 	</head>
 	<body>
 			<h1 style="line-height:20px; padding: 12px; background: #bbb; border-bottom: 1px solid #000">

+ 1 - 1
apps/jframegallery/src/jframegallery/templates/splitview.(vertical).html

@@ -18,7 +18,7 @@ limitations under the License.
 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
 <html>
 	<head>
-		<title>Spitview</title>
+		<title>Splitview (Vertical)</title>
 	</head>
 	<body>
 			<div class="splitview resizable">

+ 1 - 1
apps/jframegallery/src/jframegallery/templates/splitview.html

@@ -18,7 +18,7 @@ limitations under the License.
 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
 <html>
 	<head>
-		<title>Spitview</title>
+		<title>Splitview</title>
 	</head>
 	<body>
 			<div class="splitview resizable">

+ 2 - 2
apps/jobbrowser/src/jobbrowser/templates/jobs.mako

@@ -43,10 +43,10 @@
                 </select>
               </li>
               <li class="ccs-inline">
-                <input type="text" class="jt_filter overtext" name="user" title="filter on user" value="${user_filter}"/>
+                <input type="text" class="jt_filter" data-filters="OverText" name="user" title="filter on user" value="${user_filter}"/>
               </li>
               <li class="ccs-inline">
-                <input type="text" class="jt_filter overtext" name="text" title="text filter" value="${text_filter}"/>
+                <input type="text" class="jt_filter" data-filters="OverText" name="text" title="text filter" value="${text_filter}"/>
               </li>
             </form>
           </ul>

+ 1 - 1
apps/jobbrowser/src/jobbrowser/templates/tasks.mako

@@ -81,7 +81,7 @@
             </select>
           </li>
           <li class="ccs-inline">
-            <input type="text" name="tasktext" class="jtv_filter overtext" title="text filter"
+            <input type="text" name="tasktext" class="jtv_filter" data-filters="OverText" title="text filter"
               % if tasktext:
                 value="${tasktext}"
               % endif

+ 2 - 2
apps/jobsub/src/jobsub/templates/list.html

@@ -28,8 +28,8 @@ limitations under the License.
 			<div class="jobsub_topsection clearfix">
 				<form class="jobsub_filter submit_on_change" action="{% url jobsub.list %}" method="get">
 					<span class="filter_text">Filter by owner:</span>
-					<input class="overtext" name="owner" alt="filter by owner" value="{{ owner }}"/>
-					<input class="overtext" name="name" alt="filter by name" value="{{ name }}"/>
+					<input data-filters="OverText" name="owner" alt="filter by owner" value="{{ owner }}"/>
+					<input data-filters="OverText" name="name" alt="filter by name" value="{{ name }}"/>
 					<input class="submit" type="submit"/>
 				</form>
 			</div>

+ 1 - 2
apps/useradmin/static/js/Source/UserAdmin/CCS.UserAdmin.js

@@ -50,8 +50,7 @@ CCS.UserAdmin = new Class({
 	options: {
 		className: 'art browser logo_header useradmin',
 		jframeOptions: {
-		    clickRelays: 'a, .relays'
+			clickRelays: 'a, .relays'
 		}
 	}
-
 });

+ 3 - 3
desktop/core/static/css/shared.css

@@ -65,7 +65,7 @@
 	color: #999;
 	margin-top: 3px;
 }
-.ccs-shared label.OverText-ccs-search, .ccs-shared label.OverText-ccs-input {
+.ccs-shared label.OverText-ArtInput {
 	margin-top: 0px;
 }
 /* clear both sides of floats */
@@ -478,11 +478,11 @@ Takes a multi-select box and makes it so you can click to move elements from a s
 	background: url(/static/art/info.png);
 }
 
-.ccs-search, .ccs-input {
+[data-filters*=ArtInput] {
 	display: none;
 }
 
-*[data-size-to-width], *[data-size-to-height] {
+[data-size-to-width], [data-size-to-height] {
 	overflow: auto;
 }
 

+ 110 - 0
desktop/core/static/js/Source/BehaviorFilters/Behavior.FitText.js

@@ -0,0 +1,110 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+/*
+---
+description: Automatically sizes text to fit an element adding an elipse to the text using the FitText plugin for any elements with the FitText data-filter.
+provides: [Behavior.FitText]
+requires: [Widgets/Behavior, FitText/FitText]
+script: Behavior.FitText.js
+
+...
+*/
+
+(function(){
+	//implements the FitText filter on an element; attaches to the jframe for events on resize
+	var fitIt = function(filter, element, events){
+		if (element.get('tag') == 'td' || element.getParent('table')) {
+			fixTable(element.getParent('table'));
+			if (element.get('tag') == 'td') element.setStyles(tdStyles);
+			else element.getParent('td').setStyles(tdStyles);
+		}
+		
+		if (element.getChildren().length > 0) {
+			dbug.warn('attempting to truncate an element (%o) that has child elements; this is not permitted.', element);
+			return;
+		}
+		var text = element.get('text');
+		var span = new Element('span', {
+			text: text,
+			styles: {
+				'white-space': 'nowrap'
+			}
+		}).inject(element.empty());
+
+		//because FitText requires element measurement, don't create an instance
+		//until after the element is visible.
+		var fitter = function(){
+			if (!element.isVisible()) {
+				//not ready; call again when the thread is released
+				fitter.delay(1);
+			} else {
+				var options = {};
+				var offset = element.get('data', 'fit-text-offset', true);
+				if (offset != null) options.offset = offset;
+				var fitText = new FitText(element, span, options);
+				fitText.fit();
+				element.store('FitText', fitText).set('title', text);
+				var fitTextFit = fitText.fit.bind(fitText);
+				//rerun this after a while, as some filters muck about w/ the DOM
+				//I'm not crazy about this solution, but it'll have to do for now
+				fitTextFit.delay(10); 
+				events.addEvent('show', fitTextFit);
+				filter.markForCleanup(element, function(){
+					events.removeEvent('show', fitTextFit);
+				});
+			}
+		};
+		fitter();
+	};
+
+	var tdStyles = {
+		'max-width': 200, //this number doesn't seem to matter, really
+		'overflow': 'hidden'
+	};
+
+	var fixTable = function(table) {
+		if (!table || table.retrieve('fittext:fixed')) return;
+		table.store('fittext:fixed', true);
+		table.getElements('tbody td').setStyles(tdStyles);
+	};
+	
+
+	Behavior.addGlobalFilters({
+
+		/*
+			truncates text automatically for elements with the class .ccs-truncate
+			elements cannot have child elements (only text)
+		*/
+
+		FitText: function(element, events) {
+			fitIt(this, element, events);
+		},
+
+		/*
+			finds all elements wth data-fit-text properties - these properties must be selectors
+			for the elements to apply the FitText class to.
+		*/
+		'FitText-Children': function(element, events){
+			var selector = element.get('data', 'fit-text');
+			element.getElements(selector).each(function(el){
+				fitIt(this, el, events);
+			}, this);
+		}
+
+	});
+
+
+})();

+ 29 - 0
desktop/core/static/js/Source/BehaviorFilters/Behavior.SideBySideSelect.js

@@ -0,0 +1,29 @@
+/*
+---
+description: Automatically turns multi select inputs into an instance of ART.SideBySideSelect.
+provides: [Behavior.SideBySideSelect]
+requires: [Widgets/Behavior, /ART.SideBySideSelect]
+script: Behavior.SideBySideSelect.js
+...
+*/
+
+Behavior.addGlobalFilters({
+
+	SideBySideSelect: function(element, events) {
+		if (element.get('tag') != 'select' && element.get('multiple')){
+			dbug.warn("Side_by_side_select element %o does not contain multiple=true.", element);
+			return;
+		}
+		var parent = element.get('parentWidget');
+		var sbs = new ART.SideBySideSelect(element);
+
+		//inject our new widget into the DOM and the widget tree (if there is a parent widget)
+		if (parent) sbs.inject(parent, element, 'after');
+		else widget.inject(element, element, 'after');
+
+		this.markForCleanup(element, function(){
+			sbs.destroy();
+		});
+	}
+
+});

+ 38 - 0
desktop/core/static/js/Source/CCS/CCS.Desktop.Config.js

@@ -66,6 +66,44 @@ UI.Sheet.define('window.art button.art.wincontrol', {
 	'font-color': 'black'
 });
 
+(function(){
+	var button = {
+		'height': 19,
+		'width': 22,
+		'padding': [0, 0, 0, 0],
+		'float': 'left',
+		'marginLeft': -1,
+		'corner-radius-top-right': 4,
+		'corner-radius-bottom-right': 4,
+		'corner-radius-top-left': 0,
+		'corner-radius-bottom-left': 0,
+		'glyph': ART.Glyphs.refresh,
+		'glyph-stroke': 0,
+		'glyph-fill': true,
+		'glyph-height': 12,
+		'glyph-width': 12,
+		'glyph-top': 4,
+		'glyph-left': 5
+	};
+	var large = {
+		'height': 24,
+		'width': 24,
+		'glyph-top': 6,
+		'glyph-left': 6
+	};
+	ART.Sheet.define('button.art.ccs-refresh', button);
+	ART.Sheet.define('button.art.ccs-refresh.large', large);
+	button.glyph = ART.Glyphs.triangleLeft;
+	button['glyph-top'] = 5;
+	button['glyph-left'] = 6;
+	ART.Sheet.define('button.art.ccs-back', button);
+	ART.Sheet.define('button.art.ccs-back.large', large);
+	button.glyph = ART.Glyphs.triangleRight;
+	button['glyph-left'] = 8;
+	ART.Sheet.define('button.art.ccs-next', button);
+	ART.Sheet.define('button.art.ccs-next.large', large);
+})();
+
 if (Browser.Engine.trident) {
 	UI.Sheet.define('window.art:dragging', {
 		'background-color': hsb(202, 20, 38, 1)

+ 7 - 1
desktop/core/static/js/Source/CCS/CCS.JBrowser.js

@@ -178,7 +178,13 @@ script: CCS.JBrowser.js
 				}.bind(this),
 				destroy: function(){
 					this.jframe.destroy();
-				}.bind(this)
+				}.bind(this),
+				unshade: function(){
+					this.jframe.behavior.show();
+				},
+				shade: function(){
+					this.jframe.behavior.hide();
+				}
 			});
 			this.jframe.addEvent('refresh', this._storeScroll.bind(this));
 			

+ 86 - 30
desktop/core/static/js/Source/CCS/CCS.JFrame.js

@@ -30,6 +30,12 @@ requires:
  - More/HtmlTable.Sort
  - More/HtmlTable.Select
  - More/Spinner
+ - Widgets/Behavior
+ - Widgets/Behavior.OverText
+ - Widgets/Behavior.ArtInput
+ - Widgets/Behavior.ArtButton
+ - /Behavior.SideBySideSelect
+ - /Behavior.FitText
  - /CCS
  - /CCS.ContextMenu
 script: CCS.JFrame.js
@@ -70,7 +76,10 @@ CCS.JFrame = new Class({
 		useSpinner: true,
 		//linkers: a key/value set of linkers (see the addLinker method for docs)
 		linkers: {},
+		//filters: a key/value set of JFrame filters
 		filters: {},
+		//behaviors: a key/value set of behavior filters passed along to Behavior.addFilters
+		behaviors: {},
 		//the selector to match clicks against for delegation; defaults to only links
 		clickRelays: 'a',
 		//given the response and response text, this method determines if there's been a serverside error
@@ -95,6 +104,11 @@ CCS.JFrame = new Class({
 		new ART.Keyboard(this, this.keyboardOptions);
 		this.addLinkers(this.options.linkers);
 		this.addFilters(this.options.filters);
+
+		this.behavior = new Behavior(this.element);
+		this.addEvent('resize', this.behavior.resize.bind(this.behavior));
+		this.addBehaviors(this.options.behaviors);
+
 		this.element.addClass('jframe_wrapper').addClass('ccs-shared');
 		this.scroller = new Fx.Scroll(this.options.getScroller.call(this));
 		this.content = new Element('div', {
@@ -292,6 +306,26 @@ CCS.JFrame = new Class({
 
 	},
 
+	/*
+		fill: fills a given target with the appropriate content
+		target - (*element*) the target to fill with content
+		content - (*object*) an object with the following properties:
+			js - (*string*) any inline javascript to evalutate (stripped from <script> tags)
+			links - (*elements array*) css links to be injected into the target
+			elements - (*elements array*) elements to inject into the target (i.e. the actual content)
+			title - (*string*) the title of the content
+			view - (*string*; optional) if defined, the view of the content
+			viewElement - (*element*; optional) if defined, the element for the view
+	*/
+
+	fill: function(target, content){
+		target.empty().adopt(content.elements);
+		if (content.links && content.links.length && this.options.includeLinkTags) target.adopt(content.links);
+		if (this.options.evaluateJs && content.js) $exec(content.js);
+		this.applyDelegates(target);
+		this.applyFilters(target, content);
+	},
+
 	resize: function(x, y){
 		this.element.setStyles({
 			width: 'auto',
@@ -322,12 +356,54 @@ CCS.JFrame = new Class({
 		obj - (*object*) a key/value set of filters to add
 	*/
 	addFilters: function(obj){
-		$each(obj, function(fn, name){
-			this.addFilter(name, fn);
-		}, this);
+		for (var name in obj) {
+			this.addFilter(name, obj[name]);
+		}
 		return this;
 	},
 
+	/*
+		add a new behavior filter
+		name - (*string*) the name fo the behavior (no spaces or commas; preferably CamelCase)
+		fn - (*function*) the function for the behavior filter.
+		overwrite - (*boolean*) if true, will overwrite any pre-existing filter if one is present
+	*/
+	addBehavior: function(name, fn, overwrite){
+		this.behavior.addFilter(name, fn, overwrite);
+	},
+
+	/*
+		add a group of behavior filters
+		obj - (*object*) an object of key/value pairs of name/functions for filters (see addBehavior)
+		overwrite - (*boolean*) if true, will overwrite any pre-existing filter if one is present
+	*/
+	addBehaviors: function(obj, overwrite){
+		this.behavior.addFilters(obj, overwrite);
+	},
+
+	/*
+		apply a specific behavior to an element
+		name - (*string*) the name fo the behavior (no spaces or commas; preferably CamelCase)
+		element - (*element*) the DOM element to apply the behavior to
+		force - (*boolean*) forces the behavior to reapply, even if it has already been applied; defaults to *false*.
+	*/
+	applyBehavior: function(name, element, force){
+		var behavior = this.behavior.lookup(name);
+		this.behavior.applyBehavior(element, behavior, force);
+	},
+
+	//Applies all the behavior filters for an element.
+	//element - (element) an element to apply the filters registered with this Behavior instance to.
+	//force - (boolean; optional) passed through to applyBehavior (see it for docs)
+	applyBehaviors: function(element, force){
+		this.behavior.apply(element, force);
+	},
+
+	//garbage collects all applied filters for the specified element
+	collectElement: function(element){
+		this.behavior.cleanup(element);
+	},
+
 	/*
 		applyFilters:
 		container - (*element*) applies all the filters on this instance of jFrame to the contents of the container.
@@ -336,9 +412,10 @@ CCS.JFrame = new Class({
 	*/
 
 	applyFilters: function(container, content){
-		for (name in this.filters) {
+		for (var name in this.filters) {
 			this.applyFilter(name, container, content);
 		}
+		this.applyBehaviors(container);
 	},
 
 
@@ -347,7 +424,6 @@ CCS.JFrame = new Class({
 		name - (*string*) the name of the JFrame filter to apply
 		container - (*element*) applies all the filters on this instance of jFrame to the contents of the container.
 		content - (*object*) optional object containing various metadata about the content; js tags, meta tags, title, view, etc. See the "notes" section of the renderContent method comments in this file.
-		
 	*/
 	applyFilter: function(name, container, content){
 		dbug.conditional(this.filters[name].bind(this, [container, content]), function(e) {
@@ -442,7 +518,7 @@ CCS.JFrame = new Class({
 	*/
 
 	destroy: function(){
-		this._sweep();
+		this._sweep(this.element);
 	},
 
 
@@ -496,28 +572,6 @@ CCS.JFrame = new Class({
 		}
 	},
 
-	/*
-		fill: fills a given target with the appropriate content
-		target - (*element*) the target to fill with content
-		content - (*object*) an object with the following properties:
-			
-			js - (*string*) any the inline javascript to evalutate,
-			links - (*elements array*) css links to be injected into the target
-			elements - (*elements array*) elements to inject into the target (i.e. the actual content)
-			title - (*string*) the title of the content
-			view - (*string*; optional) if defined, the view of the content
-			viewElement - (*element*; optional) if defined, the element for the view
-		
-	*/
-
-	fill: function(target, content){
-		target.empty().adopt(content.elements);
-		if (content.links && content.links.length && this.options.includeLinkTags) target.adopt(content.links);
-		if (this.options.evaluateJs && content.js) $exec(content.js);
-		this.applyDelegates(target);
-		this.applyFilters(target, content);
-	},
-
 	/*
 		options:
 			see renderContent
@@ -642,14 +696,16 @@ CCS.JFrame = new Class({
 
 	/*
 		sweeps all marked functions.
+		target - (*element*) the element to garbage collect;
 	*/
-	_sweep: function(){
+	_sweep: function(target){
 		this.marked.each(function(fn) {
 			dbug.conditional(fn.bind(this), function(e) {
 				dbug.log('sweeper failed, error: ', e);
 			});
 		});
 		this.marked.empty();
+		this.behavior.cleanup(target);
 		//if there are any child widgets that were not destroyed, destroy them
 		if (this._childWidgets.length) this._childWidgets.each(function(w) { w.eject(); });
 	},
@@ -718,7 +774,7 @@ CCS.JFrame = new Class({
 		//if we're injecting into the main content body, cleanup and scrollto the top
 		if (target == this.content) {
 			this.scroller.toTop();
-			this._sweep();
+			this._sweep(target);
 		}
 
 		//if we're injecting into the main content body apply the view classes and remove the old one

+ 6 - 173
desktop/core/static/js/Source/JFrameFilters/CCS.JFrame.ArtButtons.js

@@ -23,191 +23,24 @@ script: CCS.JFrame.ArtButtons.js
 
 ...
 */
-(function(){
-	var button = {
-		'height': 19,
-		'width': 22,
-		'padding': [0, 0, 0, 0],
-		'float': 'left',
-		'marginLeft': -1,
-		'corner-radius-top-right': 4,
-		'corner-radius-bottom-right': 4,
-		'corner-radius-top-left': 0,
-		'corner-radius-bottom-left': 0,
-		'glyph': ART.Glyphs.refresh,
-		'glyph-stroke': 0,
-		'glyph-fill': true,
-		'glyph-height': 12,
-		'glyph-width': 12,
-		'glyph-top': 4,
-		'glyph-left': 5
-	};
-	var large = {
-		'height': 24,
-		'width': 24,
-		'glyph-top': 6,
-		'glyph-left': 6
-	};
-	ART.Sheet.define('button.art.ccs-refresh', button);
-	ART.Sheet.define('button.art.ccs-refresh.large', large);
-	button.glyph = ART.Glyphs.triangleLeft;
-	button['glyph-top'] = 5;
-	button['glyph-left'] = 6;
-	ART.Sheet.define('button.art.ccs-back', button);
-	ART.Sheet.define('button.art.ccs-back.large', large);
-	button.glyph = ART.Glyphs.triangleRight;
-	button['glyph-left'] = 8;
-	ART.Sheet.define('button.art.ccs-next', button);
-	ART.Sheet.define('button.art.ccs-next.large', large);
-})();
-ART.Sheet.define('button.art.selected', {
-	'font-color': hsb(50, 100, 10),
-	'glyph-color': hsb(0, 0, 0, 0.8),
-	'background-color': [hsb(210, 30, 100), hsb(210, 40, 80)],
-	'reflection-color': [hsb(0, 0, 100, 1), hsb(0, 0, 0, 0)],
-	'border-color': hsb(0, 0, 0, 0.8)
-});
-
-ART.Button.Icon = new Class({
-
-	Extends: ART.Button,
-
-	options: {
-		//icon - the properties passed to the element constructor for the icon div
-		icon: null
-	},
-
-	initialize: function(options){
-		this.parent(options);
-		if (this.options.icon) {
-			this.iconDiv = new Element('div', this.options.icon);
-			this.iconDiv.inject($(this));
-		}
-	}
-
-});
-
 
 CCS.JFrame.addGlobalFilters({
 
-	/*
-		auto-magic art buttons from links and buttons. 
-		By default, the button is just rendered as a button with text in it, but it is possible to have an icon image.
-		
-		To have an icon, style the element with a background image as normal and specify the padding to accomodate that image as normal.
-		You must also specify at the very least a width and height in the data-icon-styles property, but you can, if you like, also specify
-		other styles for the image, including top and left offsets (these default to 4px). Here's a simple example (it's not required that you
-		style it inline).
-
-			<a class="ccs-art_button" href="/jframegallery/" style="background: url(/static/art/info.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px;" data-icon-styles="{'width': 14, 'height': 14}">I'm a button too!</a>
-		
-		When used with buttons, the button is replaced with a standard art-button (button's with inner text just render as buttons with 
-		that text as the visible value, so we must replace it with a regular DOM element). The button input is hidden. When the art button
-		is clicked, the click event is fired on the button so it still behaves the same way.
-	*/
 	artButtons: function(container) {
 		if (!container.get('html').contains('ccs-art_button')) return;
 		var buttonSelector = 'a.ccs-art_button, button.ccs-art_button, input.ccs-art_button';
 		container.getElements('.ccs-button_bar, .ccs-button_subbar_above, .ccs-button_subbar_below').each(function(bar) {
 			var above = bar.hasClass('ccs-button_subbar_above');
 			var below = bar.hasClass('ccs-button_subbar_below');
-			var buttons = bar.getElements(buttonSelector);
-			buttons.each(function(button, i) {
-				var first = (i == 0);
-				var last = (i == buttons.length - 1);
-				var margin = !first ? -1 : 0;
-				button.store('ccs-art_button:styles', {
-					borderRadius: [
-						first && !below ? 4 : 0, //top-left
-						last && !below ? 4 : 0, //top-right
-						last && !above ? 4 : 0, //bottom-right
-						first && !above ? 4 : 0 //bottom-left
-					],
-					'buttonElementMargin': margin
-				});
-			});
+			dbug.warn('you are using a deprecated JFrame filter (ccs-button_bar) on %o, use the ArtButtonBar data-filter instead.', bar);
+			bar.addDataFilter('ArtButtonBar');
+			if (above) bar.set('data', 'bar-position', 'above');
+			if (below) bar.set('data', 'bar-position', 'below');
 		}, this);
 
-
 		container.getElements(buttonSelector).each(function(button){
-			var pos = button.getStyle('position');
-			if (pos != 'absolute' && button.getStyle('display') == 'inline') button.addClass('ccs-inline');
-			var text, element;
-			var isAnchor = button.get('tag') == 'a';
-			if (isAnchor) {
-				text = button.get('html').stripTags();
-				button.empty();
-				element = button;
-			} else {
-				text = button.get('html').stripTags() || button.get('value') || button.get('name');
-			}
-			var buttonOptions = {
-				element: element,
-				text: text,
-				styles: {},
-				className: 'art ' + button.get('class')
-			};
-			var height = button.getStyle('height').toInt();
-			if (height) buttonOptions.height = height;
-			if (button.getStyle("background-image")) {
-				buttonOptions.icon = {
-					styles: $merge(
-						{
-							position: 'absolute', 
-							top: 4,
-							left: 4,
-							backgroundRepeat: 'no-repeat'
-						},
-						button.getStyles('background-image', 'background-position'),
-						button.get('data', 'icon-styles', true)
-					)
-				};
-			}
-			//converts valid css expressions for padding, margin, etc into arrays of integers
-			var fixStyleString = function(str) {
-				var style = str.split(' ');
-				style[0] = style[0].toInt();
-				for (var i = 1; i < 4; i++) {
-					style[i] = (style[i] || style[(i-2).max(0)]).toInt();
-				}
-				return style;
-			};
-
-			var isInButtonBar;
-			if (button.retrieve('ccs-art_button:styles')) {
-				isInButtonBar = true;
-				$extend(buttonOptions.styles, button.retrieve('ccs-art_button:styles'));
-			}
-			if (button.getStyle('padding') != "0px 0px 0px 0px") buttonOptions.styles.padding = button.getStyle('padding');
-			if ($type(buttonOptions.styles.padding) == "string") {
-				buttonOptions.styles.padding = fixStyleString(buttonOptions.styles.padding);
-			}
-			if (isAnchor) button.set('class', '');
-			var b = new ART.Button.Icon(buttonOptions);
-			this.markForCleanup(function(){
-				b.eject();
-			});
-			if (button.retrieve('ccs-art_button:styles')) {
-				$(b).setStyle('margin-left', button.retrieve('ccs-art_button:styles').buttonElementMargin);
-			}
-			
-
-			$(b).setStyles({
-				background: 'none',
-				padding: 0,
-				position: (pos == 'static') ? 'relative' : pos
-			});
-			if (isInButtonBar) $(b).setStyle('float', 'left');
-			if (!isAnchor) {
-				b.inject(this, button, 'after');
-				b.addEvent('press', function(){
-					button.click();
-				});
-				button.hide();
-			} else {
-				b.register(this);
-			}
-			b.draw();
+			dbug.warn('you are using a deprecated JFrame filter (ccs-art_button) on %o, use the ArtButton data-filter instead.', button);
+			button.addDataFilter('ArtButton');
 		}, this);
 	}
 

+ 27 - 104
desktop/core/static/js/Source/JFrameFilters/CCS.JFrame.FitText.js

@@ -23,109 +23,32 @@ script: CCS.JFrame.FitText.js
 ...
 */
 
-(function(){
-	//implements the FitText filter on an element; attaches to the jframe for events on resize
-	var fitIt = function(element, jframe){
-		if (element.getChildren().length > 0) {
-			dbug.warn('attempting to truncate an element (%o) that has child elements; this is not permitted.', element);
-			return;
-		}
-		var text = element.get('text');
-		var span = new Element('span', {
-			text: text,
-			styles: {
-				'white-space': 'nowrap'
+CCS.JFrame.addGlobalFilters({
+
+	/*
+		truncates text automatically for elements with the class .ccs-truncate
+		elements cannot have child elements (only text)
+	*/
+	truncate: function(container) {
+		if (!container.get('html').contains('ccs-truncate')) return;
+		container.getElements('.ccs-truncate').each(function(el){
+			dbug.warn('you are using a deprecated JFrame filter (truncate) on %o, use the FitText data-filter instead.', el);
+			el.addDataFilter('FitText');
+		}, this);
+	},
+
+	/*
+		finds all elements wth data-fit-text properties - these properties must be selectors
+		for the elements to apply the FitText class to.
+	*/
+	truncateChildren: function(container){
+		if (!container.get('html').contains('data-fit-text')) return;
+		container.getElements('[data-fit-text]').each(function(fitParent){
+			if (!fitParent.hasDataFilter('FitText-Children')) {
+				dbug.warn('you are using a deprecated JFrame filter (data-fit-text) on %o without the data-filter "FitText-Children".', fitParent);
+				fitParent.addDataFilter('FitText-Children');
 			}
-		}).inject(element.empty());
-		var win = jframe.getWindow();
-		//because FitText requires element measurement, don't create an instance
-		//until after the element is visible.
-		var fitter = function(){
-			if (!element.isVisible()) {
-				//not ready; call again when the thread is released
-				fitter.delay(1);
-			} else {
-				var options = {};
-				var offset = element.get('data', 'fit-text-offset', true);
-				if (offset != null) options.offset = offset;
-				var fitText = new FitText(element, span, options);
-				fitText.fit();
-				element.store('FitText', fitText).set('title', text);
-				if (win) {
-					var fitTextFit = fitText.fit.bind(fitText);
-					//rerun this after a while, as some filters muck about w/ the DOM
-					//I'm not crazy about this solution, but it'll have to do for now
-					fitTextFit.delay(10); 
-					win.addEvent('unshade', fitTextFit);
-					jframe.markForCleanup(function(){
-						win.removeEvent('unshade', fitTextFit);
-					});
-				}
-			}
-		};
-		fitter();
-	};
-
-	var tdStyles = {
-		'max-width': 200, //this number doesn't seem to matter, really
-		'overflow': 'hidden'
-	};
-
-	var fixTable = function(table) {
-		if (!table || table.retrieve('fittext:fixed')) return;
-		table.store('fittext:fixed', true);
-		table.getElements('tbody td').setStyles(tdStyles);
-	};
-	
-
-	CCS.JFrame.addGlobalFilters({
-
-		/*
-			truncates text automatically for elements with the class .ccs-truncate
-			elements cannot have child elements (only text)
-		*/
-		truncate: function(container) {
-			if (!container.get('html').contains('ccs-truncate')) return;
-			var tables = container.getElements('table');
-			var tds = [];
-			tables.each(function(table){
-				var tdsToTruncate = table.getElements('td.ccs-truncate');
-				if (tdsToTruncate.length) fixTable(table);
-				tdsToTruncate.each(function(td){
-					fitIt(td, this);
-					tds.push(td);
-				}, this);
-			}, this);
-			
-			container.getElements('.ccs-truncate').each(function(el){
-				if (!tds.contains(el)) {
-					//this is for partial refresh
-					//we're assuming this td element, which is not in a table
-					//because it would have been found in the block above,
-					//is already in a table that's been fixed
-					//so we just apply the td styles to it alone
-					if (el.get('tag') == 'td') el.setStyles(tdStyles); 
-					fitIt(el, this);
-				}
-			}, this);
-		},
-
-		/*
-			finds all elements wth data-fit-text properties - these properties must be selectors
-			for the elements to apply the FitText class to.
-		*/
-		truncateChildren: function(container){
-			if (!container.get('html').contains('data-fit-text')) return;
-			container.getElements('[data-fit-text]').each(function(fitParent){
-				var selector = fitParent.get('data', 'fit-text');
-				var table = fitParent.get('tag') == 'table';
-				if (table) fixTable(fitParent);
-				fitParent.getElements(selector).each(function(el){
-					fitIt(el, this);
-				}, this);
-			}, this);
-		}
-
-	});
+		}, this);
+	}
 
-})();
+});

+ 4 - 17
desktop/core/static/js/Source/JFrameFilters/CCS.JFrame.Input.js

@@ -17,7 +17,7 @@
 ---
 description: Inputs.
 provides: [CCS.JFrame.Input]
-requires: [/CCS.JFrame, Widgets/ART.Input, Widgets/ART.Search]
+requires: [/CCS.JFrame]
 script: CCS.JFrame.Input.js
 
 ...
@@ -31,22 +31,9 @@ CCS.JFrame.addGlobalFilters({
 	artInputs: function(container) {
 		if (!container.get('html').match(re)) return;
 		container.getElements('.ccs-search, .ccs-input').each(function(input){
-			var temp = new Element('span').injectAfter(input);
-			var widget = new ART[input.hasClass('ccs-search') ? 'Search' : 'Input']({
-				inputElement: input,
-				placeholder: false,
-				onChange: function() {
-					input.fireEvent('change');
-				}
-			});
-			widget.inject(this, temp, 'after');
-			widget.draw();
-			temp.dispose();
-			
-			if (input.retrieve('OverText')) input.retrieve('OverText').reposition();
-			this.markForCleanup(function(){
-				widget.eject();
-			});
+			dbug.warn('you are using a deprecated JFrame filter (ccs-search or ccs-input) on %o, use the ArtInput data-filter instead.', input);
+			input.addDataFilter('ArtInput');
+			if (input.hasClass('ccs-search')) input.set('data', 'art-input-type', 'search');
 		}, this);
 	}
 

+ 3 - 20
desktop/core/static/js/Source/JFrameFilters/CCS.JFrame.OverText.js

@@ -17,7 +17,7 @@
 ---
 description: Sets up all inputs with the css class .overtext to have an OverText instance for inline labeling. The OverText label element inherits all the classes assigned to the input (at run time) with an "OverText-" prefix, allowing you to style OverText labels on a per-input basis.
 provides: [CCS.JFrame.OverText]
-requires: [/CCS.JFrame, More/OverText]
+requires: [/CCS.JFrame]
 script: CCS.JFrame.OverText.js
 
 ...
@@ -28,26 +28,9 @@ CCS.JFrame.addGlobalFilters({
 	overText: function(container){
 		//get all input.overtext elements and make an OverText for them.
 		var ots = container.getElements('input.overtext, textarea.overtext').map(function(input){
-			var ot = new OverText(input);
-			input.get('class').split(' ').each(function(cls) {
-				ot.text.addClass('OverText-'+cls);
-			});
-			return ot;
+			dbug.warn('you are using a deprecated JFrame filter (overtext) on %o, use the OverText data-filter instead.', input);
+			input.addDataFilter('OverText');
 		});
-		var updater = function(){
-			(function(){
-				ots.each(function(ot) {
-					ot.reposition();
-				});
-			}).delay(10);
-		};
-		this.addEvent('loadComplete', updater);
-		var win = this.getWindow();
-		if (win) win.addEvent('unshade', updater);
-		this.markForCleanup(function(){
-			this.removeEvent('loadComplete', updater);
-			if (win) win.removeEvent('unshade', updater);
-		}.bind(this));
 	}
 
 });

+ 2 - 11
desktop/core/static/js/Source/JFrameFilters/CCS.JFrame.SideBySideSelect.js

@@ -32,17 +32,8 @@ CCS.JFrame.addGlobalFilters({
 			//if the element with the side_by_side class is the container of the select, get the 
 			//select element within it.
 			if (select && select.get('tag') != "select") select = select.getElement('select');
-			if (!select || !select.get('multiple')){
-				dbug.warn("Side_by_side_select element does not contain multiple=true.");
-				return;
-			}
-			//create a new instance of SideBySideSelect
-			var sbss = new ART.SideBySideSelect(select);
-			sbss.inject(this, select, 'after');
-			select.hide();
-			this.markForCleanup(function(){
-				sbss.destroy();
-			});
+			dbug.warn('you are using a deprecated JFrame filter (side_by_side_select) on %o, use the SideBySideSelect data-filter instead.', select);
+			select.addDataFilter('SideBySideSelect');
 		}, this);
 	}
 

+ 3 - 1
desktop/core/static/js/package.yml

@@ -62,6 +62,8 @@ sources: [
   Source/Native/Element.Data.js,
   Source/StaticThirdParty/DynamicTextarea.js,
   Source/CCS/CCS.PostEditor.Simple.js,
-  Source/UI/ART.SideBySideSelect.js
+  Source/UI/ART.SideBySideSelect.js,
+  Source/BehaviorFilters/Behavior.FitText.js,
+  Source/BehaviorFilters/Behavior.SideBySideSelect.js,
 ]
 version: Unknown

+ 1 - 1
ext/thirdparty/js/art-widgets.hash

@@ -1 +1 @@
-7cc7221269cb7896b476578506a72695b47e137a
+25952ed9d715ed02fe3d546d9ccaeb492c87c1ab

+ 1 - 1
ext/thirdparty/js/mootools-more.hash

@@ -1 +1 @@
-124d4879fcbf446b3495f7aa87b57e28086ce2ee
+d9bcdcade799d7254241bf0f3fb24ba5d85743e8