Пример #1
0
/** 
 *  Smarty plugin that allows you to code a block of HTML formatted nicely for development, but convert it to JSON for use with Javascript.
 *  
 *  @param array The params from smarty tag.
 *  @param string The HTML code.
 *  @param object WFSmarty object of the current tpl.
 *  @return string The rendered HTML.
 */
function smarty_block_JSON($params, $content, &$smarty, &$repeat)
{
    // beginning or end block?
    if (isset($content)) {
        print WFJSON::encode($content);
    }
}
Пример #2
0
 public function getSetupJS()
 {
     $options = array();
     foreach (array('base', 'loadOptional', 'allowRollup', 'combine', 'comboBase') as $k) {
         if ($this->{$k}() === NULL) {
             continue;
         }
         $options[$k] = $this->{$k}();
     }
     if ($this->debug()) {
         $options['filter'] = "DEBUG";
         $options['allowRollup'] = false;
     }
     $optionsJSON = WFJSON::encode($options);
     return "<script>new PHOCOA.YUI({$optionsJSON});</script>";
 }
Пример #3
0
 /**
  * Exception handler for the WFRequestController.
  *
  * This is basically the uncaught exception handler for the request cycle.
  * We want to have this in the request object because we want the result to be displayed within our skin system.
  * This function will display the appropriate error page based on the deployment mode for this machine.
  *
  * @param Exception The exception object to handle.
  */
 function handleException(Exception $e)
 {
     // give ourselves a little more memory so we can process the exception
     ini_set('memory_limit', memory_get_usage() + 25000000);
     // grab error_get_last ASAP so that it cannot get adulterated by other things.
     // we will inject it into things downstream that want it.
     $standardErrorData = WFExceptionReporting::generatedStandardizedErrorDataFromException($e);
     // let the current module try to handle the exception
     if ($this->rootModuleInvocation) {
         $this->rootModuleInvocation->handleUncaughtException($e);
     }
     $webAppDelegate = WFWebApplication::sharedWebApplication()->delegate();
     if (is_object($webAppDelegate) && method_exists($webAppDelegate, 'handleUncaughtException')) {
         $handled = $webAppDelegate->handleUncaughtException($e);
         if ($handled) {
             return;
         }
     }
     WFExceptionReporting::log($standardErrorData);
     $exceptionPage = new WFSmarty();
     // LEGACY tpl var setup (in case there are old .tpl's that expect it
     // build stack of errors (php 5.3+)
     if (method_exists($e, 'getPrevious')) {
         $tmpE = $e;
         $allExceptions = array();
         do {
             $allExceptions[] = $tmpE;
         } while ($tmpE = $tmpE->getPrevious());
     } else {
         $allExceptions = array($e);
     }
     $exceptionPage->assign('exceptions', $allExceptions);
     $exceptionPage->assign('exceptionClass', get_class($allExceptions[0]));
     $exceptionPage->assign('home_url', WWW_ROOT . '/');
     // end LEGACY
     // modern format
     $exceptionPage->assign('location', "http://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}");
     $exceptionPage->assign('headline', "{$standardErrorData[0]['title']}: {$standardErrorData[0]['message']}");
     $exceptionPage->assign('standardErrorData', $standardErrorData);
     $exceptionPage->assign('standardErrorDataJSON', WFJSON::encode(array('error' => $standardErrorData, '$_SERVER' => $_SERVER, '$_REQUEST' => $_REQUEST, '$_SESSION' => $_SESSION)));
     if (IS_PRODUCTION) {
         $exceptionPage->setTemplate(WFWebApplication::appDirPath(WFWebApplication::DIR_SMARTY) . '/app_error_user.tpl');
     } else {
         $exceptionPage->setTemplate(WFWebApplication::appDirPath(WFWebApplication::DIR_SMARTY) . '/app_error_developer.tpl');
     }
     // display the error
     $body_html = $exceptionPage->render(false);
     // output error info
     header("HTTP/1.0 500 Uncaught Exception");
     if ($this->isAjax()) {
         print strip_tags($body_html);
     } else {
         $skin = new WFSkin();
         $skin->setDelegateName(WFWebApplication::sharedWebApplication()->defaultSkinDelegate());
         $skin->setBody($body_html);
         $skin->setTitle("An error has occurred.");
         $skin->render();
     }
 }
Пример #4
0
 public function data()
 {
     return WFJSON::json_encode($this->data);
 }
