diff --git a/docs/upgrades/upgrade_7.7.5-7.7.6.pl b/docs/upgrades/upgrade_7.7.5-7.7.6.pl
index ed32524f8..9d49e3244 100644
--- a/docs/upgrades/upgrade_7.7.5-7.7.6.pl
+++ b/docs/upgrades/upgrade_7.7.5-7.7.6.pl
@@ -31,6 +31,7 @@ my $quiet;
my $session = start();
# upgrade functions go here
+addTemplateAttachmentsTable($session);
revertUsePacked( $session );
finish($session);
@@ -45,6 +46,22 @@ finish($session);
# print "DONE!\n" unless $quiet;
#}
+sub addTemplateAttachmentsTable {
+ my $session = shift;
+ my $create = q{
+ create table template_attachments (
+ templateId varchar(22),
+ revisionDate bigint(20),
+ url varchar(256),
+ type varchar(20),
+ sequence int(11),
+
+ primary key (templateId, revisionDate, url)
+ )
+ };
+ $session->db->write($create);
+}
+
#----------------------------------------------------------------------------
# Rollback usePacked. It should be carefully applied manually for now
sub revertUsePacked {
diff --git a/lib/WebGUI/Asset/Template.pm b/lib/WebGUI/Asset/Template.pm
index 972740542..9f2d7ba42 100644
--- a/lib/WebGUI/Asset/Template.pm
+++ b/lib/WebGUI/Asset/Template.pm
@@ -19,9 +19,17 @@ use base 'WebGUI::Asset';
use WebGUI::International;
use WebGUI::Asset::Template::HTMLTemplate;
use WebGUI::Utility;
+use WebGUI::Form;
+use Tie::IxHash;
use Clone qw/clone/;
use HTML::Packer;
+tie my %attachmentTypeNames, 'Tie::IxHash' => (
+ stylesheet => 'CSS Stylesheet',
+ headScript => 'Javascript (head)',
+ bodyScript => 'Javascript (body)',
+);
+
=head1 NAME
Package WebGUI::Asset::Template
@@ -43,6 +51,52 @@ These methods are available from this class:
#-------------------------------------------------------------------
+=head2 addAttachments ( attachments )
+
+Adds attachments to this template. Attachments is an arrayref of hashrefs,
+where each hashref should have at least url, type, and sequence as keys.
+
+=cut
+
+sub addAttachments {
+ my ($self, $attachments) = @_;
+ my $db = $self->session->db;
+
+ my $sql = q{
+ INSERT INTO template_attachments
+ (templateId, revisionDate, url, type, sequence)
+ VALUES
+ (?, ?, ?, ?, ?)
+ };
+
+ foreach my $a (@$attachments) {
+ my @params = (
+ $self->getId,
+ $self->get('revisionDate'),
+ @{$a}{qw(url type sequence)}
+ );
+ $db->write($sql, \@params);
+ }
+}
+
+#-------------------------------------------------------------------
+
+=head2 addRevision ( )
+
+Override the master addRevision to copy attachments
+
+=cut
+
+sub addRevision {
+ my $self = shift;
+ my $object = $self->SUPER::addRevision(@_);
+ $object->addAttachments($self->getAttachments);
+
+ return $object;
+}
+
+#-------------------------------------------------------------------
+
=head2 definition ( session, definition )
Defines the properties of this asset.
@@ -146,6 +200,39 @@ sub duplicate {
#-------------------------------------------------------------------
+=head2 removeAttachments ( urls )
+
+Removes the specified attachments (specified by their urls, NOT by hashrefs,
+since urls are unique to a particular revision) from this template. urls
+should be an arrayref of strings.
+
+=cut
+
+sub removeAttachments {
+ my ($self, $urls) = @_;
+
+ return unless @$urls;
+
+ my $db = $self->session->db;
+ my $dbh = $db->dbh;
+ my $in = join(',', map { $dbh->quote($_) } @$urls);
+ my $rmsql = qq{
+ DELETE FROM
+ template_attachments
+ WHERE
+ templateId = ?
+ AND revisionDate = ?
+ AND url IN ($in)
+ };
+ my @params = (
+ $self->getId,
+ $self->get('revisionDate'),
+ );
+ $db->write($rmsql, \@params);
+}
+
+#-------------------------------------------------------------------
+
=head2 packTemplate ( template )
Pack the template into a minified version for faster downloads.
@@ -185,11 +272,76 @@ sub processPropertiesFromFormPost {
$needsUpdate = 1;
$data{extraHeadTags} = '';
}
+
+ my $f = $self->session->form;
+ my $p = $f->paramsHashRef;
+ my @nums = grep {$_} map { my ($i) = /^attachmentUrl(\d+)$/; $i } keys %$p;
+ my @add;
+
+ foreach my $n (@nums) {
+ my ($index, $type, $url) =
+ map { $f->process('attachment' . $_ . $n) }
+ qw(Index Type Url);
+
+ push(
+ @add, {
+ sequence => $index,
+ url => $url,
+ type => $type,
+ }
+ );
+ }
+
+ my @remove = $f->process('removeAttachment', 'list');
+ $data{removeAttachments} = \@remove;
+ $data{attachments} = \@add;
+
+ $needsUpdate = 1 if @remove || @add;
+
$self->update(\%data) if $needsUpdate;
}
#-------------------------------------------------------------------
+=head2 getAttachments ( [type] )
+
+Returns an arrayref of hashrefs representing all attachments for this template
+of the specified type (link, bodyScript, headScript).
+
+=head3 type
+
+If defined, will limit the attachments to this type; e.g., passing
+'stylesheet' will return only stylesheets.
+
+=cut
+
+sub getAttachments {
+ my ( $self, $type ) = @_;
+ my @params = ($self->getId, $self->get('revisionDate'));
+ my $typeString;
+
+ if ($type) {
+ $typeString = 'AND type = ?';
+ push(@params, $type);
+ }
+
+ my $sql = qq{
+ SELECT
+ *
+ FROM
+ template_attachments
+ WHERE
+ templateId = ?
+ AND revisionDate = ?
+ $typeString
+ ORDER BY
+ type, sequence ASC
+ };
+ return $self->session->db->buildArrayRefOfHashRefs($sql, \@params);
+}
+
+#-------------------------------------------------------------------
+
=head2 getEditForm ( )
Returns the TabForm object that will be used in generating the edit page for this asset.
@@ -230,11 +382,11 @@ sub getEditForm {
-label=>$i18n->get('show in forms'),
-hoverHelp=>$i18n->get('show in forms description'),
);
- $tabform->getTab("properties")->codearea(
+ $tabform->getTab("properties")->codearea(
-name=>"template",
-label=>$i18n->get('assetName'),
-hoverHelp=>$i18n->get('template description'),
- -syntax => "html",
+ -syntax => "html",
-value=>$self->getValue("template")
);
$tabform->getTab('properties')->yesNo(
@@ -257,8 +409,67 @@ sub getEditForm {
-value=>$value,
-label=>$i18n->get('parser'),
-hoverHelp=>$i18n->get('parser description'),
- );
+ );
}
+
+ my $session = $self->session;
+ my @headers = map { '
' . $i18n->get("attachment header $_") . ' | ' }
+ qw(index type url remove);
+
+ my $table = '';
+ $table .= "@headers
";
+ foreach my $a ( @{ $self->getAttachments } ) {
+ my ($seq, $type, $url) = @{$a}{qw(sequence type url)};
+ # escape macros in the url so they don't get processed
+ $url =~ s/\^/^/g;
+ my $del = WebGUI::Form::checkbox(
+ $session, {
+ name => 'removeAttachment',
+ value => $url,
+ extras => 'class="id"',
+ }
+ );
+ my @data = (
+ "$seq | ",
+ "$type | ",
+ "$url | ",
+ "$del | ",
+ );
+ $table .= "@data
";
+ }
+ $table .= '
';
+ my $properties = $tabform->getTab('properties');
+ my $label = $i18n->get('attachment display label');
+ $properties->raw("| $label | $table |
");
+
+ my @data = map { "$_ | " } (
+ WebGUI::Form::integer(
+ $session, { size => '2', id => 'addBoxIndex' }
+ ),
+ WebGUI::Form::selectBox(
+ $session, { options => \%attachmentTypeNames, id => 'addBoxType' }
+ ),
+ WebGUI::Form::text($session, { id => 'addBoxUrl', size => 40 }),
+ WebGUI::Form::button(
+ $session, {
+ value => $i18n->get('attachment add button'),
+ extras => 'onclick="addClick()"'
+ }
+ ),
+ );
+
+ my ($style, $url) = $self->session->quick(qw(style url));
+ $style->setScript($url->extras('yui/build/yahoo/yahoo-min.js'));
+ $style->setScript($url->extras('yui/build/json/json-min.js'));
+ $style->setScript($url->extras('yui/build/dom/dom-min.js'));
+
+ pop(@headers);
+ my $scriptUrl = $url->extras('templateAttachments.js');
+ $table = "";
+ $table .= qq();
+ $label = $i18n->get('attachment add field label');
+ $properties->raw("| $label | $table |
");
+
return $tabform;
}
@@ -370,15 +581,42 @@ A hash reference containing template variables to be processed for the head bloc
sub prepare {
my $self = shift;
- my $vars = shift;
+ my $vars = shift;
$self->{_prepared} = 1;
- my $templateHeadersSent = $self->session->stow->get("templateHeadersSent") || [];
- my @sent = @{$templateHeadersSent};
- unless (isIn($self->getId, @sent)) { # don't send head block if we've already sent it for this template
- $self->session->style->setRawHeadTags($self->getParser($self->session, $self->get('parser'))->process($self->getExtraHeadTags, $vars));
- }
- push(@sent, $self->getId);
- $self->session->stow->set("templateHeadersSent", \@sent);
+
+ my $sent = $self->session->stow->get('templateHeadersSent');
+ unless ($sent) {
+ $self->session->stow->set('templateHeadersSent', $sent = []);
+ }
+
+ my $id = $self->getId;
+ # don't send head block if we've already sent it for this template
+ return if isIn($id, @$sent);
+
+ my $session = $self->session;
+ my ($db, $style) = $session->quick(qw(db style));
+ my $parser = $self->getParser($session, $self->get('parser'));
+ my $headBlock = $parser->process($self->getExtraHeadTags, $vars);
+
+ $style->setRawHeadTags($headBlock);
+
+ foreach my $sheet ( @{ $self->getAttachments('stylesheet') } ) {
+ my %props = ( type => 'text/css', rel => 'stylesheet' );
+ $style->setLink($sheet->{url}, \%props);
+ }
+
+ my $doScripts = sub {
+ my ($type, $body) = @_;
+ foreach my $script ( @{ $self->getAttachments($type) } ) {
+ my %props = ( type => 'text/javascript' );
+ $style->setScript($script->{url}, \%props, $body);
+ }
+ };
+
+ $doScripts->('headScript');
+ $doScripts->('bodyScript', 1);
+
+ push(@$sent, $id);
}
@@ -445,6 +683,27 @@ sub processRaw {
return $class->getParser($session,$parser)->process($template, $vars);
}
+#-------------------------------------------------------------------
+
+=head2 purgeRevision ( )
+
+Override the master purgeRevision to purge attachments
+
+=cut
+
+sub purgeRevision {
+ my $self = shift;
+ my $db = $self->session->db;
+ my $sql = q{
+ DELETE FROM
+ template_attachments
+ WHERE
+ templateId = ?
+ AND revisionDate = ?
+ };
+ $db->write($sql, [$self->getId, $self->get('revisionDate')]);
+ return $self->SUPER::purgeRevision(@_);
+}
#-------------------------------------------------------------------
@@ -456,16 +715,27 @@ packages that contain headBlocks.
This method is deprecated and will be removed in the future. Don't plan
on this being here.
+Note, we are also using this now to update template attachments. So maybe it
+will stay.
+
=cut
sub update {
my $self = shift;
my $requestedProperties = shift;
my $properties = clone($requestedProperties);
+
+ my $attachments = delete $properties->{attachments} || [];
+ my $removeAttachments = delete $properties->{removeAttachments} || [];
+
+ $self->removeAttachments($removeAttachments);
+ $self->addAttachments($attachments);
+
if (exists $properties->{headBlock}) {
$properties->{extraHeadTags} .= $properties->{headBlock};
delete $properties->{headBlock};
}
+
$self->SUPER::update($properties);
}
diff --git a/lib/WebGUI/Session/Style.pm b/lib/WebGUI/Session/Style.pm
index 54b1bd3b5..e5d021bf4 100644
--- a/lib/WebGUI/Session/Style.pm
+++ b/lib/WebGUI/Session/Style.pm
@@ -67,6 +67,30 @@ sub DESTROY {
undef $self;
}
+#-------------------------------------------------------------------
+
+sub _generateAdditionalTags {
+ my $var = shift;
+ return sub {
+ my $self = shift;
+ my $tags = $self->{$var};
+ delete $self->{$var};
+ WebGUI::Macro::process($self->session,\$tags);
+ return $tags;
+ };
+}
+
+#-------------------------------------------------------------------
+
+=head2 generateAdditionalBodyTags ( )
+
+Creates tags that were set using setScript (if inBody was true) and setRawBodyTags.
+Macros are processed in the tags if processed by this method.
+
+=cut
+
+BEGIN { *generateAdditionalBodyTags = _generateAdditionalTags('_rawBody') }
+
#-------------------------------------------------------------------
@@ -77,15 +101,7 @@ Macros are processed in the tags if processed by this method.
=cut
-sub generateAdditionalHeadTags {
- my $self = shift;
- my $tags = $self->{_raw};
- delete $self->{_raw};
- WebGUI::Macro::process($self->session,\$tags);
- return $tags;
-}
-
-
+BEGIN { *generateAdditionalHeadTags = _generateAdditionalTags('_raw') }
#-------------------------------------------------------------------
@@ -196,10 +212,16 @@ if ($self->session->user->isRegistered || $self->session->setting->get("preventP
} else {
$var{'head.tags'} .= ''
}
+
# Removing the newlines will probably annoy people.
# Perhaps turn it off under debug mode?
$var{'head.tags'} =~ s/\n//g;
+ # head.tags = head_attachments . body_attachments
+ # keeping head.tags for backwards compatibility
+ $var{'head_attachments'} = $var{'head.tags'};
+ $var{'head.tags'} .= ($var{'body_attachments'} = '');
+
my $style = WebGUI::Asset::Template->new($self->session,$templateId);
my $output;
if (defined $style) {
@@ -216,8 +238,10 @@ if ($self->session->user->isRegistered || $self->session->setting->get("preventP
}
WebGUI::Macro::process($self->session,\$output);
$self->sent(1);
- my $macroHeadTags = $self->generateAdditionalHeadTags();
- $output =~ s/\<\!--morehead--\>/$macroHeadTags/;
+ my $macroHeadTags = $self->generateAdditionalHeadTags();
+ my $macroBodyTags = $self->generateAdditionalBodyTags();
+ $output =~ s/\<\!--morehead--\>/$macroHeadTags/;
+ $output =~ s/\<\!--morebody--\>/$macroBodyTags/;
return $output;
}
@@ -339,7 +363,33 @@ sub setMeta {
$self->setRawHeadTags($tag);
}
+#-------------------------------------------------------------------
+sub _setRawTags {
+ my $var = shift;
+ return sub {
+ my $self = shift;
+ my $tags = shift;
+ if ($self->sent) {
+ $self->session->output->print($tags);
+ }
+ else {
+ $self->{$var} .= $tags;
+ }
+ };
+}
+
+#-------------------------------------------------------------------
+
+=head2 setRawBodyTags ( tags )
+
+Does exactly the same thing as setRawHeadTags, except that the tags will be
+appended to a seperate variable (to be output after the body if the style
+template supports it) instead.
+
+=cut
+
+BEGIN { *setRawBodyTags = _setRawTags('_rawBody') }
#-------------------------------------------------------------------
@@ -357,21 +407,11 @@ A raw string containing tags. This is just a raw string so you must actually pas
=cut
-sub setRawHeadTags {
- my $self = shift;
- my $tags = shift;
- if ($self->sent) {
- $self->session->output->print($tags);
- }
- else {
- $self->{_raw} .= $tags;
- }
-}
-
+BEGIN { *setRawHeadTags = _setRawTags('_raw') }
#-------------------------------------------------------------------
-=head2 setScript ( url, params )
+=head2 setScript ( url, params, [inBody] )
Sets a '."\n";
$self->{_javascript}{$url} = 1;
- $self->setRawHeadTags($tag);
+ if ($inBody) {
+ $self->setRawBodyTags($tag);
+ }
+ else {
+ $self->setRawHeadTags($tag);
+ }
}
#-------------------------------------------------------------------
diff --git a/lib/WebGUI/i18n/English/Asset_Template.pm b/lib/WebGUI/i18n/English/Asset_Template.pm
index 7b17560ee..cde93e6d9 100644
--- a/lib/WebGUI/i18n/English/Asset_Template.pm
+++ b/lib/WebGUI/i18n/English/Asset_Template.pm
@@ -298,6 +298,48 @@ Any scratch variables will be available in the template with this syntax:
context => q{Label for URL to make a duplicate and open the duplicate's edit screen},
},
+ 'attachment header index' => {
+ message => 'Index',
+ lastUpdated => 1241192473,
+ context => q|header for the sequence number column for attachments|,
+ },
+
+ 'attachment header type' => {
+ message => 'Type',
+ lastUpdated => 1241192473,
+ context => q|header for the attachment types column|,
+ },
+
+ 'attachment header url' => {
+ message => 'Url',
+ lastUpdated => 1241192473,
+ context => q|header for the url column for attachments|,
+ },
+
+ 'attachment header remove' => {
+ message => 'Remove',
+ lastUpdated => 1241192473,
+ context => q|header for the remove button column for attachments|,
+ },
+
+ 'attachment display label' => {
+ message => 'Attachments',
+ lastUpdated => 1241192473,
+ context => q|field label for displaying existing attachments|,
+ },
+
+ 'attachment add field label' => {
+ message => 'Add Attachments',
+ lastUpdated => 1241192473,
+ context => q|field label for adding new attachments|,
+ },
+
+ 'attachment add button' => {
+ message => 'Add',
+ lastUpdated => 1241192473,
+ context => q|button text for adding a new attachment|,
+ },
+
'usePacked label' => {
message => q{Use Packed Template},
lastUpdated => 0,
diff --git a/t/Asset/Template.t b/t/Asset/Template.t
index ae3579181..605ff673e 100644
--- a/t/Asset/Template.t
+++ b/t/Asset/Template.t
@@ -15,7 +15,7 @@ use lib "$FindBin::Bin/../lib";
use WebGUI::Test;
use WebGUI::Session;
use WebGUI::Asset::Template;
-use Test::More tests => 15; # increment this value for each test you create
+use Test::More tests => 32; # increment this value for each test you create
use Test::Deep;
my $session = WebGUI::Test->session;
@@ -67,6 +67,51 @@ my $template3 = $importNode->addChild({
ok(!$template3->get('headBlock'), 'headBlock is empty');
is($template3->get('extraHeadTags'), 'tag1 tag2 tag3', 'extraHeadTags contains headBlock info');
+my @atts = (
+ {type => 'headScript', sequence => 1, url => 'bar'},
+ {type => 'headScript', sequence => 0, url => 'foo'},
+ {type => 'stylesheet', sequence => 0, url => 'style'},
+ {type => 'bodyScript', sequence => 0, url => 'body'},
+);
+
+$template3->addAttachments(\@atts);
+my $att3 = $template3->getAttachments('headScript');
+is($att3->[0]->{url}, 'foo', 'has foo');
+is($att3->[1]->{url}, 'bar', 'has bar');
+is(@$att3, 2, 'proper size');
+
+$template3->prepare;
+ok(exists $session->style->{_link}->{style}, 'style in style');
+ok(exists $session->style->{_javascript}->{$_}, "$_ in style") for qw(foo bar body);
+
+# revision-ness of attachments
+
+# sleep so the revisiondate isn't duplicated
+sleep 1;
+
+my $template3rev = $template3->addRevision();
+my $att4 = $template3rev->getAttachments('headScript');
+is($att4->[0]->{url}, 'foo', 'rev has foo');
+is($att4->[1]->{url}, 'bar', 'rev has bar');
+is(@$att4, 2, 'rev is proper size');
+
+$template3rev->addAttachments([{type => 'headScript', sequence => 2, url => 'baz'}]);
+$att4 = $template3rev->getAttachments('headScript');
+$att3 = $template3->getAttachments('headScript');
+is($att3->[0]->{url}, 'foo', 'original still has foo');
+is($att3->[1]->{url}, 'bar', 'original still has bar');
+is(@$att3, 2, 'original does not have new thing');
+
+is($att4->[0]->{url}, 'foo', 'rev still has foo');
+is($att4->[1]->{url}, 'bar', 'rev still has bar');
+is($att4->[2]->{url}, 'baz', 'rev does have new thing');
+is(@$att4, 3, 'rev is proper size');
+
+$template3rev->purgeRevision();
+
+
+# done checking revision stuff
+
$template->purge;
$templateCopy->purge;
$template3->purge;
diff --git a/www/extras/templateAttachments.js b/www/extras/templateAttachments.js
new file mode 100644
index 000000000..442712029
--- /dev/null
+++ b/www/extras/templateAttachments.js
@@ -0,0 +1,203 @@
+// Really only used by the Template editor, but it doesn't belong in perl
+// code. It's too long.
+
+var addClick = (function() {
+ var uniqueId = 1;
+ var count = 0;
+ var urls = {};
+ var types = {};
+ var originals = {};
+ var addAnchor = document.getElementById('addAnchor');
+ var displayTable = document.getElementById('attachmentDisplay');
+
+ var nodes = {
+ table: '',
+ index: 'Index',
+ type: 'Type',
+ url: 'Url'
+ };
+
+ function init() {
+ displayTable.style.display = 'none';
+
+ for (var k in nodes) {
+ var id = 'addBox' + nodes[k];
+ nodes[k] = document.getElementById(id);
+ }
+
+ var opts = nodes.type.options;
+ for (var i = 0; i < opts.length; i++) {
+ var o = opts[i];
+ types[o.value] = o.text;
+ }
+
+ var query = YAHOO.util.Dom.getElementsByClassName;
+ var existing = query('existingAttachment', 'tr');
+ for (var i = 0; i < existing.length; i++) {
+ var node = existing[i];
+ var d = {
+ index: query('index', 'td', node)[0].innerHTML,
+ type: query('type', 'td', node)[0].innerHTML,
+ url: query('url', 'td', node)[0].innerHTML
+ };
+ originals[d.url] = true;
+ add(d);
+ node.parentNode.removeChild(node);
+ }
+ }
+
+ // When an original box gets changed, we should insert a removal node and
+ // name the fields so that we remove the old and insert the new.
+ function updater(u) {
+ return function() {
+ obj = urls[u];
+ obj.index.onchange = null;
+ obj.type.onchange = null;
+ insertHidden(u);
+ nameFields(u);
+ };
+ }
+
+ // Give the fields for an attachment entry some names so that they'll get
+ // posted to the backend.
+ function nameFields(u) {
+ var id = uniqueId++;
+ var obj = urls[u];
+ obj.index.name = 'attachmentIndex' + id;
+ obj.type.name = 'attachmentType' + id;
+ obj.url.name = 'attachmentUrl' + id;
+ }
+
+ // Insert a hidden input field in the form so that the backend knows to
+ // remove one of the original attachments
+ function insertHidden(u) {
+ var tr = urls[u].tr;
+ var hid = document.createElement('input');
+ hid.type = 'hidden';
+ hid.name = 'removeAttachment';
+ hid.value = u;
+ tr.parentNode.appendChild(hid);
+ delete originals[u];
+ }
+
+ // Make a function which will remove an attachment (remove the table row
+ // and insertHidden if necesary)
+ function remover(u) {
+ return function() {
+ var tr = urls[u].tr;
+
+ if (originals[u]) {
+ insertHidden(u);
+ }
+
+ tr.parentNode.removeChild(tr);
+ delete urls[u];
+
+ if (--count == 0) {
+ displayTable.style.display = 'none';
+ }
+ }
+ }
+
+ // Add a new attachment (proper table row, etc). Attachments that already
+ // existed (originals) will have unnamed fields so they don't get posted
+ // to the backend.
+ function add(d) {
+ if (urls[d.url]) {
+ alert('Already attached!');
+ return;
+ }
+
+ if (++count == 1) {
+ displayTable.style.display = 'block';
+ }
+
+ var index = document.createElement('input');
+ index.size = 2;
+ index.value = d.index;
+
+ var type = document.createElement('select');
+
+ for (var k in types) {
+ var o = document.createElement('option');
+ o.value = k;
+ o.text = types[k];
+ if (k == d.type) {
+ o.selected = true;
+ }
+ type.appendChild(o);
+ }
+
+ var url = document.createElement('input');
+ url.size = 40;
+
+ var update = updater(d.url);
+ url.value = d.url;
+ url.oldValue = d.url;
+ url.onchange = function() {
+ var newValue = url.value;
+ var oldValue = url.oldValue;
+ if (urls[newValue]) {
+ url.value = oldValue;
+ alert('Already attached!');
+ }
+ else {
+ url.oldValue = newValue;
+ var d = urls[oldValue];
+ if (originals[oldValue]) {
+ update();
+ }
+
+ delete urls[oldValue];
+ urls[newValue] = d;
+ }
+ };
+
+ var btn = document.createElement('input');
+ btn.type = 'button';
+ btn.value = 'Remove';
+ btn.onclick = remover(d.url);
+
+ var tr = document.createElement('tr');
+ var data = [index, type, url, btn];
+ for (var i = 0; i < data.length; i++) {
+ var td = document.createElement('td');
+ td.appendChild(data[i]);
+ tr.appendChild(td);
+ }
+
+ urls[d.url] = {
+ tr : tr,
+ index : index,
+ type : type,
+ url : url,
+ };
+
+ if (originals[d.url]) {
+ // url's is already taken care of above
+ index.onchange = update;
+ type.onchange = update;
+ }
+ else {
+ nameFields(d.url);
+ }
+
+ addAnchor.appendChild(tr);
+ }
+
+ init();
+ return function() {
+ var d = {
+ index: nodes.index.value,
+ type: nodes.type.value,
+ url: nodes.url.value
+ };
+
+ d.url = d.url.replace(/^\s+|\s+$/g, '')
+ if (d.url == '') {
+ alert('No url!');
+ return;
+ }
+ add(d);
+ };
+})();