diff --git a/docs/changelog/7.x.x.txt b/docs/changelog/7.x.x.txt index 1e5df3dd8..4d3b02862 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -1,4 +1,8 @@ 7.5.11 + - fix: widget dialogues weren't opening correctly inside a widgetized + view of a widget + - fix: snippets were exporting as index.html or such. pass them through + unchanged. - add: United Knowledge Player added. This player allows you to display a photo collection as a slideshow. A demo can be found in /extras/ukplayer/slideshow. html. (Arjan Widlak, United Knowledge) - add: Assets can now inherit their parent's URL, prepended. Check the meta tab. diff --git a/lib/WebGUI/Asset.pm b/lib/WebGUI/Asset.pm index 2b61f1df2..d5a181278 100644 --- a/lib/WebGUI/Asset.pm +++ b/lib/WebGUI/Asset.pm @@ -2437,7 +2437,6 @@ true. sub isValidRssItem { 1 } #------------------------------------------------------------------- - =head2 www_widgetView ( ) Returns the view() method of the asset object suitable for widgetizing. @@ -2451,7 +2450,9 @@ sub www_widgetView { return $session->privilege->noAccess() unless $self->canView; - my $templateId = $session->form->process('templateId'); + my $templateId = $session->form->process('templateId'); + my $width = $session->form->process('width'); + my $height = $session->form->process('height'); if($templateId eq 'none') { $self->prepareView; @@ -2459,8 +2460,7 @@ sub www_widgetView { else { $self->prepareWidgetView($templateId); } - $self->_outputWidgetJs; - return $self->view; + return $self->outputWidgetMarkup($width, $height, $templateId); } #------------------------------------------------------------------- @@ -2479,22 +2479,117 @@ sub prepareWidgetView { my $template = WebGUI::Asset::Template->new($self->session, $templateId); my $session = $self->session; my $extras = $session->config->get('extrasURL'); - my $yahooDomJs = $extras . '/yui/build/yahoo-dom-event/yahoo-dom-event.js'; - my $widgetJs = $extras . '/widgetLinkTargets.js'; $template->prepare; $self->{_viewTemplate} = $template; } -sub _outputWidgetJs { + +#------------------------------------------------------------------- + +=head2 outputWidgetMarkup ( width, height, templateId ) + +Output the markup required for the widget view. Includes markup to handle the +widget macro in the iframe holding the widgetized asset. This does the following: + +=item retrieves the content for this asset using its L method + +=item processes macros in that content + +=item serializes the processed content in JSON + +=item writes the JSON to a storage location + +=item refers the user to download this JSON + +=item references the appropriate JS files for the templating engine and the widget macro + +=item invokes the templating engine on this JSON + +=head3 width + +The width of the iframe. Required for making widget-in-widget function properly. + +=head3 height + +The height of the iframe. Required for making widget-in-widget function properly. + +=head3 templateId + +The templateId for this widgetized asset to use. Required for making +widget-in-widget function properly. + +=cut + +sub outputWidgetMarkup { + # get our parameters. my $self = shift; + my $width = shift; + my $height = shift; + my $templateId = shift; + + # construct / retrieve the values we'll use later. + my $assetId = $self->getId; my $session = $self->session; - my $extras = $session->config->get('extrasURL'); + my $conf = $session->config; + my $extras = $conf->get('extrasURL'); + + # the widgetized version of content that has the widget macro in it is + # executing in an iframe. this iframe doesn't have a style object. + # therefore, the macro won't be able to output the stylesheet and JS + # information it needs to do its work. because of this, we need to output + # that content manually. construct the filesystem paths for those files. + my $containerCss = $extras . '/yui/build/container/assets/container.css'; + my $containerJs = $extras . '/yui/build/container/container-min.js'; my $yahooDomJs = $extras . '/yui/build/yahoo-dom-event/yahoo-dom-event.js'; - my $widgetJs = $extras . '/widgetLinkTargets.js'; - $session->output->print(""); - $session->output->print(""); + my $wgWidgetJs = $extras . '/wgwidget.js'; + my $ttJs = $extras . '/tt.js'; + + # the templating engine requires its source data to be in json format. + # write this out to disk and then serve the URL to the user. in this case, + # we'll be serializing the content of the asset which is being widgetized. + my $storage = WebGUI::Storage->get($session, $assetId); + my $content = $self->view; + WebGUI::Macro::process($session, \$content); + my $jsonContent = objToJson( { "asset$assetId" => { content => $content } } ); + $storage->addFileFromScalar("$assetId.js", "data = $jsonContent"); + my $jsonUrl = $storage->getUrl("$assetId.js"); + + # WebGUI.widgetBox.initButton() needs the full URL of the asset being + # widgetized, and also the full URL of the JS file that does most of the + # work. + my $fullUrl = "http://" . $conf->get("sitename")->[0] . $self->getUrl; + my $wgWidgetPath = 'http://' . $conf->get('sitename')->[0] . $extras . '/wgwidget.js'; + + # finally, given all of the above, construct our output. WebGUI outputs + # fully valid XHTML 1.0 Strict, and there's no reason this should be any + # different. + my $output = < + + + + + + + + + + + + + \${asset$assetId.content} + + +OUTPUT + return $output; } 1; diff --git a/lib/WebGUI/Asset/Snippet.pm b/lib/WebGUI/Asset/Snippet.pm index 0db704338..5eb27b07a 100644 --- a/lib/WebGUI/Asset/Snippet.pm +++ b/lib/WebGUI/Asset/Snippet.pm @@ -107,6 +107,43 @@ sub definition { } +#------------------------------------------------------------------- +=head2 exportGetUrlAsPath ( index ) + +Translates a URL into an appropriate path and filename for exporting. +Overridden here to return a filename corresponding to the URL of this asset +as-is. + +=head3 index + +index filename passed from L + +=cut + +sub exportGetUrlAsPath { + my $self = shift; + + # we don't use this, but get it anyway + my $index = shift || 'index.html'; + + my $config = $self->session->config; + + # make sure that the export path is valid + WebGUI::Asset->exportCheckPath($self->session); + + # if we're still here, it's valid. get it. + my $exportPath = $config->get('exportPath'); + + # get the asset's URL as a URI::URL object for easy parsing of components + my $url = URI::URL->new($config->get("sitename")->[0] . $self->getUrl); + my @pathComponents = $url->path_components; + shift @pathComponents; # first item is the empty string + my $filename = pop @pathComponents; + + # return a path with the filename part of the URL. No fancy twiddling needed. + return Path::Class::File->new($exportPath, @pathComponents, $filename); +} + #------------------------------------------------------------------- =head2 getToolbar ( ) diff --git a/lib/WebGUI/AssetExportHtml.pm b/lib/WebGUI/AssetExportHtml.pm index c1521a8ac..235cbc769 100644 --- a/lib/WebGUI/AssetExportHtml.pm +++ b/lib/WebGUI/AssetExportHtml.pm @@ -205,6 +205,7 @@ sub exportAsHtml { my $indexFileName = $args->{indexFileName}; my $extrasUploadAction = $args->{extrasUploadAction}; my $rootUrlAction = $args->{rootUrlAction}; + my $exportUrl = $args->{exportUrl}; # if we're doing symlinking of the root URL, then the current default asset # is the root of the tree. take down that asset ID so we can pass it to @@ -282,8 +283,12 @@ sub exportAsHtml { return ($returnCode, $message); } + my $exportedCount = 0; foreach my $assetId ( @{$assetIds} ) { + # set a scratch variable for widgets to know we're exporting + $exportSession->scratch->set('exportUrl', $exportUrl); + my $asset = WebGUI::Asset->newByDynamicClass($exportSession, $assetId); my $fullPath = $asset->exportGetUrlAsPath; @@ -500,6 +505,10 @@ Translates a URL into an appropriate path and filename for exporting. For example, given C<'/foo/bar/baz'>, will return C<'/foo/bar/baz/index.html'> provided the value of indexFile as given to exportAsHtml was C<'index.html'>. +=head3 url + +URL of the asset we need an export path for + =head3 index index filename passed from L @@ -815,6 +824,11 @@ sub www_export { -name => "index", -value => "index.html" ); + $f->text( + -label => 'Export site root URL', + -name => 'exportUrl', + -value => '', + ); # TODO: maybe add copy options to these boxes alongside symlink $f->selectBox( @@ -858,7 +872,7 @@ sub www_exportStatus { return $self->session->privilege->insufficient() unless ($self->session->user->isInGroup(13)); my $i18n = WebGUI::International->new($self->session, "Asset"); my $iframeUrl = $self->getUrl('func=exportGenerate'); - foreach my $formVar (qw/index depth userId extrasUploadsAction rootUrlAction/) { + foreach my $formVar (qw/index depth userId extrasUploadsAction rootUrlAction exportUrl/) { $iframeUrl = $self->session->url->append($iframeUrl, $formVar . '=' . $self->session->form->process($formVar)); } @@ -893,6 +907,7 @@ sub www_exportGenerate { extrasUploadAction => $self->session->form->process('extrasUploadsAction'), rootUrlAction => $self->session->form->process('rootUrlAction'), depth => $self->session->form->process('depth'), + exportUrl => $self->session->form->process('exportUrl'), }); if (!$success) { $self->session->output->print($description, 1); diff --git a/lib/WebGUI/Macro/Widget.pm b/lib/WebGUI/Macro/Widget.pm index 9c59f305b..5f23c857b 100755 --- a/lib/WebGUI/Macro/Widget.pm +++ b/lib/WebGUI/Macro/Widget.pm @@ -1,7 +1,7 @@ package WebGUI::Macro::Widget; #------------------------------------------------------------------- -# WebGUI is Copyright 2001-2008 Plain Black Corporation. +# WebGUI is Copyright 2001-2007 Plain Black Corporation. #------------------------------------------------------------------- # Please read the legal notices (docs/legal.txt) and the license # (docs/license.txt) that came with this distribution before using @@ -16,8 +16,8 @@ use strict; sub process { # get passed parameters - my $session = shift; - my $assetId = shift; + my $session = shift; + my $url = shift; my $width = shift || 600; my $height = shift || 400; my $templateId = shift || 'none'; @@ -27,94 +27,89 @@ sub process { my $extras = $conf->get("extrasURL"); # add CSS and JS to the page - my $style = $session->style; - $style->setLink($extras."/yui/build/container/assets/container.css",{ + my $style = $session->style; + $style->setLink($extras."/yui/build/container/assets/container.css",{ rel=>"stylesheet", type=>"text/css", } ); - $style->setScript($extras."/wgwidget.js",{ - type=>"text/javascript" - } - ); - $style->setScript($extras."/yui/build/yahoo-dom-event/yahoo-dom-event.js",{ - type=>"text/javascript" - } - ); - $style->setScript($extras."/yui/build/container/container-min.js",{ - type=>"text/javascript" - } - ); - - # construct the absolute URL - my $asset = WebGUI::Asset->new($session, $assetId); - my $fullUrl = "http://" . $conf->get("sitename")->[0] . $asset->getUrl; - - # construct path to wgwidget.js - my $wgWidgetPath = 'http://' . $conf->get('sitename')->[0] . $extras . '/wgwidget.js'; - - # and yahoo-dom-event.js - my $yahooDomJsPath = 'http://' . $conf->get('sitename')->[0] . $extras . '/yui/build/yahoo-dom-event/yahoo-dom-event.js'; - my $imgSrc = $extras . '/gear.png'; + # and the JS + $style->setScript($extras."/wgwidget.js",{ + type=>"text/javascript" + } + ); + $style->setScript($extras."/yui/build/yahoo-dom-event/yahoo-dom-event.js",{ + type=>"text/javascript" + } + ); + $style->setScript($extras."/yui/build/container/container-min.js",{ + type=>"text/javascript" + } + ); - my $output = <<"EOHTML"; - + + + + + + $content + + +OUTPUT + $storage->addFileFromScalar("$assetId.html", $content); + } + else { + $fullUrl = "http://" . $conf->get("sitename")->[0] . $asset->getUrl; + $wgWidgetPath = 'http://' . $conf->get('sitename')->[0] . $extras . '/wgwidget.js'; + } - // Render the Dialog - codeGeneratorButton.render(document.body); - - YAHOO.util.Event.addListener("show", "click", handleButtonShow, codeGeneratorButton, true); -} - -var dismissButton = function () { - this.hide(); -} - -YAHOO.util.Event.addListener(window, "load", initButton); + # get the gear icon + my $imgSrc = $extras . '/gear.png'; + my $output = < + - - - - EOHTML - return $output; + return $output; } 1; diff --git a/www/extras/tt.js b/www/extras/tt.js new file mode 100755 index 000000000..ffc40e9f4 --- /dev/null +++ b/www/extras/tt.js @@ -0,0 +1,397 @@ +/** + * TrimPath Template. Release 1.0.38. + * Copyright (C) 2004, 2005 Metaha. + * + * TrimPath Template is licensed under the GNU General Public License + * and the Apache License, Version 2.0, as follows: + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Licensed 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. + */ +var TrimPath; + +// TODO: Debugging mode vs stop-on-error mode - runtime flag. +// TODO: Handle || (or) characters and backslashes. +// TODO: Add more modifiers. + +(function() { // Using a closure to keep global namespace clean. + if (TrimPath == null) + TrimPath = new Object(); + if (TrimPath.evalEx == null) + TrimPath.evalEx = function(src) { return eval(src); }; + + var UNDEFINED; + if (Array.prototype.pop == null) // IE 5.x fix from Igor Poteryaev. + Array.prototype.pop = function() { + if (this.length === 0) {return UNDEFINED;} + return this[--this.length]; + }; + if (Array.prototype.push == null) // IE 5.x fix from Igor Poteryaev. + Array.prototype.push = function() { + for (var i = 0; i < arguments.length; ++i) {this[this.length] = arguments[i];} + return this.length; + }; + + TrimPath.parseTemplate = function(tmplContent, optTmplName, optEtc) { + if (optEtc == null) + optEtc = TrimPath.parseTemplate_etc; + var funcSrc = parse(tmplContent, optTmplName, optEtc); + var func = TrimPath.evalEx(funcSrc, optTmplName, 1); + if (func != null) + return new optEtc.Template(optTmplName, tmplContent, funcSrc, func, optEtc); + return null; + } + + try { + String.prototype.process = function(context, optFlags) { + var template = TrimPath.parseTemplate(this, null); + if (template != null) + return template.process(context, optFlags); + return this; + } + } catch (e) { // Swallow exception, such as when String.prototype is sealed. + } + + TrimPath.parseTemplate_etc = {}; // Exposed for extensibility. + TrimPath.parseTemplate_etc.statementTag = "forelse|for|if|elseif|else|var|macro"; + TrimPath.parseTemplate_etc.statementDef = { // Lookup table for statement tags. + "if" : { delta: 1, prefix: "if (", suffix: ") {", paramMin: 1 }, + "else" : { delta: 0, prefix: "} else {" }, + "elseif" : { delta: 0, prefix: "} else if (", suffix: ") {", paramDefault: "true" }, + "/if" : { delta: -1, prefix: "}" }, + "for" : { delta: 1, paramMin: 3, + prefixFunc : function(stmtParts, state, tmplName, etc) { + if (stmtParts[2] != "in") + throw new etc.ParseError(tmplName, state.line, "bad for loop statement: " + stmtParts.join(' ')); + var iterVar = stmtParts[1]; + var listVar = "__LIST__" + iterVar; + return [ "var ", listVar, " = ", stmtParts[3], ";", + // Fix from Ross Shaull for hash looping, make sure that we have an array of loop lengths to treat like a stack. + "var __LENGTH_STACK__;", + "if (typeof(__LENGTH_STACK__) == 'undefined' || !__LENGTH_STACK__.length) __LENGTH_STACK__ = new Array();", + "__LENGTH_STACK__[__LENGTH_STACK__.length] = 0;", // Push a new for-loop onto the stack of loop lengths. + "if ((", listVar, ") != null) { ", + "var ", iterVar, "_ct = 0;", // iterVar_ct variable, added by B. Bittman + "for (var ", iterVar, "_index in ", listVar, ") { ", + iterVar, "_ct++;", + "if (typeof(", listVar, "[", iterVar, "_index]) == 'function') {continue;}", // IE 5.x fix from Igor Poteryaev. + "__LENGTH_STACK__[__LENGTH_STACK__.length - 1]++;", + "var ", iterVar, " = ", listVar, "[", iterVar, "_index];" ].join(""); + } }, + "forelse" : { delta: 0, prefix: "} } if (__LENGTH_STACK__[__LENGTH_STACK__.length - 1] == 0) { if (", suffix: ") {", paramDefault: "true" }, + "/for" : { delta: -1, prefix: "} }; delete __LENGTH_STACK__[__LENGTH_STACK__.length - 1];" }, // Remove the just-finished for-loop from the stack of loop lengths. + "var" : { delta: 0, prefix: "var ", suffix: ";" }, + "macro" : { delta: 1, + prefixFunc : function(stmtParts, state, tmplName, etc) { + var macroName = stmtParts[1].split('(')[0]; + return [ "var ", macroName, " = function", + stmtParts.slice(1).join(' ').substring(macroName.length), + "{ var _OUT_arr = []; var _OUT = { write: function(m) { if (m) _OUT_arr.push(m); } }; " ].join(''); + } }, + "/macro" : { delta: -1, prefix: " return _OUT_arr.join(''); };" } + } + TrimPath.parseTemplate_etc.modifierDef = { + "eat" : function(v) { return ""; }, + "escape" : function(s) { return String(s).replace(/&/g, "&").replace(//g, ">"); }, + "capitalize" : function(s) { return String(s).toUpperCase(); }, + "default" : function(s, d) { return s != null ? s : d; } + } + TrimPath.parseTemplate_etc.modifierDef.h = TrimPath.parseTemplate_etc.modifierDef.escape; + + TrimPath.parseTemplate_etc.Template = function(tmplName, tmplContent, funcSrc, func, etc) { + this.process = function(context, flags) { + if (context == null) + context = {}; + if (context._MODIFIERS == null) + context._MODIFIERS = {}; + if (context.defined == null) + context.defined = function(str) { return (context[str] != undefined); }; + for (var k in etc.modifierDef) { + if (context._MODIFIERS[k] == null) + context._MODIFIERS[k] = etc.modifierDef[k]; + } + if (flags == null) + flags = {}; + var resultArr = []; + var resultOut = { write: function(m) { resultArr.push(m); } }; + try { + func(resultOut, context, flags); + } catch (e) { + if (flags.throwExceptions == true) + throw e; + var result = new String(resultArr.join("") + "[ERROR: " + e.toString() + (e.message ? '; ' + e.message : '') + "]"); + result["exception"] = e; + return result; + } + return resultArr.join(""); + } + this.name = tmplName; + this.source = tmplContent; + this.sourceFunc = funcSrc; + this.toString = function() { return "TrimPath.Template [" + tmplName + "]"; } + } + TrimPath.parseTemplate_etc.ParseError = function(name, line, message) { + this.name = name; + this.line = line; + this.message = message; + } + TrimPath.parseTemplate_etc.ParseError.prototype.toString = function() { + return ("TrimPath template ParseError in " + this.name + ": line " + this.line + ", " + this.message); + } + + var parse = function(body, tmplName, etc) { + body = cleanWhiteSpace(body); + var funcText = [ "var TrimPath_Template_TEMP = function(_OUT, _CONTEXT, _FLAGS) { with (_CONTEXT) {" ]; + var state = { stack: [], line: 1 }; // TODO: Fix line number counting. + var endStmtPrev = -1; + while (endStmtPrev + 1 < body.length) { + var begStmt = endStmtPrev; + // Scan until we find some statement markup. + begStmt = body.indexOf("{", begStmt + 1); + while (begStmt >= 0) { + var endStmt = body.indexOf('}', begStmt + 1); + var stmt = body.substring(begStmt, endStmt); + var blockrx = stmt.match(/^\{(cdata|minify|eval)/); // From B. Bittman, minify/eval/cdata implementation. + if (blockrx) { + var blockType = blockrx[1]; + var blockMarkerBeg = begStmt + blockType.length + 1; + var blockMarkerEnd = body.indexOf('}', blockMarkerBeg); + if (blockMarkerEnd >= 0) { + var blockMarker; + if( blockMarkerEnd - blockMarkerBeg <= 0 ) { + blockMarker = "{/" + blockType + "}"; + } else { + blockMarker = body.substring(blockMarkerBeg + 1, blockMarkerEnd); + } + + var blockEnd = body.indexOf(blockMarker, blockMarkerEnd + 1); + if (blockEnd >= 0) { + emitSectionText(body.substring(endStmtPrev + 1, begStmt), funcText); + + var blockText = body.substring(blockMarkerEnd + 1, blockEnd); + if (blockType == 'cdata') { + emitText(blockText, funcText); + } else if (blockType == 'minify') { + emitText(scrubWhiteSpace(blockText), funcText); + } else if (blockType == 'eval') { + if (blockText != null && blockText.length > 0) // From B. Bittman, eval should not execute until process(). + funcText.push('_OUT.write( (function() { ' + blockText + ' })() );'); + } + begStmt = endStmtPrev = blockEnd + blockMarker.length - 1; + } + } + } else if (body.charAt(begStmt - 1) != '$' && // Not an expression or backslashed, + body.charAt(begStmt - 1) != '\\') { // so check if it is a statement tag. + var offset = (body.charAt(begStmt + 1) == '/' ? 2 : 1); // Close tags offset of 2 skips '/'. + // 10 is larger than maximum statement tag length. + if (body.substring(begStmt + offset, begStmt + 10 + offset).search(TrimPath.parseTemplate_etc.statementTag) == 0) + break; // Found a match. + } + begStmt = body.indexOf("{", begStmt + 1); + } + if (begStmt < 0) // In "a{for}c", begStmt will be 1. + break; + var endStmt = body.indexOf("}", begStmt + 1); // In "a{for}c", endStmt will be 5. + if (endStmt < 0) + break; + emitSectionText(body.substring(endStmtPrev + 1, begStmt), funcText); + emitStatement(body.substring(begStmt, endStmt + 1), state, funcText, tmplName, etc); + endStmtPrev = endStmt; + } + emitSectionText(body.substring(endStmtPrev + 1), funcText); + if (state.stack.length != 0) + throw new etc.ParseError(tmplName, state.line, "unclosed, unmatched statement(s): " + state.stack.join(",")); + funcText.push("}}; TrimPath_Template_TEMP"); + return funcText.join(""); + } + + var emitStatement = function(stmtStr, state, funcText, tmplName, etc) { + var parts = stmtStr.slice(1, -1).split(' '); + var stmt = etc.statementDef[parts[0]]; // Here, parts[0] == for/if/else/... + if (stmt == null) { // Not a real statement. + emitSectionText(stmtStr, funcText); + return; + } + if (stmt.delta < 0) { + if (state.stack.length <= 0) + throw new etc.ParseError(tmplName, state.line, "close tag does not match any previous statement: " + stmtStr); + state.stack.pop(); + } + if (stmt.delta > 0) + state.stack.push(stmtStr); + + if (stmt.paramMin != null && + stmt.paramMin >= parts.length) + throw new etc.ParseError(tmplName, state.line, "statement needs more parameters: " + stmtStr); + if (stmt.prefixFunc != null) + funcText.push(stmt.prefixFunc(parts, state, tmplName, etc)); + else + funcText.push(stmt.prefix); + if (stmt.suffix != null) { + if (parts.length <= 1) { + if (stmt.paramDefault != null) + funcText.push(stmt.paramDefault); + } else { + for (var i = 1; i < parts.length; i++) { + if (i > 1) + funcText.push(' '); + funcText.push(parts[i]); + } + } + funcText.push(stmt.suffix); + } + } + + var emitSectionText = function(text, funcText) { + if (text.length <= 0) + return; + var nlPrefix = 0; // Index to first non-newline in prefix. + var nlSuffix = text.length - 1; // Index to first non-space/tab in suffix. + while (nlPrefix < text.length && (text.charAt(nlPrefix) == '\n')) + nlPrefix++; + while (nlSuffix >= 0 && (text.charAt(nlSuffix) == ' ' || text.charAt(nlSuffix) == '\t')) + nlSuffix--; + if (nlSuffix < nlPrefix) + nlSuffix = nlPrefix; + if (nlPrefix > 0) { + funcText.push('if (_FLAGS.keepWhitespace == true) _OUT.write("'); + var s = text.substring(0, nlPrefix).replace('\n', '\\n'); // A macro IE fix from BJessen. + if (s.charAt(s.length - 1) == '\n') + s = s.substring(0, s.length - 1); + funcText.push(s); + funcText.push('");'); + } + var lines = text.substring(nlPrefix, nlSuffix + 1).split('\n'); + for (var i = 0; i < lines.length; i++) { + emitSectionTextLine(lines[i], funcText); + if (i < lines.length - 1) + funcText.push('_OUT.write("\\n");\n'); + } + if (nlSuffix + 1 < text.length) { + funcText.push('if (_FLAGS.keepWhitespace == true) _OUT.write("'); + var s = text.substring(nlSuffix + 1).replace('\n', '\\n'); + if (s.charAt(s.length - 1) == '\n') + s = s.substring(0, s.length - 1); + funcText.push(s); + funcText.push('");'); + } + } + + var emitSectionTextLine = function(line, funcText) { + var endMarkPrev = '}'; + var endExprPrev = -1; + while (endExprPrev + endMarkPrev.length < line.length) { + var begMark = "${", endMark = "}"; + var begExpr = line.indexOf(begMark, endExprPrev + endMarkPrev.length); // In "a${b}c", begExpr == 1 + if (begExpr < 0) + break; + if (line.charAt(begExpr + 2) == '%') { + begMark = "${%"; + endMark = "%}"; + } + var endExpr = line.indexOf(endMark, begExpr + begMark.length); // In "a${b}c", endExpr == 4; + if (endExpr < 0) + break; + emitText(line.substring(endExprPrev + endMarkPrev.length, begExpr), funcText); + // Example: exprs == 'firstName|default:"John Doe"|capitalize'.split('|') + var exprArr = line.substring(begExpr + begMark.length, endExpr).replace(/\|\|/g, "#@@#").split('|'); + for (var k in exprArr) { + if (exprArr[k].replace) // IE 5.x fix from Igor Poteryaev. + exprArr[k] = exprArr[k].replace(/#@@#/g, '||'); + } + funcText.push('_OUT.write('); + emitExpression(exprArr, exprArr.length - 1, funcText); + funcText.push(');'); + endExprPrev = endExpr; + endMarkPrev = endMark; + } + emitText(line.substring(endExprPrev + endMarkPrev.length), funcText); + } + + var emitText = function(text, funcText) { + if (text == null || + text.length <= 0) + return; + text = text.replace(/\\/g, '\\\\'); + text = text.replace(/\n/g, '\\n'); + text = text.replace(/"/g, '\\"'); + funcText.push('_OUT.write("'); + funcText.push(text); + funcText.push('");'); + } + + var emitExpression = function(exprArr, index, funcText) { + // Ex: foo|a:x|b:y1,y2|c:z1,z2 is emitted as c(b(a(foo,x),y1,y2),z1,z2) + var expr = exprArr[index]; // Ex: exprArr == [firstName,capitalize,default:"John Doe"] + if (index <= 0) { // Ex: expr == 'default:"John Doe"' + funcText.push(expr); + return; + } + var parts = expr.split(':'); + funcText.push('_MODIFIERS["'); + funcText.push(parts[0]); // The parts[0] is a modifier function name, like capitalize. + funcText.push('"]('); + emitExpression(exprArr, index - 1, funcText); + if (parts.length > 1) { + funcText.push(','); + funcText.push(parts[1]); + } + funcText.push(')'); + } + + var cleanWhiteSpace = function(result) { + result = result.replace(/\t/g, " "); + result = result.replace(/\r\n/g, "\n"); + result = result.replace(/\r/g, "\n"); + result = result.replace(/^(\s*\S*(\s+\S+)*)\s*$/, '$1'); // Right trim by Igor Poteryaev. + return result; + } + + var scrubWhiteSpace = function(result) { + result = result.replace(/^\s+/g, ""); + result = result.replace(/\s+$/g, ""); + result = result.replace(/\s+/g, " "); + result = result.replace(/^(\s*\S*(\s+\S+)*)\s*$/, '$1'); // Right trim by Igor Poteryaev. + return result; + } + + // The DOM helper functions depend on DOM/DHTML, so they only work in a browser. + // However, these are not considered core to the engine. + // + TrimPath.parseDOMTemplate = function(elementId, optDocument, optEtc) { + if (optDocument == null) + optDocument = document; + var element = optDocument.getElementById(elementId); + var content = element.value; // Like textarea.value. + if (content == null) + content = element.innerHTML; // Like textarea.innerHTML. + content = content.replace(/</g, "<").replace(/>/g, ">"); + return TrimPath.parseTemplate(content, elementId, optEtc); + } + + TrimPath.processDOMTemplate = function(elementId, context, optFlags, optDocument, optEtc) { + return TrimPath.parseDOMTemplate(elementId, optDocument, optEtc).process(context, optFlags); + } +}) (); diff --git a/www/extras/wgwidget.js b/www/extras/wgwidget.js index ff4ee4ab6..6bd95d3b9 100755 --- a/www/extras/wgwidget.js +++ b/www/extras/wgwidget.js @@ -1,40 +1,148 @@ -var WebGUI = { +if (typeof WebGUI == 'undefined') { + WebGUI = {}; +} + +if(typeof WebGUI.widgetBox == 'undefined') { + WebGUI.widgetBox = {}; +} + +WebGUI.widgetBox = { + + parentNodeId : null, + url : null, - widgetBox : { + // this function courtesy of http://www-128.ibm.com/developerworks/web/library/wa-ie2mozgd/ + // lots of reformatting to be easier to read - parentNodeId : null, - url : null, - - - widget : function( url, parentId, width, height, templateId ) { - if(url == "") { - return ""; - } + getWidgetFrame : function( frameId ) { + var widgetFrame = null; - if(width == undefined) { - width = 600; - } - if(height == undefined) { - height = 400; - } - - this.url = url + "?func=widgetView&templateId=" + templateId; - - this.parentNodeId = parentId; - - this.markup = ""; - this.markup += ""; - - return this.markup; + // standards compliant, i.e. gecko et al. + if (document.getElementById(frameId).contentDocument) { + widgetFrame = document.getElementById(frameId).contentDocument; } + // not compliant, i.e. IE + else { + //widgetFrame = document.frames[frameId].document; + widgetFrame = document.getElementById(frameId).document; + } + + return widgetFrame; + }, + + widget : function( url, parentId, width, height, templateId ) { + if(url == "") { + return ""; + } + + if(width == undefined) { + width = 600; + } + if(height == undefined) { + height = 400; + } + + if(templateId == undefined) { + this.url = url + "?func=ajaxInlineView"; + } + else { + this.url = url + "?func=widgetView&templateId=" + templateId; + this.url += ";width=" + width; + this.url += ";height=" + height; + } + + this.parentNodeId = parentId; + + this.markup = ""; + this.markup += ""; + + return this.markup; + }, + + retargetLinksAndForms : function() { + + // get all the elements, change their target appropriately + var allLinks = document.getElementsByTagName('a'); + for(var i = 0; i < allLinks.length; i++) { + // skip the gear links for widgets + if( allLinks[i].href.search(/#/) != -1 && allLinks[i].name.search(/^show/) != -1 ) { + continue; + } + else { + allLinks[i].target = '_blank'; + } + } + + // same for
s + var allForms = document.getElementsByTagName('form'); + for(var i = 0; i < allForms.length; i++) { + allForms[i].target = '_blank'; + } + }, + + doTemplate : function(elementId) { + document.body.innerHTML = TrimPath.processDOMTemplate(elementId, data); + }, + + initButton : function(first, second) { + + // for some unknown reason (God do I love JS), either the first or + // second argument to initButton may have the params we sent it. + // hurray. work around this by checking to see which object holds our + // information and then setting a known name to hold our values. + + var params; + if(!first.fullUrl) { + params = second; + } + else { + params = first; + } + + var jsCode = ""; + jsCode += "<script type='text/javascript' src='" + params.wgWidgetPath + "'> </script>"; + jsCode += "<script type='text/javascript'>"; + jsCode += "document.write(WebGUI.widgetBox.widget('" + params.fullUrl + "', '" + params.assetId + "', " + params.width + ", " + params.height + ", '" + params.templateId + "')); </script>"; + + // Instantiate the Dialog + codeGeneratorButton = new YAHOO.widget.SimpleDialog("codeGeneratorButton", { + width: "500px", + height: "200px", + fixedcenter: true, + visible: false, + draggable: true, + close: true, + text: "", + icon: YAHOO.widget.SimpleDialog.ICON_INFO, + constraintoviewport: true, + modal: true, + zIndex: 9999, + buttons: [{text: "Dismiss", handler:WebGUI.widgetBox.dismissButton, isDefault: true}] + } + ); + codeGeneratorButton.setHeader("Widget code"); + + // Render the Dialog + codeGeneratorButton.render(document.body); + + YAHOO.util.Event.addListener("show" + params.assetId, "click", WebGUI.widgetBox.handleButtonShow, codeGeneratorButton, true); + }, + handleButtonShow : function() { + codeGeneratorButton.show(); + var tag = document.getElementById('jsWidgetCode'); + tag.focus(); + tag.select(); + }, + dismissButton : function () { + this.hide(); } } diff --git a/www/extras/widgetLinkTargets.js b/www/extras/widgetLinkTargets.js deleted file mode 100755 index 52593eec7..000000000 --- a/www/extras/widgetLinkTargets.js +++ /dev/null @@ -1,19 +0,0 @@ -var WebGUI = { - widgetBox : { - function retargetLinksAndForms() { - - // get all the elements, change their target appropriately - var allLinks = document.getElementsByTagName('a'); - for(var i = 0; i < allLinks.length; i++) { - allLinks[i].target = '_blank'; - } - - // same for s - var allForms = document.getElementsByTagName('form'); - for(var i = 0; i < allForms.length; i++) { - allForms[i].target = '_blank'; - } - } - } -}; -YAHOO.util.Event.addListener(window, "load", WebGUI.widget.retargetLinksAndForms);