summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjstritar@chromium.org <jstritar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-24 03:41:02 +0000
committerjstritar@chromium.org <jstritar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-24 03:41:02 +0000
commit482816411e1fb5379f7f9f7030f8648128bd95c7 (patch)
tree6a323e76e9c33bc65c168b8ec6a7d7ea63d5eeb2
parent076692a9d28e8634e87db7fe182adcf8a51fc293 (diff)
downloadchromium_src-482816411e1fb5379f7f9f7030f8648128bd95c7.zip
chromium_src-482816411e1fb5379f7f9f7030f8648128bd95c7.tar.gz
chromium_src-482816411e1fb5379f7f9f7030f8648128bd95c7.tar.bz2
[NTP] Allow reordering of apps via drag and drop.
BUG=53977 TEST=None. Review URL: http://codereview.chromium.org/6297013 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@72311 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/dom_ui/app_launcher_handler.cc16
-rw-r--r--chrome/browser/dom_ui/app_launcher_handler.h3
-rw-r--r--chrome/browser/extensions/extension_prefs.cc11
-rw-r--r--chrome/browser/extensions/extension_prefs.h3
-rw-r--r--chrome/browser/resources/new_new_tab.html1
-rw-r--r--chrome/browser/resources/new_new_tab.js13
-rw-r--r--chrome/browser/resources/ntp/apps.css29
-rw-r--r--chrome/browser/resources/ntp/apps.js257
-rw-r--r--chrome/browser/resources/ntp/drag_drop_controller.js164
-rw-r--r--chrome/common/notification_type.h3
10 files changed, 483 insertions, 17 deletions
diff --git a/chrome/browser/dom_ui/app_launcher_handler.cc b/chrome/browser/dom_ui/app_launcher_handler.cc
index f1679c7..b5c9142 100644
--- a/chrome/browser/dom_ui/app_launcher_handler.cc
+++ b/chrome/browser/dom_ui/app_launcher_handler.cc
@@ -157,6 +157,8 @@ void AppLauncherHandler::RegisterMessages() {
NewCallback(this, &AppLauncherHandler::HandleHideAppsPromo));
dom_ui_->RegisterMessageCallback("createAppShortcut",
NewCallback(this, &AppLauncherHandler::HandleCreateAppShortcut));
+ dom_ui_->RegisterMessageCallback("reorderApps",
+ NewCallback(this, &AppLauncherHandler::HandleReorderApps));
}
void AppLauncherHandler::Observe(NotificationType type,
@@ -168,6 +170,7 @@ void AppLauncherHandler::Observe(NotificationType type,
switch (type.value) {
case NotificationType::EXTENSION_LOADED:
case NotificationType::EXTENSION_UNLOADED:
+ case NotificationType::EXTENSION_LAUNCHER_REORDERED:
if (dom_ui_->tab_contents())
HandleGetApps(NULL);
break;
@@ -257,6 +260,8 @@ void AppLauncherHandler::HandleGetApps(const ListValue* args) {
NotificationService::AllSources());
registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
NotificationService::AllSources());
+ registrar_.Add(this, NotificationType::EXTENSION_LAUNCHER_REORDERED,
+ NotificationService::AllSources());
}
if (pref_change_registrar_.IsEmpty()) {
pref_change_registrar_.Init(
@@ -387,6 +392,17 @@ void AppLauncherHandler::HandleCreateAppShortcut(const ListValue* args) {
browser->profile(), extension);
}
+void AppLauncherHandler::HandleReorderApps(const ListValue* args) {
+ std::vector<std::string> extension_ids;
+ for (size_t i = 0; i < args->GetSize(); ++i) {
+ std::string value;
+ if (args->GetString(i, &value))
+ extension_ids.push_back(value);
+ }
+
+ extensions_service_->extension_prefs()->SetAppLauncherOrder(extension_ids);
+}
+
// static
void AppLauncherHandler::RecordWebStoreLaunch(bool promo_active) {
if (!promo_active) return;
diff --git a/chrome/browser/dom_ui/app_launcher_handler.h b/chrome/browser/dom_ui/app_launcher_handler.h
index 4940752..cee1aaf 100644
--- a/chrome/browser/dom_ui/app_launcher_handler.h
+++ b/chrome/browser/dom_ui/app_launcher_handler.h
@@ -71,6 +71,9 @@ class AppLauncherHandler
// Callback for the "createAppShortcut" message.
void HandleCreateAppShortcut(const ListValue* args);
+ // Callback for the 'reorderApps" message.
+ void HandleReorderApps(const ListValue* args);
+
private:
// Records a web store launch in the appropriate histograms. |promo_active|
// specifies if the web store promotion was active.
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc
index 3f6bb88..33ced74 100644
--- a/chrome/browser/extensions/extension_prefs.cc
+++ b/chrome/browser/extensions/extension_prefs.cc
@@ -1101,6 +1101,17 @@ int ExtensionPrefs::GetNextAppLaunchIndex() {
return max_value + 1;
}
+void ExtensionPrefs::SetAppLauncherOrder(
+ const std::vector<std::string>& extension_ids) {
+ for (size_t i = 0; i < extension_ids.size(); ++i)
+ SetAppLaunchIndex(extension_ids.at(i), i);
+
+ NotificationService::current()->Notify(
+ NotificationType::EXTENSION_LAUNCHER_REORDERED,
+ Source<ExtensionPrefs>(this),
+ NotificationService::NoDetails());
+}
+
void ExtensionPrefs::SetUpdateUrlData(const std::string& extension_id,
const std::string& data) {
DictionaryValue* dictionary = GetExtensionPref(extension_id);
diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h
index f02f6ec..828b98b 100644
--- a/chrome/browser/extensions/extension_prefs.h
+++ b/chrome/browser/extensions/extension_prefs.h
@@ -251,6 +251,9 @@ class ExtensionPrefs {
// highest current application launch index found.
int GetNextAppLaunchIndex();
+ // Sets the order the apps should be displayed in the app launcher.
+ void SetAppLauncherOrder(const std::vector<std::string>& extension_ids);
+
// The extension's update URL data. If not empty, the ExtensionUpdater
// will append a ap= parameter to the URL when checking if a new version
// of the extension is available.
diff --git a/chrome/browser/resources/new_new_tab.html b/chrome/browser/resources/new_new_tab.html
index e9bd7a1..b0f8ae9 100644
--- a/chrome/browser/resources/new_new_tab.html
+++ b/chrome/browser/resources/new_new_tab.html
@@ -289,6 +289,7 @@ i18nTemplate.process(document, templateData);
<script src="shared/js/cr/ui/context_menu_handler.js"></script>
<script src="ntp/util.js"></script>
+<script src="ntp/drag_drop_controller.js"></script>
<script src="ntp/most_visited.js"></script>
<script src="new_new_tab.js"></script>
<script src="ntp/apps.js"></script>
diff --git a/chrome/browser/resources/new_new_tab.js b/chrome/browser/resources/new_new_tab.js
index 877144c..fdc1c61 100644
--- a/chrome/browser/resources/new_new_tab.js
+++ b/chrome/browser/resources/new_new_tab.js
@@ -187,7 +187,7 @@ function createForeignSession(client, name) {
// Sort tabs by MRU order
win.tabs.sort(function(a, b) {
return a.timestamp < b.timestamp;
- })
+ });
// Create individual tab information.
win.tabs.forEach(function(data) {
@@ -205,7 +205,7 @@ function createForeignSession(client, name) {
handleIfEnterKey(maybeOpenForeignTab));
winSpan.appendChild(tabEl);
- })
+ });
// Append the window.
stack.appendChild(winSpan);
@@ -309,6 +309,7 @@ function handleWindowResize() {
if (layoutMode != oldLayoutMode){
mostVisited.useSmallGrid = b;
mostVisited.layout();
+ apps.layout({force:true});
renderRecentlyClosed();
renderForeignSessions();
updateAllMiniviewClippings();
@@ -626,6 +627,10 @@ function showSection(section) {
mostVisited.visible = true;
mostVisited.layout();
break;
+ case Section.APPS:
+ apps.visible = true;
+ apps.layout({disableAnimations:true});
+ break;
}
}
}
@@ -650,6 +655,10 @@ function hideSection(section) {
mostVisited.visible = false;
mostVisited.layout();
break;
+ case Section.APPS:
+ apps.visible = false;
+ apps.layout();
+ break;
}
var el = getSectionElement(section);
diff --git a/chrome/browser/resources/ntp/apps.css b/chrome/browser/resources/ntp/apps.css
index 2d03d58..ab129fc 100644
--- a/chrome/browser/resources/ntp/apps.css
+++ b/chrome/browser/resources/ntp/apps.css
@@ -2,7 +2,6 @@
#apps-content {
position: relative;
- width: intrinsic;
max-width: 780px; /* (124 + margin * 2) * 6 */
}
@@ -28,9 +27,8 @@ html.apps-promo-visible #apps-content {
-webkit-perspective: 400;
border-radius: 10px;
color: black;
- display: inline-block;
margin: 5px 3px;
- position: relative;
+ position: absolute;
height: 136px;
width: 124px; /* 920 / 7 - margin * 2 */
}
@@ -89,6 +87,19 @@ html.apps-promo-visible #apps-content {
opacity: .9;
}
+.app.dragging > .app-settings {
+ background-image: none;
+}
+
+.app.dragging {
+ opacity: .7;
+ z-index: 2;
+}
+
+#apps-content[launcher-animations=true] .app {
+ -webkit-transition: top .2s, left .2s, right .2s;
+}
+
@-webkit-keyframes bounce {
0% {
-webkit-transform: scale(0, 0);
@@ -112,7 +123,7 @@ html[install-animation-enabled=true] .app[new=installed] {
-webkit-transition: opacity .5s;
}
-.app[app-id=web-store-entry] > a {
+.app.web-store-entry > a {
background-image: url("chrome://theme/IDR_WEBSTORE_ICON");
}
@@ -154,18 +165,18 @@ html[dir=rtl] #apps-promo-hide {
float: left;
}
-html.apps-promo-visible .app[app-id=web-store-entry] {
+html.apps-promo-visible .app.web-store-entry {
position: absolute;
left: 100%;
top: 0;
-webkit-margin-start: 22px;
}
-html.apps-promo-visible[dir=rtl] .app[app-id=web-store-entry] {
+html.apps-promo-visible[dir=rtl] .app.web-store-entry {
right: 100%;
}
-html.apps-promo-visible .app[app-id=web-store-entry] a {
+html.apps-promo-visible .app.web-store-entry a {
font-weight: bold;
}
@@ -175,12 +186,12 @@ column when there is at least one full row of apps. Note that this is similar,
but different than its position during promo (html.apps-promo-visible), so we
never set .loner while the promo is running.
*/
-.app[app-id=web-store-entry].loner {
+.app.web-store-entry.loner {
position: absolute;
left: 100%;
top: 0;
}
-html[dir=rtl] .app[app-id=web-store-entry].loner {
+html[dir=rtl] .app.web-store-entry.loner {
right: 100%;
}
diff --git a/chrome/browser/resources/ntp/apps.js b/chrome/browser/resources/ntp/apps.js
index e61ebe0..db15e58 100644
--- a/chrome/browser/resources/ntp/apps.js
+++ b/chrome/browser/resources/ntp/apps.js
@@ -45,6 +45,15 @@ function getAppsCallback(data) {
return a.app_launch_index - b.app_launch_index;
});
+ // Determines if the web store link should be detached and place in the
+ // top right of the screen.
+ apps.detachWebstoreEntry =
+ !apps.showPromo && data.apps.length >= MAX_APPS_PER_ROW[layoutMode];
+
+ apps.data = data.apps;
+ if (!apps.detachWebstoreEntry)
+ apps.data.push('web-store-entry');
+
clearClosedMenu(apps.menu);
data.apps.forEach(function(app) {
appsSectionContent.appendChild(apps.createElement(app));
@@ -84,12 +93,15 @@ function getAppsCallback(data) {
appsPromoLink.setAttribute('ping', appsPromoPing);
maybeDoneLoading();
- if (isDoneLoading()) {
- if (!apps.showPromo && data.apps.length >= MAX_APPS_PER_ROW[layoutMode])
- webStoreEntry.classList.add('loner');
- else
- webStoreEntry.classList.remove('loner');
+ // Disable the animations when the app launcher is being (re)initailized.
+ apps.layout({disableAnimations:true});
+
+ if (apps.detachWebstoreEntry)
+ webStoreEntry.classList.add('loner');
+ else
+ webStoreEntry.classList.remove('loner');
+ if (isDoneLoading()) {
updateMiniviewClipping(appsMiniview);
layoutSections();
}
@@ -261,6 +273,12 @@ var apps = (function() {
}
});
+ // Moves the element at position |from| in array |arr| to position |to|.
+ function arrayMove(arr, from, to) {
+ var element = arr.splice(from, 1);
+ arr.splice(to, 0, element[0]);
+ }
+
return {
loaded: false,
@@ -268,6 +286,230 @@ var apps = (function() {
showPromo: false,
+ detachWebstoreEntry: false,
+
+ // The list of app ids, in order, of each app in the launcher.
+ data_: null,
+ get data() { return this.data_; },
+ set data(data) {
+ this.data_ = data.map(function(app) {
+ return app.id;
+ });
+ this.invalidate_();
+ },
+
+ dirty_: true,
+ invalidate_: function() {
+ this.dirty_ = true;
+ },
+
+ visible_: true,
+ get visible() {
+ return this.visible_;
+ },
+ set visible(visible) {
+ this.visible_ = visible;
+ this.invalidate_();
+ },
+
+ // DragAndDropDelegate
+
+ dragContainer: $('apps-content'),
+ transitionsDuration: 200,
+
+ get dragItem() { return this.dragItem_; },
+ set dragItem(dragItem) {
+ if (this.dragItem_ != dragItem) {
+ this.dragItem_ = dragItem;
+ this.invalidate_();
+ }
+ },
+
+ // The dimensions of each item in the app launcher. This calculates the
+ // dimensions dynamically, so it should be called after creating the DOM.
+ dimensions_: null,
+ get dimensions() {
+ if (this.dimensions_)
+ return this.dimensions_;
+
+ var app = this.dragContainer.firstChild;
+
+ var width = app.offsetWidth;
+ var height = app.offsetHeight;
+
+ // If the apps haven't properly loaded yet, don't cache the result.
+ if (app.offsetWidth == 0 || app.offsetHeight == 0)
+ return {width:0, height:0};
+
+ var style = getComputedStyle(app);
+
+ var marginWidth =
+ parseInt(style.marginLeft) + parseInt(style.marginRight);
+ var marginHeight =
+ parseInt(style.marginTop) + parseInt(style.marginBottom);
+
+ var borderWidth = parseInt(style.borderLeftWidth) +
+ parseInt(style.borderRightWidth);
+ var borderHeight = parseInt(style.borderTopWidth) +
+ parseInt(style.borderBottomWidth);
+
+ this.dimensions_ = {
+ width: width + marginWidth + borderWidth,
+ height: height + marginHeight + borderHeight
+ };
+
+ return this.dimensions_;
+ },
+
+ // Gets the item under the mouse event |e|. Returns null if there is no
+ // item or if the item is not draggable.
+ getItem: function(e) {
+ var item = findAncestorByClass(e.target, 'app');
+
+ // You can't drag the web store launcher.
+ if (item.classList.contains('web-store-entry'))
+ return null;
+
+ return item;
+ },
+
+ // Returns true if |coordinates| point to a valid drop location. The
+ // coordinates are relative to the drag container and the object should
+ // have the 'x' and 'y' properties set.
+ canDropOn: function(coordinates) {
+ var cols = MAX_APPS_PER_ROW[layoutMode];
+ var rows = Math.ceil(this.data.length / cols);
+
+ var bottom = rows * this.dimensions.height;
+ var right = cols * this.dimensions.width;
+
+ if (coordinates.x > right || coordinates.x < 0 ||
+ coordinates.y > bottom || coordinates.y < 0)
+ return false;
+
+ var position = this.getIndexAt_(coordinates);
+ var appCount = this.data.length;
+
+ if (!this.detachWebstoreEntry)
+ appCount--;
+
+ return position >= 0 && position < appCount;
+ },
+
+ setDragPlaceholder: function(coordinates) {
+ var position = this.getIndexAt_(coordinates);
+ var appId = this.dragItem.querySelector('a').getAttribute('app-id');
+ var current = this.data.indexOf(appId);
+
+ if (current == position || current < 0)
+ return;
+
+ arrayMove(this.data, current, position);
+ this.invalidate_();
+ this.layout();
+ },
+
+ getIndexAt_: function(coordinates) {
+ var x = coordinates.x;
+ var y = coordinates.y;
+
+ var w = this.dimensions.width;
+ var h = this.dimensions.height;
+
+ var availableWidth = this.dragContainer.offsetWidth;
+
+ var row = Math.floor(y / h);
+ var col = Math.floor(x / w);
+ var index = Math.floor(availableWidth / w) * row + col;
+
+ return index;
+ },
+
+ saveDrag: function() {
+ this.invalidate_();
+ this.layout();
+
+ var appIds = this.data.filter(function(id) {
+ return id != 'web-store-entry';
+ });
+
+ // Wait until the transitions are complete before notifying the browser.
+ // Otherwise, the apps will be re-rendered while still transitioning.
+ setTimeout(function() {
+ chrome.send('reorderApps', appIds);
+ }, this.transitionsDuration + 10);
+ },
+
+ layout: function(options) {
+ options = options || {};
+ if (!this.dirty_ && options.force != true)
+ return;
+
+ try {
+ var container = this.dragContainer;
+ if (options.disableAnimations)
+ container.setAttribute('launcher-animations', false);
+ var d0 = Date.now();
+ this.layoutImpl_();
+ this.dirty_ = false;
+ logEvent('apps.layout: ' + (Date.now() - d0));
+
+ } finally {
+ if (options.disableAnimations) {
+ // We need to re-enable animations asynchronously, so that the
+ // animations are still disabled for this layout update.
+ setTimeout(function() {
+ container.setAttribute('launcher-animations', true);
+ }, 0);
+ }
+ }
+ },
+
+ layoutImpl_: function() {
+ var apps = this.data;
+ var rects = this.getLayoutRects_(apps.length);
+ var appsContent = this.dragContainer;
+
+ if (!this.visible)
+ return;
+
+ for (var i = 0; i < apps.length; i++) {
+ var app = appsContent.querySelector('[app-id='+apps[i]+']').parentNode;
+
+ // If the node is being dragged, don't try to place it in the grid.
+ if (app == this.dragItem)
+ continue;
+
+ app.style.left = rects[i].left + 'px';
+ app.style.top = rects[i].top + 'px';
+ }
+
+ // We need to set the container's height manually because the apps use
+ // absolute positioning.
+ var rows = Math.ceil(apps.length / MAX_APPS_PER_ROW[layoutMode]);
+ appsContent.style.height = (rows * this.dimensions.height) + 'px';
+ },
+
+ getLayoutRects_: function(appCount) {
+ var availableWidth = this.dragContainer.offsetWidth;
+ var rtl = isRtl();
+ var rects = [];
+ var w = this.dimensions.width;
+ var h = this.dimensions.height;
+
+ for (var i = 0; i < appCount; i++) {
+ var row = Math.floor((w * i) / availableWidth);
+ var top = row * h;
+ var left = (w * i) % availableWidth;
+
+ // Reflect the X axis if an RTL language is active.
+ if (rtl)
+ left = availableWidth - left - w;
+ rects[i] = {left: left, top: top, row: row};
+ }
+ return rects;
+ },
+
createElement: function(app) {
var div = createElement(app);
var a = div.firstChild;
@@ -344,7 +586,7 @@ var apps = (function() {
'name': localStrings.getString('web_store_title'),
'launch_url': localStrings.getString('web_store_url')
});
- elm.setAttribute('app-id', 'web-store-entry');
+ elm.classList.add('web-store-entry');
return elm;
},
@@ -364,3 +606,6 @@ var apps = (function() {
}
};
})();
+
+// Enable drag and drop reordering of the app launcher.
+var appDragAndDrop = new DragAndDropController(apps);
diff --git a/chrome/browser/resources/ntp/drag_drop_controller.js b/chrome/browser/resources/ntp/drag_drop_controller.js
new file mode 100644
index 0000000..fe604a4
--- /dev/null
+++ b/chrome/browser/resources/ntp/drag_drop_controller.js
@@ -0,0 +1,164 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The delegate interface:
+// dragContainer -->
+// element containing the draggable items
+//
+// transitionsDuration -->
+// length of time of transitions in ms
+//
+// dragItem -->
+// get / set property containing the item being dragged
+//
+// getItem(e) -->
+// get's the item that is under the mouse event |e|
+//
+// canDropOn(coordinates) -->
+// returns true if the coordinates (relative to the drag container)
+// point to a valid place to drop an item
+//
+// setDragPlaceholder(coordinates) -->
+// tells the delegate that the dragged item is currently above
+// the specified coordinates.
+//
+// saveDrag() -->
+// tells the delegate that the drag is done. move the item to the
+// position last specified by setDragPlaceholder. (e.g., commit changes)
+//
+
+function DragAndDropController(delegate) {
+ this.delegate_ = delegate;
+
+ this.installHandlers_();
+}
+
+DragAndDropController.prototype = {
+ startX_: 0,
+ startY_: 0,
+ startScreenX_: 0,
+ startScreenY_: 0,
+
+ installHandlers_: function() {
+ var el = this.delegate_.dragContainer;
+ el.addEventListener('dragstart', this.handleDragStart_.bind(this));
+ el.addEventListener('dragenter', this.handleDragEnter_.bind(this));
+ el.addEventListener('dragover', this.handleDragOver_.bind(this));
+ el.addEventListener('dragleave', this.handleDragLeave_.bind(this));
+ el.addEventListener('drop', this.handleDrop_.bind(this));
+ el.addEventListener('dragend', this.handleDragEnd_.bind(this));
+ el.addEventListener('drag', this.handleDrag_.bind(this));
+ el.addEventListener('mousedown', this.handleMouseDown_.bind(this));
+ },
+
+ getCoordinates_: function(e) {
+ var rect = this.delegate_.dragContainer.getBoundingClientRect();
+ var coordinates = {
+ x: e.clientX + window.scrollX - rect.left,
+ y: e.clientY + window.scrollY - rect.top
+ };
+
+ // If we're in an RTL language, reflect the coordinates so the delegate
+ // doesn't need to worry about it.
+ if (isRtl())
+ coordinates.x = this.delegate_.dragContainer.offsetWidth - coordinates.x;
+
+ return coordinates;
+ },
+
+ // Listen to mousedown to get the relative position of the cursor when
+ // starting drag and drop.
+ handleMouseDown_: function(e) {
+ var item = this.delegate_.getItem(e);
+ if (!item)
+ return;
+
+ this.startX_ = item.offsetLeft;
+ this.startY_ = item.offsetTop;
+ this.startScreenX_ = e.screenX;
+ this.startScreenY_ = e.screenY;
+
+ // We don't want to focus the item on mousedown. However, to prevent
+ // focus one has to call preventDefault but this also prevents the drag
+ // and drop (sigh) so we only prevent it when the user is not doing a
+ // left mouse button drag.
+ if (e.button != 0) // LEFT
+ e.preventDefault();
+ },
+
+ handleDragStart_: function(e) {
+ var item = this.delegate_.getItem(e);
+ if (!item)
+ return;
+
+ // Don't set data since HTML5 does not allow setting the name for
+ // url-list. Instead, we just rely on the dragging of link behavior.
+ this.delegate_.dragItem = item;
+ item.classList.add('dragging');
+
+ e.dataTransfer.effectAllowed = 'copyLinkMove';
+ },
+
+ handleDragEnter_: function(e) {
+ if (this.delegate_.canDropOn(this.getCoordinates_(e)))
+ e.preventDefault();
+ },
+
+ handleDragOver_: function(e) {
+ var coordinates = this.getCoordinates_(e);
+ if (!this.delegate_.canDropOn(coordinates))
+ return;
+
+ this.delegate_.setDragPlaceholder(coordinates);
+ e.preventDefault();
+ e.dataTransfer.dropEffect = 'move';
+ },
+
+ handleDragLeave_: function(e) {
+ if (this.delegate_.canDropOn(this.getCoordinates_(e)))
+ e.preventDefault();
+ },
+
+ handleDrop_: function(e) {
+ var dragItem = this.delegate_.dragItem;
+ if (!dragItem)
+ return;
+
+ this.delegate_.dragItem = null;
+ this.delegate_.saveDrag();
+
+ setTimeout(function() {
+ dragItem.classList.remove('dragging');
+ }, this.delegate_.transitionsDuration + 10);
+ },
+
+ handleDragEnd_: function(e) {
+ return this.handleDrop_(e);
+ },
+
+ handleDrag_: function(e) {
+ // Moves the drag item making sure that it is not displayed outside the
+ // browser viewport.
+ var dragItem = this.delegate_.dragItem;
+ var rect = this.delegate_.dragContainer.getBoundingClientRect();
+
+ var x = this.startX_ + e.screenX - this.startScreenX_;
+ var y = this.startY_ + e.screenY - this.startScreenY_;
+
+ // The position of the item is relative to #apps so we need to
+ // subtract that when calculating the allowed position.
+ x = Math.max(x, -rect.left);
+ x = Math.min(x, document.body.clientWidth - rect.left -
+ dragItem.offsetWidth - 2);
+
+ // The shadow is 2px
+ y = Math.max(-rect.top, y);
+ y = Math.min(y, document.body.clientHeight - rect.top -
+ dragItem.offsetHeight - 2);
+
+ // Override right in case of RTL.
+ dragItem.style.left = x + 'px';
+ dragItem.style.top = y + 'px';
+ }
+};
diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h
index 54f2a36..fadff2f 100644
--- a/chrome/common/notification_type.h
+++ b/chrome/common/notification_type.h
@@ -1305,6 +1305,9 @@ class NotificationType {
// |webkit_glue::PasswordForm|s that were affected.
LOGINS_CHANGED,
+ // Sent when the applications in the NTP app launcher have been reordered.
+ EXTENSION_LAUNCHER_REORDERED,
+
// Count (must be last) ----------------------------------------------------
// Used to determine the number of notification types. Not valid as
// a type parameter when registering for or posting notifications.