diff --git a/app/views/admin/my_accounts/edit.html.haml b/app/views/admin/my_accounts/edit.html.haml
index 4e8e35c1..c68c5286 100644
--- a/app/views/admin/my_accounts/edit.html.haml
+++ b/app/views/admin/my_accounts/edit.html.haml
@@ -1,5 +1,8 @@
- title link_to(@account.name.blank? ? @account.name_was : @account.name, '#', :rel => 'my_account_name', :title => t('.ask_for_name'), :class => 'editable')
+- content_for :head do
+ = javascript_include_tag 'admin/account'
+
- content_for :submenu do
= render 'admin/shared/menu/settings'
diff --git a/app/views/admin/shared/_head.html.haml b/app/views/admin/shared/_head.html.haml
index 1b30fad9..387d13a3 100644
--- a/app/views/admin/shared/_head.html.haml
+++ b/app/views/admin/shared/_head.html.haml
@@ -10,7 +10,7 @@
= stylesheet_link_tag 'admin/layout', 'admin/jquery/ui', 'admin/plugins/toggle', 'admin/menu', 'admin/buttons', 'admin/formtastic', 'admin/formtastic_changes', 'admin/application', :media => 'screen', :cache => Rails.env.production? && !Locomotive.heroku?
-= javascript_include_tag 'admin/jquery', 'admin/jquery.ui', 'admin/rails', 'admin/utils', 'admin/plugins/shortcut', 'admin/plugins/toggle', 'admin/plugins/growl', 'admin/plugins/cookie', 'admin/application', 'admin/locales/datepicker_fr', :cache => Rails.env.production? && !Locomotive.heroku?
+= javascript_include_tag 'admin/jquery', 'admin/jquery.ui', 'admin/rails', 'admin/utils', 'admin/plugins/subscribe', 'admin/plugins/shortcut', 'admin/plugins/toggle', 'admin/plugins/growl', 'admin/plugins/cookie', 'admin/application', 'admin/locales/datepicker_fr', :cache => Rails.env.production? && !Locomotive.heroku?
%script{ :type => 'text/javascript' }
= find_and_preserve(growl_message)
diff --git a/doc/TODO b/doc/TODO
index d659d89e..487ebfd3 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -2,8 +2,7 @@ BOARD:
- refactor slugify method (use parameterize + create a module)
- send email when new content added thru api
-- publish event when saving form in ajax (for instance, in order to update account name or site name)
-- switch to list (theme assets / assets ?). delete all in once (with checkbox) or see details (updated_at, size, ...etc)
+- page templatized (bound to a model)
BACKLOG:
@@ -13,6 +12,7 @@ BACKLOG:
- belongs_to => association
- cucumber features for admin pages
- validation for custom fields
+- switch to list (theme assets / assets ?). delete all in once (with checkbox) or see details (updated_at, size, ...etc)
BUGS:
@@ -60,4 +60,5 @@ x sitemap
x refactoring admin crud (pages + layouts + snippets)
x flash messages in French
x save layout / snippet / page / stylesheet / javascript with CMD + S (ajax)
-x change action icons according to the right action [Sacha]
\ No newline at end of file
+x change action icons according to the right action [Sacha]
+x publish event when saving form in ajax (for instance, in order to update account name or site name)
\ No newline at end of file
diff --git a/public/javascripts/admin/account.js b/public/javascripts/admin/account.js
new file mode 100644
index 00000000..69f39ced
--- /dev/null
+++ b/public/javascripts/admin/account.js
@@ -0,0 +1,5 @@
+$(document).ready(function() {
+ $.subscribe('form.saved.success', function(event, data) {
+ $('#global-actions-bar a:first').html($('#my_account_name').val());
+ }, []);
+});
\ No newline at end of file
diff --git a/public/javascripts/admin/plugins/shortcut.js b/public/javascripts/admin/plugins/shortcut.js
index 1f8bd03c..6ae11956 100644
--- a/public/javascripts/admin/plugins/shortcut.js
+++ b/public/javascripts/admin/plugins/shortcut.js
@@ -36,6 +36,7 @@ jQuery.fn.saveWithShortcut = function() {
form.find('li.error input').eq(0).focus();
} else {
$.growl('success', data.notice);
+ $.publish('form.saved.success', [data]);
}
};
diff --git a/public/javascripts/admin/plugins/subscribe.js b/public/javascripts/admin/plugins/subscribe.js
new file mode 100644
index 00000000..16596cf8
--- /dev/null
+++ b/public/javascripts/admin/plugins/subscribe.js
@@ -0,0 +1,367 @@
+/*
+ * jquery.subscribe.1.1
+ *
+ * Implementation of publish/subcription framework for jQuery
+ * Requires use of jQuery. Tested with jQuery 1.3 and above
+ *
+ *
+ * Copyright (c) 2008 Eric Chijioke (obinna a-t g mail dot c o m)
+ *
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Release Notes:
+ *
+ * version 1.1:
+ *
+ * Fixed unexpected behavior which can occur when a script in a embedded page (page loaded in div,tab etc.) subscribes a handler for a topic using
+ * the jQuery subscribe ($.subscribe) or a no-id element but this subscribe plugin is not reloaded within that embedded page (for example, when
+ * script is included in containing page) . In this case, if the embedded page is reloaded without reloading the entire page (and plugin), the
+ * subscription could be made multiple times for the topic, which will call the handler multiple times each time the topic is published.
+ * Code has been added to prevent this when the subscription is made using the non-element subscribe ($.subscribe()), which assures that only one
+ * subscription is made for a topic for a given window/frame. To prevent this from happening for an element subscription ($elem.subscribe()), make
+ * sure that the element has an id attribute.
+ */
+
+
+(function($){
+
+ _subscribe_topics = {};
+ _subscribe_handlers = {};
+
+ _subscribe_getDocumentWindow = function(document){
+
+ return document.parentWindow || document.defaultView;
+ };
+
+ $.fn.extend({
+
+ /**
+ * Creates a new topic without any subscribers.
+ * Not usually used explicitly
+ */
+ createTopic : function(topic) {
+ if(topic && !_subscribe_topics[topic]) {
+
+ _subscribe_topics[topic] = {};
+ _subscribe_topics[topic].objects = {};
+ _subscribe_topics[topic].objects['__noId__'] = [];
+ }
+
+ return this;
+ },
+
+ /**
+ * Destroy an existing topic and unsubscribe all subscribers
+ */
+ destroyTopic : function(topic) {
+
+ if(topic && _subscribe_topics[topic]) {
+
+ for(i in _subscribe_topics[topic].objects) {
+
+ var object = _subscribe_topics[topic].objects[i];
+
+ if($.isArray(object)) { // handle '__noId__' elements
+
+ if(object.length > 0) {
+
+ for(j in object) {
+
+ object[j].unbind(topic);
+ }
+ }
+
+ } else {
+
+ object.unbind(topic,data);
+ }
+ }
+ }
+
+ delete _subscribe_topics[topic];
+
+ return this;
+ },
+
+ /**
+ * Subscribes an object to particular topic with a handler.
+ * When the topic is published, this handler will be executed.
+ *
+ * Parameters:
+ * -topic- is the string name of the topic
+ * -handler- is a handler function and is of the form function(event, data), in which the 'this' refers to the element itself.
+ * handler can be a function or can be a string referring to a function previously registered using the $.subscribeHandler() function
+ * Note: returning 'false' from the handler will prevent subsequent handlers from being executed on this element during
+ * this call.
+ * -data- (optional) is additional data that is passed to the event handler as event.data when the topic is published
+ *
+ * Note: Unexpected behavior can occur when a script in a embedded page (page loaded in div,tab etc.) subscribes a handler for a topic using
+ * the jQuery subscribe ($.subscribe) or a no-id element but this subscribe plugin is not reloaded within that embedded page (for example, when
+ * script is included in containing page) . In this case, if the embedded page is reloaded without reloading the entire page (and plugin), the
+ * subscription could be made multiple times for the topic, which will call the handler multiple times each time the topic is published.
+ * Code has been added to prevent this when the subscription is made using the non-element subscribe ($.subscribe()), which assures that only one
+ * subscription is made for a topic for a given window/frame. To prevent this from happening for an element subscription ($elem.subscribe()), make
+ * sure that the element has an id attribute.
+ */
+ subscribe : function(topic, handler, data) {
+
+ if(this[0] && topic && handler) {
+
+ this.createTopic(topic);
+
+ if(this.attr('id')) {
+
+ _subscribe_topics[topic].objects[this.attr('id')] = this;
+
+ } else {
+
+ //do not subscribe the same window/frame document multiple times, this causes unexpected behavior of executing embedded scripts multiple times
+ var noIdObjects = _subscribe_topics[topic].objects['__noId__'];
+
+ if(this[0].nodeType == 9) { //if document is being bound (the case for non-element jQuery subscribing ($.subscribe)
+
+ for ( var index in noIdObjects) {
+
+ var noIdObject = noIdObjects[index];
+
+ if(noIdObject[0].nodeType == 9 && _subscribe_getDocumentWindow(this[0]).frameElement == _subscribe_getDocumentWindow(noIdObject[0]).frameElement ) {
+
+ return this;
+ }
+ }
+ }
+
+ var exists = false;
+ for(var i = 0; i < noIdObjects.length; i++){
+ if(noIdObjects[i] == this){
+ exists = true;
+ break;
+ }
+ }
+
+ if(!exists) {
+
+ _subscribe_topics[topic].objects['__noId__'].push(this);
+ }
+ }
+
+ if(typeof(handler) == 'function') {
+
+ this.bind(topic, data, handler);
+
+ } else if(typeof(handler) == 'string' && typeof(_subscribe_handlers[handler]) == 'function') {
+
+ this.bind(topic, data, _subscribe_handlers[handler]);
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * Remove a subscription of an element to a topic.
+ * This will unbind stop all handlers from executing on this element when the topic
+ * is published
+ */
+ unsubscribe : function(topic) {
+
+ if(topic) {
+
+ if(_subscribe_topics[topic]) {
+
+ if(this.attr('id')) {
+
+ var object = _subscribe_topics[topic].objects[this.attr('id')];
+
+ if(object) {
+
+ delete _subscribe_topics[topic].objects[this.attr('id')];
+ }
+
+ } else {
+
+ var noIdObjects = _subscribe_topics[topic].objects['__noId__'];
+
+ for(var i = 0; i < noIdObjects.length; i++){
+
+ if(noIdObjects[i] == this){
+
+ subscribe_topics[topic].objects['__noId__'].splice(index,1);
+ break;
+ }
+ }
+ }
+ }
+
+ this.unbind(topic);
+ }
+
+ return this;
+ },
+
+ /**
+ * Publishes a topic (triggers handlers on all topic subscribers)
+ * This ends up calling any subscribed handlers which are functions of the form function (event, data)
+ * where: event - is a standard jQuery event object
+ * data - is the data parameter that was passed to this publish() method
+ * event.data - is the data parameter passed to the subscribe() function when this published topic was subscribed to
+ * event.target - is the dom element that subscribed to the event (or the document element if $.subscribe() was used)
+ *
+ * Parameters:
+ * -topic- is the string name of the topic
+ * -data- (optional) is additional data that is passed to the event handler 'data' parameter when the topic is published
+ * handler can be a function or can be a string referring to a function previously registered using the $.subscribeHandler() function
+ * -originalEvent- (optional) may be passed in a reference to an event which triggered this publishing. This will be passed as the
+ * 'originalEvent' field of the triggered event which will allow for controlling the propagation of higher level events
+ * from within the topic handler. In other words, this allows one to cancel execution of all subsequent handlers on the originalEvent
+ * for this element by return 'false' from a handler that is subscribed to the topic published here. This can be especially useful
+ * in conjunction with publishOnEvent(), where a topic is published when an event executes (such as a click) and we want our
+ * handler logic prevent additional topics from being published (For example if our topic displays a 'delete confirm' dialog on click and
+ * the user cancels, we may want to prevent subsequent topics bound to the original click event from being published).
+ */
+ publish : function(topic, data, originalEvent) {
+
+ if(topic) {
+
+ this.createTopic(topic);
+
+ //if an orginal event exists, need to modify the event object to prevent execution of all
+ //other handlers if the result of the handler is false (which calls stopPropagation())
+
+ var subscriberStopPropagation = function(){
+
+ this.isImmediatePropagationStopped = function(){
+ return true;
+ };
+
+ (new $.Event).stopPropagation();
+
+ if(this.originalEvent) {
+
+ this.originalEvent.isImmediatePropagationStopped = function(){
+ return true;
+ };
+
+ this.originalEvent.stopPropagation = subscriberStopPropagation;
+ }
+ }
+
+ var event = jQuery.Event(topic);
+ $.extend(event,{originalEvent: originalEvent, stopPropagation: subscriberStopPropagation});
+
+ for(i in _subscribe_topics[topic].objects) {
+
+ var object = _subscribe_topics[topic].objects[i];
+
+ if($.isArray(object)) { // handle '__noId__' elements (if any)
+
+ if(object.length > 0) {
+
+ for(j in object) {
+
+ object[j].trigger( event,data);
+ }
+ }
+
+ } else {
+
+ object.trigger( event,data);
+ }
+ }
+
+ }
+
+ return this;
+ },
+
+ /**
+ * Binds an objects event handler to a publish call
+ *
+ * Upon the event triggering, this ends up calling any subscribed handlers which are functions of the form function (event, data)
+ * where: event- is a standard jQuery event object
+ * event.data- is the data parameter passed to the subscribe() function when this published topic was subscribed to
+ * data- is the data parameter that was passed to this publishOnEvent() method
+ * Parameters:
+ * -event- is the string name of the event upon which to publish the topic
+ * -topic- is the string name of the topic to publish when the event occurs
+ * -data- (optional) is additional data which will be passed in to the publish() method ant hen available as the second ('data')
+ * parameter to the topic handler
+ */
+ publishOnEvent : function(event, topic, data) {
+
+ if(event && topic) {
+
+ this.createTopic(topic);
+
+ this.bind(event, data, function (e) {
+
+ $(this).publish(topic, e.data, e);
+ });
+ }
+
+ return this;
+ }
+ });
+
+ /**
+ * Make publish(), createTopic() and destroyTopic() callable without an element context
+ * Often don't need a context to subscribe, publish, create or destroy a topic.
+ * We will call from the document context
+ */
+ $.extend({
+
+ /**
+ * Subscribe an event handler to a topic without an element context
+ *
+ * Note: Caution about subscribing using same document to topic multiple time (maybe by loading subscribe script multiple times)
+ *
+ */
+ subscribe : function(topic, handler, data) {
+ return $(window).subscribe(topic, handler, data);
+
+ },
+
+ /**
+ * Unsubscribe an event handler for a topic without an element context
+ *
+ */
+ unsubscribe : function(topic, handler, data) {
+
+ return $(window).unsubscribe(topic, handler, data);
+
+ },
+
+ /**
+ * Register a handler function which can then be referenced by name when calling subscribe()
+ */
+ subscribeHandler: function(name, handler) {
+
+ if(name && handler && typeof(handler) == "function") {
+
+ _subscribe_handlers[name] = handler;
+ }
+
+ return $(window);
+ },
+
+ publish: function(topic, data) {
+
+ return $(window).publish(topic,data);
+ },
+
+ createTopic: function(topic) {
+
+ return $(window).createTopic(topic);
+ },
+
+ destroyTopic: function(topic) {
+
+ return $(window).destroyTopic(topic);
+ }
+
+ });
+
+})(jQuery);
diff --git a/public/javascripts/admin/site.js b/public/javascripts/admin/site.js
index 1c5f5df3..65e65e7a 100644
--- a/public/javascripts/admin/site.js
+++ b/public/javascripts/admin/site.js
@@ -1,7 +1,7 @@
$(document).ready(function() {
var defaultValue = $('fieldset.editable-list li.template input[type=text]').val();
-
+
/* __ fields ___ */
$('fieldset.editable-list li.template input[type=text]').focus(function() {
if ($(this).hasClass('void') && $(this).parents('li').hasClass('template'))
@@ -32,5 +32,8 @@ $(document).ready(function() {
e.preventDefault();
e.stopPropagation();
});
-
+
+ $.subscribe('form.saved.success', function(event, data) {
+ $('#header h1 a').html($('#current_site_name').val());
+ }, []);
});
\ No newline at end of file
diff --git a/public/stylesheets/admin/application.css b/public/stylesheets/admin/application.css
index fdb18af0..65312fe9 100644
--- a/public/stylesheets/admin/application.css
+++ b/public/stylesheets/admin/application.css
@@ -117,7 +117,7 @@ ul.assets li.asset.last {
margin-right: 0px;
}
-ul.assets li.asset h4 { margin: 0px; height: 30px; }
+ul.assets li.asset h4 { margin: 0px; height: 30px; border-bottom: 1px solid #C2C4D2; }
ul.assets li.asset h4 a {
position: relative;
diff --git a/public/stylesheets/admin/formtastic_changes.css b/public/stylesheets/admin/formtastic_changes.css
index 4068e5a2..9ca40a69 100644
--- a/public/stylesheets/admin/formtastic_changes.css
+++ b/public/stylesheets/admin/formtastic_changes.css
@@ -60,7 +60,7 @@ form.formtastic fieldset.foldable.folded legend span em {
form.formtastic fieldset.foldable ol {
clear: both;
width: 100%;
- overflow: hidden;
+ overflow: hidden;
}
form.formtastic fieldset.foldable.folded ol { display: none; }
@@ -80,6 +80,7 @@ form.formtastic fieldset.inputs ol {
padding-top: 15px;
padding-bottom: 5px;
background: #ebedf4 url(/images/admin/form/footer.png) no-repeat 0 bottom;
+ border-top: 1px solid #C2C4D2;
}
@media screen and (-webkit-min-device-pixel-ratio:0) {
diff --git a/public/stylesheets/admin/page_parts.css b/public/stylesheets/admin/page_parts.css
index a1e6fea6..89b9fe43 100644
--- a/public/stylesheets/admin/page_parts.css
+++ b/public/stylesheets/admin/page_parts.css
@@ -2,6 +2,7 @@
background: #ebedf4 url(/images/admin/form/footer.png) no-repeat 0 bottom;
width: 880px;
padding: 20px 20px;
+ border-top: 1px solid #C2C4D2;
}
#page-parts {
@@ -12,6 +13,12 @@
height: 30px;
}
+#page-parts .nav {
+ position: relative;
+ top: 1px;
+ z-index: 990;
+}
+
#page-parts .nav a {
float: left;
display: block;