Пример #5
0
function smarty_modifier_json($in)
{
    return WFJSON::encode($in);
}
Пример #6
0
 function render($blockContent = NULL)
 {
     if ($this->hidden) {
         return NULL;
     }
     // get there reference to the named item
     // set the name / value
     // render
     $html = '<input type="' . ($this->imagePath ? 'image' : 'submit') . '"' . ($this->imagePath ? ' src="' . $this->imagePath . '"' : '') . ($this->class ? ' class="' . $this->class . '"' : '') . ' id="' . $this->id() . '"' . ' name="action|' . $this->id() . '"' . ' value="' . $this->label() . '"' . $this->getJSActions() . "/>\n" . $this->jsStartHTML();
     if ($this->duplicateSubmitMessage or $this->postSubmitLabel) {
         $duplicateSubmitMessage = $this->duplicateSubmitMessage ? $this->duplicateSubmitMessage : '';
         $postSubmitLabel = $this->postSubmitLabel ? $this->postSubmitLabel : '';
         $html .= "\n            PHOCOA.namespace('widgets.{$this->id}');\n            PHOCOA.widgets.{$this->id}.isSubmitted = false;\n            // define the handleEvent function before the WFAction initializes; it needs it to be defined.\n            PHOCOA.namespace('widgets.{$this->id}.events.click');\n            PHOCOA.widgets['{$this->id}'].events['click'].handleEvent = function(e) {\n                var duplicateSubmitMessage = " . WFJSON::json_encode($duplicateSubmitMessage) . ";\n                var postSubmitLabel = " . WFJSON::json_encode($postSubmitLabel) . ";\n                if (PHOCOA.widgets.{$this->id}.isSubmitted && duplicateSubmitMessage)\n                {\n                    alert(duplicateSubmitMessage);\n                    e.stop();\n                    return false;\n                }\n                if (postSubmitLabel)\n                {\n                    \$('{$this->id}').setValue(postSubmitLabel);\n                }\n                PHOCOA.widgets.{$this->id}.isSubmitted = true;\n            };\n            ";
     }
     $html .= $this->getListenerJS() . $this->jsEndHTML();
     return $html;
 }
Пример #7
0
 function initJS($blockContent)
 {
     // determine body html
     $bodyHTML = $blockContent === NULL ? $this->body : $blockContent;
     // calcualte effects
     $effects = array();
     foreach ($this->effects as $name => $duration) {
         $effects[] = "{ effect: {$name}, duration: {$duration} }";
     }
     $addEffectsJS = NULL;
     if (count($effects)) {
         $addEffectsJS = '[ ' . join(', ', $effects) . ' ]';
     }
     $script = "\nPHOCOA.namespace('widgets.{$this->id}.Module');\nPHOCOA.widgets.{$this->id}.Module.queueProps = function(o) {\n    // alert('id={$this->id}: queue Module props');\n    // queue Module props here\n};\nPHOCOA.widgets.{$this->id}.Module.init = function() {\n    var module = new YAHOO.widget.{$this->containerClass}(\"{$this->id}\");\n    module.subscribe('changeBody', function(el) { PHOCOA.widgets.{$this->id}.scriptsEvald = false; } );\n    module.showEvent.subscribe(function(el) {\n        if (!PHOCOA.widgets.{$this->id}.scriptsEvald)\n        {\n            PHOCOA.widgets.{$this->id}.scriptsEvald = true;\n            this.body.innerHTML.evalScripts();\n        }\n    }, module);\n    module.cfg.queueProperty('visible', " . ($this->visible ? 'true' : 'false') . ");\n    module.cfg.queueProperty('monitorresize', " . ($this->monitorresize ? 'true' : 'false') . ");\n    PHOCOA.widgets.{$this->id}.{$this->containerClass}.queueProps(module);";
     if ($this->buildModuleProgrammatically) {
         // renderTo is required for buildModuleProgrammatically. should default to document.body if user doesn't specify something more specific
         $renderTo = $this->renderTo ? $this->renderTo : 'document.body';
         $script .= "\n    module.setHeader(" . ($this->header === NULL ? '""' : WFJSON::json_encode($this->header)) . ");\n    module.setBody(" . ($bodyHTML === NULL ? '""' : WFJSON::json_encode($bodyHTML)) . ");\n    module.setFooter(" . ($this->footer === NULL ? '""' : WFJSON::json_encode($this->footer)) . ");\n    module.render({$renderTo});\n";
     } else {
         $script .= "\n    module.render();\n";
     }
     $script .= ($addEffectsJS ? "\n    module.cfg.setProperty('effect', {$addEffectsJS});" : NULL) . (get_class($this) != 'WFYAHOO_widget_Module' ? "\n    YAHOO.util.Dom.setStyle('{$this->id}', 'display', 'block')" : NULL) . "\n    PHOCOA.runtime.addObject(module, '{$this->id}');\n};\n";
     if (get_class($this) == 'WFYAHOO_widget_Module') {
         $script .= "PHOCOA.widgets.{$this->id}.init = function() { PHOCOA.widgets.{$this->id}.Module.init(); };";
     }
     return $script;
 }
Пример #8
0
    function render($blockContent = NULL)
    {
        $loader = WFYAHOO_yuiloader::sharedYuiLoader();
        // jquery
        $loader->addModule('jquery', 'js', NULL, 'http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.js', NULL, NULL, NULL, NULL);
        // jquery-ui
        $loader->addModule('jqueryui-css', 'css', NULL, 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css', NULL, NULL, NULL, NULL);
        $loader->addModule('jqueryui', 'js', NULL, 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.js', array('jquery', 'jqueryui-css'), NULL, NULL, NULL);
        // query-file-uploader
        $loader->addModule('jquery-file-uploader', 'js', NULL, $this->getWidgetWWWDir() . '/jquery.fileupload.js', array('jquery'), NULL, NULL, NULL);
        // and the UI
        $loader->addModule('jquery-file-uploader-ui-css', 'css', NULL, $this->getWidgetWWWDir() . '/jquery.fileupload-ui.css', NULL, NULL, array('jqueryui-css'), NULL);
        $loader->addModule('jquery-file-uploader-ui', 'js', NULL, $this->getWidgetWWWDir() . '/jquery.fileupload-ui.js', array('jquery-file-uploader', 'jqueryui', 'jquery-file-uploader-ui-css'), NULL, NULL, NULL);
        $loader->yuiRequire('jquery-file-uploader-ui');
        // @todo In future this should not need to be a WFForm subclass; should be able to drop it in a form anywhere.
        //$form = $this->getForm();
        //if (!$form) throw new WFException("WFHTML5_Uploader must be a child of a WFForm.");
        $form = $this;
        // craft a WFRPC that we can insert into the form stream to have our callback fire
        $rpc = WFRPC::RPC()->setInvocationPath($this->page->module()->invocation()->invocationPath())->setTarget('#page#' . $this->id)->setAction('_handleAsyncSingleUpload')->setForm($this)->setIsAjax(true);
        $uploadFormData = json_encode($rpc->rpcAsParameters($this));
        // figure out vars for drop-in into HTML block
        $sequentialUploads = var_export($this->maxConcurrentUploads == 1, true);
        $autoupload = var_export($this->autoupload, true);
        $fileInputName = "{$this->getInputFileName()}[]";
        // HTML
        $formInnardsHTML = <<<END
                {$blockContent}
                <input type="file" name="{$fileInputName}" multiple>
                <button type="submit" name="action|{$this->id}">Upload</button>
                <div class="file_upload_label">Click or Drop to Upload</div>
END;
        $html = parent::render($formInnardsHTML);
        $maxUploadBytesJSON = WFJSON::encode($this->maxUploadBytes);
        // progress indicators after form since the blueimp plugin takes over the entire form area for drag-n-drop
        $html .= <<<END
<div id="{$this->id}_progressAll" style="display: none;"></div>
<table id="{$this->id}_table" style="display: none;"></table>
END;
        $withJqueryJS = <<<END
function() {
    jQuery.noConflict();
    jQuery(function () {
        window.uploader = jQuery('#{$form->id()}').fileUploadUI({
            formData: {$uploadFormData},
            sequentialUploads: {$sequentialUploads},
            beforeSend: function (event, files, index, xhr, handler, callBack) {
                document.fire ('WFHTML5_Uploader:uploadStart');
                jQuery('#{$this->id}_table, #{$this->id}_progressAll').show();
                var fileSize = files[index].fileSize ? files[index].fileSize : files[index].size  // Firefox calls it file.size instead of file.fileSize
                if ({$maxUploadBytesJSON} && fileSize > {$maxUploadBytesJSON})
                {
                    var json = {
                        'name': files[index].name,
                        'description': ('File exceeds maximum file size of ' + {$maxUploadBytesJSON} + ' bytes.'),
                        'uploadOK': false
                    };
                    handler.downloadRow = handler.buildDownloadRow(json, handler);
                    handler.replaceNode(handler.uploadRow, handler.downloadRow, null);
                    return;
                }
                if ({$autoupload})
                {
                    callBack();
                }
            },
            progressAllNode: jQuery('#{$this->id}_progressAll'),
            uploadTable: jQuery('#{$this->id}_table'),
            onCompleteAll: function() {
                var autoRedirectToUrlOnCompleteAll = '{$this->autoRedirectToUrlOnCompleteAll}';
                if (autoRedirectToUrlOnCompleteAll)
                {
                    document.fire ('WFHTML5_Uploader:uploadAllComplete');
                    window.location.href = autoRedirectToUrlOnCompleteAll;
                }
            },
            buildUploadRow: function (files, index) {
                return jQuery('<tr>' +
                        '<td width="175">' + files[index].name + '<\\/td>' +
                        '<td width="1">&nbsp;<\\/td>' +
                        '<td width="250">&nbsp;<\\/td>' +
                        '<td width="16" class="file_upload_cancel">' +
                            '<button class="ui-state-default ui-corner-all" title="Cancel">' +
                            '<span class="ui-icon ui-icon-cancel">Cancel<\\/span>' +
                            '<\\/button><\\/td>' +
                        '<td class="file_upload_progress" style="width: 160px"><div><\\/div><\\/td>' +
                        '<\\/tr>');
            },
            buildDownloadRow: function (file, handler) {
                var thumbHTML = '&nbsp;';
                if (file.thumb)
                {
                    thumbHTML = '<img s'+'rc="'+file.thumb+'" style="float: right;"/>';
                }
                return jQuery('<tr>' +
                    '<td width="175">' + file.name + '<\\/td>' +
                    '<td width="' + (thumbHTML ? 100 : 1) + '">' + thumbHTML + '<\\/td>' +
                    '<td width="250">' + file.description + '<\\/td>' + 
                    '<td width="16"><span class="ui-icon ' + (file.uploadOK ? 'ui-icon-check' : 'ui-icon-alert') + '"><\\/span><\\/td>' + 
                    '<td><\\/td>' + 
                    '<\\/tr>');
            }
        });
    });
}
END;
        $bootstrapJS = $loader->jsLoaderCode($withJqueryJS);
        $html .= <<<END
<script> 
{$bootstrapJS}
</script> 
END;
        return $html;
    }
Пример #9
0
 public function data()
 {
     return "/*-secure-\n" . WFJSON::json_encode($this->data) . "\n*/";
 }
Пример #10
0
 function initJS($blockcontent)
 {
     $script = "\nPHOCOA.namespace('widgets.{$this->id}');\nPHOCOA.widgets.{$this->id}.loadData = function(node, fnLoadComplete)\n{\n    var tNode = node;\n    var pathParts = new Array();\n    while ( true ) {\n        pathParts.push(tNode.nodeId);\n        tNode = tNode.parent;\n        if (!tNode || tNode.nodeId == null) break;\n    }\n    pathParts.reverse();\n    var path = pathParts.join('|');\n";
     if ($this->dynamicDataLoader) {
         $script .= "\n    var rpc = new PHOCOA.WFRPC('" . WWW_ROOT . '/' . $this->page()->module()->invocation()->invocationPath() . "', '#page#{$this->id}', 'ajaxLoadData');\n    rpc.callback.success = PHOCOA.widgets.{$this->id}.loadDataHandleSuccess;\n    rpc.callback.failure = PHOCOA.widgets.{$this->id}.loadDataHandleFailure;\n    rpc.callback.argument = { loadComplete: fnLoadComplete, node: node };\n    " . ($this->queryFieldId ? "var qVal = PHOCOA.widgets.{$this->queryFieldId}.getValue();" : NULL) . "\n    rpc.execute(path" . ($this->queryFieldId ? ", qVal" : NULL) . ");\n    ";
     } else {
         // backwards compatibility
         $script .= "\n    var url = '{$this->bcCallback}/' + encodeURIComponent(path);\n    var callback = {\n        success: PHOCOA.widgets.{$this->id}.loadDataHandleSuccess,\n        failure: PHOCOA.widgets.{$this->id}.loadDataHandleFailure,\n        argument: { loadComplete: fnLoadComplete, node: node }\n    };\n    YAHOO.util.Connect.asyncRequest('GET', url, callback);\n    ";
     }
     $script .= "\n};\n\nPHOCOA.widgets.{$this->id}.loadDataHandleSuccess = function(o)\n{\n    if (o.argument.node.isRoot())\n    {\n        // always clear all kids on root before adding; this solves the problem of multiple reloadTree() being called before first result rolls in (and thus merges results)\n        PHOCOA.runtime.getObject('{$this->id}').removeChildren(o.argument.node);\n    }\n\n    // process XML data - this is the only x-browser way I could find since Safari doesn't support XPath yet\n    var xml = o.responseXML.documentElement;\n    var items = xml.getElementsByTagName('item');\n    for (var i = 0; i < items.length; i++)\n    {\n        var nodeData = items[i].firstChild.nodeValue;\n        var nodeId = items[i].getAttribute('nodeId');\n        var couldHaveChildren = items[i].getAttribute('couldHaveChildren');\n        var newNode = new YAHOO.widget.{$this->nodeType}(nodeData, o.argument.node, false, true);\n        newNode.nodeId = nodeId;\n        if (couldHaveChildren == '0')\n        {\n            newNode.dynamicLoadComplete = true;\n        }\n        " . ($this->enableDragDropTree ? "new DDSend(newNode.contentElId);" : NULL) . "\n    }\n\n    if (items.length == 0 && o.argument.node.isRoot())\n    {\n        \$('{$this->id}_status').show().update('No matches.');\n    }\n    else\n    {\n        \$('{$this->id}_status').hide();\n    }\n\n    // complete node loading\n    o.argument.loadComplete();\n\n    if (o.argument.node.isRoot() && " . ($this->autoExpandUntilChoices ? 1 : 0) . ") PHOCOA.widgets.{$this->id}.autoExpand(o.argument.node);\n};\n\nPHOCOA.widgets.{$this->id}.loadDataHandleFailure = function(o)\n{\n    \$('{$this->id}_status').hide();\n    alert('failed to load data');\n};\n\n// utility functions not included in YUI Tree\nPHOCOA.widgets.{$this->id}.reloadTree = function()\n{\n    var tree = PHOCOA.runtime.getObject('{$this->id}');\n    var rootNode = tree.getRoot();\n    tree.removeChildren(rootNode);\n    rootNode.refresh();\n    PHOCOA.widgets.{$this->id}.loadData(rootNode, function() { rootNode.loadComplete(); });\n};\nPHOCOA.widgets.{$this->id}.reloadNode = function(node)\n{\n    var tree = PHOCOA.runtime.getObject('{$this->id}');\n    tree.removeChildren(node);\n    node.refresh();\n    PHOCOA.widgets.{$this->id}.loadData(node, function() { node.loadComplete(); });\n};\nPHOCOA.widgets.{$this->id}.autoExpand = function(node) { if (node.children.length === 1) node.children[0].expand(); };\n// end util funcs\n\nPHOCOA.widgets.{$this->id}.init = function()\n{\n    var {$this->id} = new YAHOO.widget.TreeView('{$this->id}');\n    var root = {$this->id}.getRoot();\n    var nodes = new Array();\n";
     // load the root data set if it hasn't been set already
     if ($this->value === NULL) {
         if ($this->dynamicDataLoader) {
             $this->setValue(call_user_func($this->dynamicDataLoader, NULL, NULL));
         } else {
             $this->setValue(array());
         }
     }
     // add items
     // iterative algorithm for travesing nested list and creating JS to add all nodes in proper order
     // initailze itemStack with first level
     $itemStack = array();
     foreach (array_reverse($this->value) as $child) {
         $itemStack[] = $child;
         $itemParentPaths[] = NULL;
     }
     while (true) {
         // get item to work with; flow control
         $currentItem = array_pop($itemStack);
         if (!$currentItem) {
             break;
         }
         if (!$currentItem instanceof WFYAHOO_widget_TreeViewNode) {
             throw new Exception("Items in tree data must be WFYAHOO_widget_TreeViewNode instances.");
         }
         $currentParentPath = array_pop($itemParentPaths);
         // if we're not in dynamic data loading mode, we can auto-calculate couldHaveChildren
         if (!$this->dynamicallyLoadsData()) {
             if (!$currentItem->hasChildren()) {
                 $currentItem->setCouldHaveChildren(false);
             }
         }
         // add item to tree
         $labelPath = $currentParentPath . "|" . $currentItem->id();
         if ($currentParentPath) {
             $parentNode = "nodes['{$currentParentPath}']";
         } else {
             $parentNode = "root";
         }
         $script .= "    nodes['{$labelPath}'] = new YAHOO.widget.{$this->nodeType}(" . WFJSON::encode($currentItem->data()) . ", {$parentNode}, false, true);\n";
         $script .= "    nodes['{$labelPath}'].nodeId = '" . addslashes($currentItem->id()) . "';\n";
         if (!$currentItem->couldHaveChildren()) {
             $script .= "    nodes['{$labelPath}'].dynamicLoadComplete = true;\n";
         }
         $script .= $this->enableDragDropTree ? "new DDSend(nodes['{$labelPath}'].contentElId);" : NULL;
         if ($currentItem->hasChildren()) {
             foreach (array_reverse($currentItem->children()) as $child) {
                 $itemStack[] = $child;
                 $itemParentPaths[] = $labelPath;
             }
         }
     }
     // add dynamic loader if needed
     if ($this->dynamicallyLoadsData()) {
         $script .= "{$this->id}.setDynamicLoad(PHOCOA.widgets.{$this->id}.loadData, 1);\n";
         //throw( new WFException("dynamic loading not yet implemented"));
     }
     if ($this->autoExpandUntilChoices) {
         $script .= "{$this->id}.subscribe('expandComplete', PHOCOA.widgets.{$this->id}.autoExpand);";
     }
     if ($this->expandOnClick === false) {
         $script .= "{$this->id}.subscribe('clickEvent', function(e) { return false; });";
     }
     // finish script init function
     $script .= "\n    PHOCOA.runtime.addObject({$this->id}, '{$this->id}');\n    {$this->id}.draw();\n            ";
     if ($this->queryFieldId) {
         $qf = $this->page()->outlet($this->queryFieldId);
         if (!$qf instanceof WFSearchField) {
             throw new WFException("queryFieldId must be the ID of a WFSearchField.");
         }
         $script .= "\n    YAHOO.util.Event.onContentReady('{$this->queryFieldId}', function() {\n        var queryField = \$('{$this->queryFieldId}');\n        queryField.observe('phocoa:WFSearchField:search', PHOCOA.widgets.{$this->id}.reloadTree);\n        queryField.observe('phocoa:WFSearchField:clear', PHOCOA.widgets.{$this->id}.reloadTree);\n    });\n                ";
     }
     $script .= "\n}\n";
     return $script;
 }
Пример #11
0
 function initJS($blockContent)
 {
     // craft a WFRPC that we can insert into the form stream to have our callback fire
     $rpc = WFRPC::RPC()->setInvocationPath($this->page->module()->invocation()->invocationPath())->setTarget('#page#' . $this->id)->setAction('handleUploadedFile')->setForm($this->getForm())->setRunsIfInvalid(true)->setIsAjax(true);
     $html = "\n        PHOCOA.widgets.{$this->id}.status = { 'READY': 0, 'UPLOADING': 1, 'COMPLETE': 2};\n        PHOCOA.widgets.{$this->id}.summaryStatus = PHOCOA.widgets.{$this->id}.status.READY;\n        PHOCOA.widgets.{$this->id}.emptyFileListDisplay = function() {\n            PHOCOA.widgets.{$this->id}.filesToUploadTracker = {};\n            PHOCOA.widgets.{$this->id}.filesToUploadTotalBytes = 0;\n            \$('{$this->id}_fileList').update('').hide();\n        };\n        PHOCOA.widgets.{$this->id}.makePrettySize = function(sz, decimals) {\n            if (typeof decimals === 'undefined')\n            {\n                decimals = 2;\n            }\n            var suffixes = ['Bytes','KB','MB','GB','TB'];\n            var i = 0;\n\n            while (sz >= 1024 && (i < suffixes.length - 1)){\n                sz /= 1024;\n                i++;\n            }\n            return Math.round(sz*Math.pow(10,decimals))/Math.pow(10,decimals) + ' '  + suffixes[i];\n        };\n        PHOCOA.widgets.{$this->id}.init = function() {\n            PHOCOA.widgets.{$this->id}.emptyFileListDisplay();   // initialize\n\n            YAHOO.util.Event.onDOMReady(function () { \n                var uiLayer = YAHOO.util.Dom.getRegion('{$this->id}_browseTrigger');\n                var overlay = YAHOO.util.Dom.get('{$this->id}');\n                YAHOO.util.Dom.setStyle(overlay, 'width', uiLayer.width + 'px');\n                YAHOO.util.Dom.setStyle(overlay, 'height', uiLayer.height + 'px');\n            });\n\n            YAHOO.widget.Uploader.SWFURL = '" . WFYAHOO_yuiloader::sharedYuiLoader()->localYUI() . "uploader/assets/uploader.swf'; \n            var uploader = new YAHOO.widget.Uploader('{$this->id}');\n            PHOCOA.runtime.addObject(uploader, '{$this->id}');\n\n            // can't customize until the SWF is ready\n            uploader.addListener('contentReady', function() {\n                uploader.setAllowMultipleFiles(" . ($this->allowMultiple ? 'true' : 'false') . ");\n                uploader.setSimUploadLimit(1);\n            });\n\n            \$('{$this->id}_uploadTrigger').observe('click', function() {\n                PHOCOA.runtime.getObject('{$this->id}').uploadAll('" . $rpc->url() . "', 'POST', " . WFJSON::encode($rpc->rpcAsParameters()) . ", '{$this->id}');\n            });\n\n            uploader.addListener('fileSelect', function(e) {\n                // fileSelect sends ALL files tracked by flash, not just the ones added in the most recent file select dialog\n                PHOCOA.widgets.{$this->id}.emptyFileListDisplay();\n                \$('{$this->id}_fileList').show();\n\n                var files = \$H(e.fileList).values();\n\n                files.pluck('id').each(function(o) {\n                    PHOCOA.widgets.{$this->id}.filesToUploadTracker[o] = {\n                        id: e.fileList[o].id,\n                        name: e.fileList[o].name,\n                        size: e.fileList[o].size,\n                        sizeProgress: 0,\n                        status: PHOCOA.widgets.{$this->id}.status.READY,\n                        error: false,\n                        resultMessage: null\n                        };\n                });\n                ";
     if ($this->maxUploadBytes !== NULL) {
         $html .= "\n                var tooBig = [];\n                var justRight = {};\n                \$H(PHOCOA.widgets.{$this->id}.filesToUploadTracker).values().each(function(o) {\n                    if (o.size > {$this->maxUploadBytes})\n                    {\n                        PHOCOA.runtime.getObject('{$this->id}').removeFile(o.id);\n                        tooBig.push(o);\n                    }\n                    else\n                    {\n                        justRight[o.id] = o;\n                    }\n                });\n                PHOCOA.widgets.{$this->id}.filesToUploadTracker = justRight;\n                if (tooBig.length)\n                {\n                    alert('The following files will be skipped because they are more than ' + PHOCOA.widgets.{$this->id}.makePrettySize({$this->maxUploadBytes}) + \":\\n- \" + tooBig.pluck('name').join(\"\\n- \"));\n                }\n                ";
     }
     $html .= "\n                \$H(PHOCOA.widgets.{$this->id}.filesToUploadTracker).values().pluck('size').each(function(o) {\n                    PHOCOA.widgets.{$this->id}.filesToUploadTotalBytes += o;\n                });\n                PHOCOA.widgets.{$this->id}.updateProgress();\n            });\n            uploader.addListener('uploadStart', function(e) {\n                PHOCOA.widgets.{$this->id}.summaryStatus = PHOCOA.widgets.{$this->id}.status.UPLOADING;\n                PHOCOA.widgets.{$this->id}.updateProgress();\n                PHOCOA.widgets.{$this->id}.filesToUploadTracker[e.id].status = PHOCOA.widgets.{$this->id}.status.UPLOADING;\n            });\n            uploader.addListener('uploadProgress', function(e) {\n                PHOCOA.widgets.{$this->id}.filesToUploadTracker[e.id].sizeProgress = e.bytesLoaded;\n                PHOCOA.widgets.{$this->id}.updateProgress();\n            });\n            uploader.addListener('uploadError', function(e) {\n                PHOCOA.widgets.{$this->id}.filesToUploadTracker[e.id].error = true;\n                PHOCOA.widgets.{$this->id}.filesToUploadTracker[e.id].resultMessage = e.status;\n                PHOCOA.widgets.{$this->id}.filesToUploadTracker[e.id].status = PHOCOA.widgets.{$this->id}.status.COMPLETE;\n                PHOCOA.widgets.{$this->id}.updateProgress();\n            });\n            uploader.addListener('uploadComplete', function(e) {\n                PHOCOA.widgets.{$this->id}.filesToUploadTracker[e.id].sizeProgress = PHOCOA.widgets.{$this->id}.filesToUploadTracker[e.id].size;\n                PHOCOA.widgets.{$this->id}.filesToUploadTracker[e.id].status = PHOCOA.widgets.{$this->id}.status.COMPLETE;\n                PHOCOA.widgets.{$this->id}.updateProgress();\n\n                // are we done with ALL uploads?\n                if (\$H(PHOCOA.widgets.{$this->id}.filesToUploadTracker).values().all(function(o) { return o.sizeProgress === o.size; }))\n                {\n                    PHOCOA.widgets.{$this->id}.summaryStatus = PHOCOA.widgets.{$this->id}.status.COMPLETE;\n                    \$('{$this->id}_progress').update('Upload complete.');\n                    PHOCOA.runtime.getObject('{$this->id}').clearFileList();    // remove files from flash\n                    //PHOCOA.widgets.{$this->id}.emptyFileListDisplay();\n                    document.fire('WFYAHOO_widget_Uploader:allUploadsComplete', { uploadId: '{$this->id}' });\n                    if (" . WFJSON::encode(!empty($this->continueURL)) . ")\n                    {\n                        window.location = '{$this->continueURL}';\n                    }\n                }\n            });\n        };\n        PHOCOA.widgets.{$this->id}.updateProgress = function() {\n            // update summary progress bar\n            var allFilesToUpload = \$H(PHOCOA.widgets.{$this->id}.filesToUploadTracker).values();\n\n            if (allFilesToUpload.any(function(o) { return o.status !== PHOCOA.widgets.{$this->id}.status.READY; }))\n            {\n                var uploadProgressBytes = 0;\n                allFilesToUpload.pluck('sizeProgress').each( function(o) {\n                    uploadProgressBytes += o;\n                });\n                var msg = 'Upload progress: ' + Math.round(uploadProgressBytes*100 / PHOCOA.widgets.{$this->id}.filesToUploadTotalBytes) + '%';\n                \$('{$this->id}_progress').update(msg);\n            }\n\n            // Update per-file progress\n            // Sort by amount of upload left, descending. makes it easy to spot what's left and problems.\n            allFilesToUpload.sort( function(a,b) { return a.size - a.sizeProgress < b.size - b.sizeProgress } );\n            \$('{$this->id}_fileList').update('<strong>' + allFilesToUpload.length + ' file(s) selected:</strong><table><tr><th>Name</th><th nowrap>Size</th><th nowrap>Status</th></tr>' + allFilesToUpload.collect(function(o) { \n                                                                                                                            var msg = '<tr>';\n                                                                                                                            msg += '<td>' + o.name + '</td>';\n                                                                                                                            msg += '<td>' + PHOCOA.widgets.{$this->id}.makePrettySize(o.size) + '</td>';\n                                                                                                                            msg += '<td>';\n                                                                                                                            switch (o.status) {\n                                                                                                                                case PHOCOA.widgets.{$this->id}.status.READY:\n                                                                                                                                    break;\n                                                                                                                                case PHOCOA.widgets.{$this->id}.status.UPLOADING:\n                                                                                                                                    var pct = Math.round(100 * o.sizeProgress / o.size);\n                                                                                                                                    msg += ' progress: ' + pct + '%';\n                                                                                                                                    break;\n                                                                                                                                case PHOCOA.widgets.{$this->id}.status.COMPLETE:\n                                                                                                                                    if (o.error)\n                                                                                                                                    {\n                                                                                                                                        msg += ' Error uploading: ' + o.resultMessage;\n                                                                                                                                    }\n                                                                                                                                    else\n                                                                                                                                    {\n                                                                                                                                        msg += ' Uploaded.';\n                                                                                                                                    }\n                                                                                                                                    break;\n                                                                                                                            }\n                                                                                                                            msg += '</td>';\n                                                                                                                            msg += '</tr>';\n                                                                                                                            return msg;\n                                                                                                                        }).join('') + '</table>');\n        };\n        ";
     return $html;
 }
    function initJS($blockContent)
    {
        $myAutoCompleteContainer = "WFYAHOO_widget_AutoComplete_{$this->id}_autocomplete";
        $html = "\n        PHOCOA.widgets.{$this->id}.init = function() {\n        ";
        switch ($this->datasource) {
            case WFYAHOO_widget_AutoComplete::DATASOURCE_JS_ARRAY:
                $html .= "var jsDSArray = [";
                $first = true;
                // we allow
                $multiColumnData = false;
                foreach ($this->datasourceJSArray as $item) {
                    if ($first) {
                        $first = false;
                        if (is_array($item)) {
                            $multiColumnData = true;
                        }
                    } else {
                        $html .= ", ";
                    }
                    if ($multiColumnData) {
                        $subItems = $item;
                        array_walk($subItems, create_function('&$v,$k', '$v = \'"\' . str_replace(\'"\', \'\\"\', $v) . \'"\';'));
                        $html .= '[' . join(',', $subItems) . ']';
                    } else {
                        $html .= '"' . str_replace('"', '\\"', $item) . '"';
                    }
                }
                $html .= "];\n";
                $html .= "var acDatasource = new YAHOO.widget.DS_JSArray(jsDSArray);";
                break;
            case WFYAHOO_widget_AutoComplete::DATASOURCE_XHR:
                $html .= "\n                    // need to create a PHOCOA.RPC to get the URL for the query\n                    var acXHRRPC = new PHOCOA.WFRPC('" . WWW_ROOT . '/' . $this->page()->module()->invocation()->invocationPath() . "', '#page#{$this->id}', 'ajaxLoadData');\n                ";
                $schema = array();
                foreach ($this->dynamicDataLoaderSchema as $field) {
                    $schema[] = array('key' => $field);
                }
                $html .= "\n                    var acDatasource = new YAHOO.util.XHRDataSource();\n                    acDatasource.responseType = YAHOO.util.XHRDataSource.TYPE_JSON;\n                    acDatasource.responseSchema = {\n                        resultsList: 'results',\n                             fields: " . WFJSON::encode($schema) . "\n                    };\n                    ";
                break;
            default:
                throw new WFException("Unsupported datasource type.");
        }
        // add properties to datasource
        $html .= $this->jsForSimplePropertyConfig('acDatasource', 'maxCacheEntries', $this->datasourceMaxCacheEntries);
        // set up widget
        $html .= "\nvar AutoCompleteWidget = new YAHOO.widget.AutoComplete('{$this->id}','{$myAutoCompleteContainer}', acDatasource);\n";
        $html .= "\nAutoCompleteWidget.queryQuestionMark = false;\n";
        if ($this->datasource === WFYAHOO_widget_AutoComplete::DATASOURCE_XHR) {
            $html .= "\n\nvar urlGenerator = function(query) {\n    query = decodeURIComponent(query);  // YUI has already encoded this, but we will encode it too via PHOCOA, so we need to undo theirs to avoid encoding issues\n    var phocoaArgs = [query];\n    if (PHOCOA.widgets['{$this->id()}'] && PHOCOA.widgets['{$this->id()}'].yuiDelegate && PHOCOA.widgets['{$this->id()}'].yuiDelegate.dynamicDataLoaderCollectArguments)\n    {\n        phocoaArgs.push(PHOCOA.widgets['{$this->id()}'].yuiDelegate.dynamicDataLoaderCollectArguments());\n    }\n    phocoaArgs = phocoaArgs.flatten();\n    var url =  acXHRRPC.actionAsURL(phocoaArgs);\n    return url;\n};\nAutoCompleteWidget.generateRequest = urlGenerator;\n";
        }
        $html .= $this->jsForSimplePropertyConfig('AutoCompleteWidget', 'animVert', $this->animVert);
        $html .= $this->jsForSimplePropertyConfig('AutoCompleteWidget', 'animHoriz', $this->animHoriz);
        $html .= $this->jsForSimplePropertyConfig('AutoCompleteWidget', 'animSpeed', $this->animSpeed);
        $html .= $this->jsForSimplePropertyConfig('AutoCompleteWidget', 'queryMatchCase', $this->datasourceQueryMatchCase);
        $html .= $this->jsForSimplePropertyConfig('AutoCompleteWidget', 'queryMatchContains', $this->datasourceQueryMatchContains);
        $html .= $this->jsForSimplePropertyConfig('AutoCompleteWidget', 'queryMatchSubset', $this->datasourceQueryMatchSubset);
        // calculate delimiter
        $delimJs = NULL;
        if (is_array($this->delimChar)) {
            $delimJs = '[';
            $first = true;
            foreach ($this->delimChar as $c) {
                if ($first) {
                    $first = false;
                } else {
                    $delimJs .= ', ';
                }
                $delimJs .= "'{$c}'";
            }
            $delimJs .= ']';
        } else {
            if ($this->delimChar) {
                $delimJs = "'{$this->delimChar}'";
            }
        }
        if ($delimJs) {
            $html .= "\nAutoCompleteWidget.delimChar = {$delimJs};\n";
        }
        $html .= $this->jsForSimplePropertyConfig('AutoCompleteWidget', 'maxResultsDisplayed', $this->maxResultsDisplayed);
        $html .= $this->jsForSimplePropertyConfig('AutoCompleteWidget', 'minQueryLength', $this->minQueryLength);
        $html .= $this->jsForSimplePropertyConfig('AutoCompleteWidget', 'queryDelay', $this->queryDelay);
        $html .= $this->jsForSimplePropertyConfig('AutoCompleteWidget', 'autoHighlight', $this->autoHighlight);
        $html .= $this->jsForSimplePropertyConfig('AutoCompleteWidget', 'highlightClassName', $this->highlightClassName);
        $html .= $this->jsForSimplePropertyConfig('AutoCompleteWidget', 'prehighlightClassName', $this->prehighlightClassName);
        $html .= $this->jsForSimplePropertyConfig('AutoCompleteWidget', 'useShadow', $this->useShadow);
        $html .= $this->jsForSimplePropertyConfig('AutoCompleteWidget', 'useIFrame', $this->IFrame);
        $html .= $this->jsForSimplePropertyConfig('AutoCompleteWidget', 'forceSelection', $this->forceSelection);
        $html .= $this->jsForSimplePropertyConfig('AutoCompleteWidget', 'typeAhead', $this->typeAhead);
        $html .= $this->jsForSimplePropertyConfig('AutoCompleteWidget', 'allowBrowserAutocomplete', $this->allowBrowserAutocomplete);
        $html .= $this->jsForSimplePropertyConfig('AutoCompleteWidget', 'alwaysShowContainer', $this->alwaysShowContainer);
        if ($this->enableToggleButton) {
            $html .= <<<END
var bToggler = YAHOO.util.Dom.get("{$this->id}_acToggle");
var oPushButtonB = new YAHOO.widget.Button({container:bToggler});
oPushButtonB.on("click", function(e) {
    if(!YAHOO.util.Dom.hasClass(bToggler, "open")) {
        YAHOO.util.Dom.addClass(bToggler, "open")
    }

    // Is open
    if(AutoCompleteWidget.isContainerOpen()) {
        AutoCompleteWidget.collapseContainer();
    }
    // Is closed
    else {
        AutoCompleteWidget.getInputEl().focus(); // Needed to keep widget active
        setTimeout(function() { // For IE
            AutoCompleteWidget.sendQuery("");
        },0);
    }
});
AutoCompleteWidget.containerCollapseEvent.subscribe(function(){YAHOO.util.Dom.removeClass(bToggler, "open")});
END;
        }
        $html .= "\nPHOCOA.runtime.addObject(AutoCompleteWidget, '{$this->id}');\n";
        $html .= "\n};";
        return $html;
    }