// REQUIRES domutil.js - BK // Some string constants that will be very useful ... // ID : contiguous DIV ID names // PREF : a prefix to a DIV ID // DIR : a direction of motion // Prefixes and Constants var MINIQ_AREA_MAIN = 0; var MINIQ_AREA_JUSTADDED = 1; var CLASS_MINIQ_ROW = "mqr"; var CLASS_MINIQ_ROW_NONMOVABLE = "mqr_nomove"; var PREF_DRAGGABLE = "dd_"; var PREF_MINIQ = "mq_"; var PREF_MINIQ_INDICATOR = "mqi_"; var PREF_MINIQ_TITLE = "mqt_"; var PREF_MINIQ_NUM = "mqn_"; var PREF_MINIQ_JUSTADDED = "mqja_"; var PREF_MINIQ_JUSTADDED_INDICATOR = "mqjai_"; var PREF_MINIQ_JUSTADDED_TITLE = "mqjat_"; var PREF_MINIQ_JUSTADDED_NUM = "mqjan_"; var ID_JUSTADDED_INDICATOR = "jaInd"; var ID_TOP_BOUNDARY = "mqTop"; var ID_BOTTOM_BOUNDARY = "mqBot"; var DIR_DOWN = 0; var DIR_UP = 1; var TRIPPER_BUFFER_SIZE = 4; var MAX_QUEUE_TO_SHOW = 10; // also defined in miniQueue.jsp // END Prefixes and Constants // Colors var baseColor = "#FFFFFF"; var movedColor = baseColor; // was #ecedff var selectedColor = "#FAF1D5"; var justAddedColor = "#FAE096"; var borderColor = "#3b92ca"; var indicatorColdColor = "#ffffff"; var indicatorHotColor = "#CC0000"; // END Colors // Position Variables var innerOffsetY = 0; var eventOffsetY = 0; var outerOffsetY = 0; // END Position Variables // Numbers to Track var numDisplayedYourQueue = 0; var qItemNumbers = 0; var jaqItemNumbers = 0; var firstDisplayedJustAddedQueueItem = 0; var slideHeight = 0; // Legacy -- don't tease out yet // END Numbers to Track // Flags var hasCrossedMovie = false; var hasCrossedIndicator = false; var justAddedIndicatorCrossed = null; var movingItemDown = null; var dragDirection = null; // END Flags // Movers and Pointers var marypoppins = null; var clickedPosition = -1; var hotIndicatorId = null; var tripperTop = 0; var tripperBottom = 0; var itemHoveredOver = null; // END Movers and Pointers // Global objects - Tracking var startingPoints = new Object(); var endingPoints = new Object(); var currentStartingPoints = new Object(); var currentEndingPoints = new Object(); var indicatorStartingPoints = new Object(); var indicatorEndingPoints = new Object(); // END Global objects - Tracking // // END constant declarations // // // MiniQueue object detection and engagement systems -- can we work with what has been selected? // function getItem(evt) { if (!marypoppins) { marypoppins = (evt.target) ? evt.target : evt.srcElement; } } function isMiniQueueItem(item) { if (typeof item == "string") item = getNode(item); if (item.className == CLASS_MINIQ_ROW || item.id.indexOf(PREF_MINIQ) != -1) return true; else return false; } function isDraggable(item) { if ( item && item.nodeName == "DIV" && (isMiniQueueItem(item) || item.id.indexOf(PREF_DRAGGABLE) != -1 ) ) return true; else return false; } function getMovieIdFromElementId(elementId) { return elementId.substring(elementId.indexOf("_")+1, elementId.length); } function getPositionFromElementId(elementId) { return elementId.substring(elementId.indexOf("_")+1, elementId.length); } // // END MiniQueue object detection and engagement // // // Position Tracking Functions and Tools // function initializePoints(node) { var newnode = getNode(node); startingPoints[node] = currentStartingPoints[node] = newnode.offsetTop; endingPoints[node] = currentEndingPoints[node] = newnode.offsetTop + newnode.offsetHeight; createBorder(node, null); } function initializeIndicatorPoints(node) { var newnode = getNode(node); indicatorStartingPoints[node] = startingPoints[node] = currentStartingPoints[node] = newnode.offsetTop; indicatorEndingPoints[node] = endingPoints[node] = currentEndingPoints[node] = newnode.offsetTop + newnode.offsetHeight; createBorder(node, null); } function updatePoints(node) { var newnode = document.getElementById(node); currentStartingPoints[node] = newnode.offsetTop; currentEndingPoints[node] = newnode.offsetTop + newnode.offsetHeight; } // // END Position Tracking Functions and Tools // // // Queue and Movie Related Functions // // // Queue Item Object // function changeQueueItemPosition(newpos) { this.startPosition = new Number(this.currentPosition); this.currentPosition = newpos; } // set the start position to the same as the current position after visuals reflect virtual queue function synchronizeQueueItemPosition() { this.startPosition = this.currentPosition; } function itemHasMovedDown() { if (this.startPosition < this.currentPosition) { return true; } else if (this.startPosition > this.currentPosition) { return false; } else { return null; } } // // Creating QueueItems // function initializeQueueItem(id, title, pos, released) { var qi = new QueueItem(id, title, pos, false); if (window.location.search.indexOf(qi.id) != -1) { qi.wasAddedThisPage = true; } custq.addItem(qi); } function initializeJustAddedItem(id, title, pos, released) { var qi = new QueueItem(id, title, pos, true); if (window.location.search.indexOf(qi.id) != -1) { qi.wasAddedThisPage = true; } if (this.firstJustAdded = 0) this.firstJustAdded = pos; if (this.initialFirstJustAdded = 0) this.firstJustAdded; this.lastJustAdded = pos; custq.addItem(qi); } // // END Creating Queue Items // function QueueItem(movieId, movieTitle, firstPos, ja) { this.id = movieId; this.title = movieTitle; this.instantiatedPosition = firstPos; // the Queue position of the item when the page was rendered. this.startPosition = firstPos; // the Queue position of the item before any drag-drop actions. Different from the instantiatedPosition since there may be multiple moves per page. this.currentPosition = firstPos; // the Queue position of the item after any drag-drop actions this.justAdded = ja; // boolean this.changePosition = changeQueueItemPosition; this.syncPosition = synchronizeQueueItemPosition; this.hasMovedDown = itemHasMovedDown; this.hasBeenDragged = false; this.wasAddedThisPage = false; return this; } // // END Queue Item Object // // // Queue Object // // // Accessing QueueItems // function getItemFromQueueByPosition(pos) { // This needs to be a lot smarter about getting justAdded versus normal items // Make it so and remove the hacks that compensate for this var item = this.items[pos]; if (typeof item == "undefined") { item = this.jaItems[pos-this.initialFirstJustAdded]; } return item; } function getItemFromQueueById(movieId) { var qi = null; for (var ii = 0; ii < this.mainSize; ii++) { var item = this.getItem(ii); if (item.id == movieId) { qi = item; break; } } if (qi == null) { for (var ii = 0; ii < this.jaSize; ii++) { var item = this.getItem(ii+this.firstJustAdded); if (item.id == movieId) { qi = item; break; } } } return qi; } // // END Accessing QueueItems // // // Moving QueueItems around // function placeItemAtPosition(item, position) { this.items[position] = item; } function placeJAItemAtPosition(item, position) { this.jaItems[position] = item; } function addItemToEnd(item) { if (item.justAdded == null || !item.justAdded) { this.items[this.mainSize] = item; this.mainSize++; qItemNumbers++; } else { this.jaItems[this.jaSize] = item; this.jaSize++; jaqItemNumbers++; this.initialJASize++; } } function newResortQueue(mover, q_arg) { var insertPoint = mover.currentPosition; var removedFromPoint = mover.startPosition; var offset = 0; if (removedFromPoint < insertPoint) { movingItemDown = true; offset = -1; } else { movingItemDown = false; offset = 1; } var backfillStart = removedFromPoint; var backfillFinish = (insertPoint+offset); if (backfillStart >= custq.mainSize) { backfillStart = custq.mainSize-1; } if (movingItemDown) { for (var ii = backfillStart; ii <= backfillFinish; ii++) { var tempItem = q_arg[ii-offset]; this.placeItem(tempItem, ii); } } else { for (var ii = backfillStart; ii >= backfillFinish; ii--) { var tempItem = q_arg[ii-offset]; this.placeItem(tempItem, ii); } } this.placeItem(mover, insertPoint); } // // END Moving QueueItems around // function Queue() { this.items = new Object(); this.jaItems = new Object(); this.tempItems = new Object(); this.tempJAItems = new Object(); this.mainSize = 0; this.jaSize = 0; this.tempMainSize = 0; this.tempJASize = 0; this.initialJASize = 0; this.numMainAwaitingReleaseTitles = 0; this.numJustAddedAwaitingReleaseTitles = 0; this.addItem = addItemToEnd; this.placeItem = placeItemAtPosition; this.placeJAItem = placeJAItemAtPosition; this.getItem = getItemFromQueueByPosition; this.getItemById = getItemFromQueueById; this.resort = newResortQueue; this.firstJustAdded = 0; this.initialFirstJustAdded = 0; this.lastJustAdded = 0; return this; } // // END Queue Object // // // END Queue and Movie-Related Functions // // // Updating the Page And Interface Functions ... // // Visually slide a MiniQueue node and update its points. Currently unused, but preserve it. function slideMiniQueueNode(slider, amount) { moveNode(slider, amount); updatePoints(slider.id); } // Drop left and right one-pixel borders on a node. Falls back on "borderColor" if none specified. function createBorder(node, color) { if (color == null) color = borderColor; if (typeof node == "string") node = getNode(node); // Sanity check if (node == null || color == null) return; node.style.borderLeft="1px solid " + color; node.style.borderRight="1px solid " + color; } // Highlight the "drop zone" indicators function highlightIndicator(newIndicatorId) { if (hotIndicatorId != null) { document.getElementById(hotIndicatorId).style.background = indicatorColdColor; } if (newIndicatorId != null) { var indicator = document.getElementById(newIndicatorId); indicator.style.background = indicatorHotColor; } hotIndicatorId = newIndicatorId; } // This method isn't attached to an object. It's a quick way to fire off all the tasks that transpire when an item has been moved. function moveQueueItem(item, newpos) { // prepare the request for the move and fire it off item.changePosition(newpos); item.hasBeenDragged = true; custq.resort(item, custq.items); // fire off the request sendChangeRequest(item); // update the queue display updateQueueDisplay(item); } // This function is fragile. Tinker with it at your own peril. "Optimizations" will cause you pain. - BK function updateQueueDisplay(item) { var presyncStartPos = item.startPosition; for (var ii = 0; ii < custq.mainSize; ii++) { var row = getNode(PREF_MINIQ + ii); var titleNode = getNode(PREF_MINIQ_TITLE + ii); var qi = custq.items[ii]; replaceInnerText(titleNode, qi.title); qi.changePosition(ii); qi.syncPosition(); row.style.background = qi.hasBeenDragged ? movedColor : baseColor; updatePoints(PREF_MINIQ + ii); updatePoints(PREF_MINIQ_INDICATOR + ii); } if (item.justAdded || marypoppins.id.indexOf(PREF_MINIQ_JUSTADDED) != -1) { // Because getItemFromQueueByPosition() isn't very smart, we may have grabbed this item from the main Queue array instead of the justAdded array. // Compensate for now by checking to see if the item first clicked was from the JA section. // Both this method and getItemFromQueueByPosition() will need to be changed later. - BK var offset = 1; var backfillStart = presyncStartPos; var backfillFinish = custq.firstJustAdded+offset; var undisplayedJASlots = custq.initialJASize - custq.jaSize; for (var ii = backfillStart; ii >= backfillFinish; ii--) { var visualPositionTarget = ii; var actualPositionTarget = visualPositionTarget - custq.initialFirstJustAdded; var jqi = custq.jaItems[actualPositionTarget-offset]; jqi.changePosition(visualPositionTarget); jqi.syncPosition(); custq.placeJAItem(jqi, actualPositionTarget); var jarow = getNode(PREF_MINIQ_JUSTADDED + visualPositionTarget); var jaTitleNode = getNode(PREF_MINIQ_JUSTADDED_TITLE + visualPositionTarget); var jaNumberNode = getNode(PREF_MINIQ_JUSTADDED_NUM + visualPositionTarget); // Visual shift replaceInnerText(jaTitleNode, jqi.title); replaceInnerText(jaNumberNode, ((jqi.currentPosition+1) + ".")); jarow.style.background = jqi.wasAddedThisPage ? justAddedColor : baseColor; } var blankItem = QueueItem(0, "", 9999, true); custq.placeJAItem(blankItem, undisplayedJASlots); // remove the first slot killNode(PREF_MINIQ_JUSTADDED + (custq.firstJustAdded)); killNode(PREF_MINIQ_JUSTADDED_INDICATOR + (custq.firstJustAdded)); // clean up the list quantities and start points custq.firstJustAdded++; custq.jaSize--; // update the positions of the rest of the JA items for (var ii = custq.firstJustAdded; ii <= custq.lastJustAdded; ii++) { updatePoints(PREF_MINIQ_JUSTADDED + ii); updatePoints(PREF_MINIQ_JUSTADDED_INDICATOR + ii); } // Clear the just added flag. item.justAdded = false; if (custq.jaSize == 0 && custq.numJustAddedAwaitingReleaseTitles == 0) { killNode(ID_JUSTADDED_INDICATOR); } } // resync just in case item.syncPosition(); } // create URL and send off request. This is all in MiniQueueHandler.java function sendChangeRequest(item) { var url = "/MiniQueue?action=1&movieid=" + item.id + "&priority=" + (item.currentPosition+1) + "&mvregion=" + (item.justAdded ? 1 : 2) + "&prevpage=" + escape(window.location.pathname + window.location.search) + "&ncok=y"; window.location.href = url; } // // END Updating the Page ... // // // Rendering Procedures // function getIndicatorDivId(type, indicatorPosition) { return (type == MINIQ_AREA_MAIN ? PREF_MINIQ_INDICATOR : PREF_MINIQ_JUSTADDED_INDICATOR) + indicatorPosition; } function emitIndicator(type, indicatorPosition) { document.write("
.
\n"); } function emitMiniQueueRow(type, title, movieId, position, style, released) { var idPref, numPref, titlePref; if (type == MINIQ_AREA_MAIN) { idPref = PREF_MINIQ; numPref = PREF_MINIQ_NUM; titlePref = PREF_MINIQ_TITLE; } else if (type == MINIQ_AREA_JUSTADDED) { idPref = PREF_MINIQ_JUSTADDED; numPref = PREF_MINIQ_JUSTADDED_NUM; titlePref = PREF_MINIQ_JUSTADDED_TITLE; } else { return; } document.write("
\n"); document.write("\n"); document.write("\n"); document.write("
"); document.write("
"); document.write(Number(position)+1); document.write(".
"); document.write(title); document.write("
"); initializePoints(idPref + position); if (type == MINIQ_AREA_MAIN) { initializeQueueItem(movieId, title, position, released); numDisplayedYourQueue++; } else if (type == MINIQ_AREA_JUSTADDED) { initializeJustAddedItem(movieId, title, position, released); } } function emitAwaitingReleaseTitleRow(title, style) { document.write("
--"); document.write(title); document.write("
\n"); } // // END Rendering Procedures // // // Event handling. // function miniqCapture(evt) { evt = (evt) ? evt : event; getItem(evt); // If we didn't grab anything, get out! if (!marypoppins) { miniqRelease(evt); return false; } while (!isDraggable(marypoppins) && marypoppins.nodeName != "BODY" && marypoppins.nodeName != "HTML" && marypoppins.nodeName != "INPUT") { marypoppins = marypoppins.parentNode; } // Is this an object we can play with? if (!isMiniQueueItem(marypoppins) || !isDraggable(marypoppins)) { miniqRelease(evt); } else { document.onmouseup = miniqRelease; marypoppins.style.zIndex = 1000; if (evt.pageY || evt.offsetY || evt.clientY) { eventOffsetY = (evt.offsetY) ? evt.offsetY : evt.clientY; } innerOffsetY = (evt.y) ? evt.y : evt.layerY; outerOffsetY = marypoppins.offsetTop; outerOffsetY = marypoppins.style.top ? (marypoppins.offsetTop - stripUnits(marypoppins.style.top)) : marypoppins.offsetTop; marypoppins.style.background = selectedColor; document.onmousemove = miniqDrag; clickedPosition = getPositionFromElementId(marypoppins.id); clearRanges(evt); } } function miniqDrag(evt) { evt = (evt) ? evt : event; var topline = evt.pageY; if (marypoppins) { evt.cancelBubble = true; var neweventOffsetY = (evt.offsetY) ? evt.offsetY : evt.clientY; var moverTop = evt.clientY - TRIPPER_BUFFER_SIZE; var moverBottom = evt.clientY + TRIPPER_BUFFER_SIZE; // correct for the browser having been scrolled here var scrollAmt = getDocumentScrollAmount(); moverTop += scrollAmt; moverBottom += scrollAmt; tripperTop = moverTop; tripperBottom = moverBottom; var inBounds = (isMiniQueueItem(marypoppins)) && ( (tripperTop > currentEndingPoints[ID_TOP_BOUNDARY]) && ( tripperBottom < currentStartingPoints[ID_BOTTOM_BOUNDARY] ) ); if (inBounds) { var y = evt.pageY ? evt.pageY : (evt.clientY + document.body.scrollTop); if (isMiniQueueItem(marypoppins)) { var maxQueueItemsIndicatorId = getIndicatorDivId(MINIQ_AREA_MAIN, MAX_QUEUE_TO_SHOW); // Dragging up and sliding another item down for (var ii in currentEndingPoints) { if (ii != ID_TOP_BOUNDARY && ii != ID_BOTTOM_BOUNDARY && (ii != marypoppins.id) && (dragDirection != DIR_DOWN) && (ii != itemHoveredOver) && (tripperTop < currentEndingPoints[ii]) && (tripperBottom > currentEndingPoints[ii]) ) { dragDirection = DIR_UP; if (ii.indexOf(PREF_MINIQ_INDICATOR) != -1) { itemHoveredOver = ii; if (itemHoveredOver != maxQueueItemsIndicatorId || justAddedIndicatorCrossed == null) { hasCrossedIndicator = true; if (hasCrossedMovie && hasCrossedIndicator) { highlightIndicator(itemHoveredOver); } } else if (hasCrossedMovie && hasCrossedIndicator && itemHoveredOver == maxQueueItemsIndicatorId) { highlightIndicator(null); hasCrossedIndicator = false; } } else if (ii.indexOf(PREF_MINIQ) != -1) { hasCrossedMovie = true; } else if (ii.indexOf(ID_JUSTADDED_INDICATOR) != -1) { justAddedIndicatorCrossed = DIR_DOWN; } } } // Dragging down and sliding another item up for (var ii in currentStartingPoints) { if (ii != ID_TOP_BOUNDARY && ii != ID_BOTTOM_BOUNDARY && (ii != marypoppins.id) && (dragDirection != DIR_UP) && (ii != itemHoveredOver) && (tripperBottom > currentStartingPoints[ii]) && (tripperTop < currentStartingPoints[ii])) { dragDirection = DIR_DOWN; if (ii.indexOf(PREF_MINIQ_INDICATOR) != -1) { itemHoveredOver = ii; if (itemHoveredOver != maxQueueItemsIndicatorId || justAddedIndicatorCrossed == null) { hasCrossedIndicator = true; if (hasCrossedMovie && hasCrossedIndicator) { highlightIndicator(itemHoveredOver); } } else if (hasCrossedMovie && hasCrossedIndicator && itemHoveredOver == maxQueueItemsIndicatorId) { highlightIndicator(null); hasCrossedIndicator = false; } } else if (ii.indexOf(PREF_MINIQ) != -1) { hasCrossedMovie = true; } else if (ii.indexOf(ID_JUSTADDED_INDICATOR) != -1) { justAddedIndicatorCrossed = DIR_UP; } } } } } } clearRanges(evt); return false; } function miniqRelease(evt) { if (marypoppins && isDraggable(marypoppins)) { evt = (evt) ? evt : event; updatePoints(marypoppins.id); if (hasCrossedMovie && hasCrossedIndicator && isMiniQueueItem(marypoppins)) { var targetPosition = Number(getPositionFromElementId(itemHoveredOver)); var pickedItemByPosition = custq.getItem(clickedPosition); if (pickedItemByPosition.currentPosition < targetPosition) { targetPosition--; // if moving down the list, visual insert will actually be above indicator so highlight the right one } moveQueueItem(pickedItemByPosition, targetPosition); } else { var pickedItemByPosition = custq.getItem(clickedPosition); if (typeof pickedItemByPosition != "undefined" && pickedItemByPosition != null && pickedItemByPosition.wasAddedThisPage) { marypoppins.style.background = justAddedColor; } else { marypoppins.style.background = baseColor; } } marypoppins.style.zIndex = 1; innerOffsetY = 0; if (justAddedIndicatorCrossed != null && hasCrossedMovie && hasCrossedIndicator) { var formerTopJustAddedIndicator = document.getElementById(PREF_MINIQ_INDICATOR + firstDisplayedJustAddedQueueItem); var newLastTopSectionNumber = Number(formerTopJustAddedIndicator.innerHTML); if (justAddedIndicatorCrossed == DIR_UP) { replaceInnerText(formerTopJustAddedIndicator, new String(newLastTopSectionNumber-1)); } else if (justAddedIndicatorCrossed == DIR_DOWN) { replaceInnerText(formerTopJustAddedIndicator, new String(numDisplayedYourQueue+1)); } } clearRanges(evt); } tripperTop = 0; tripperBottom = 0; clickedPosition = -1; itemHoveredOver = null; dragDirection = null; movingItemDown = null; hasCrossedMovie = false; hasCrossedIndicator = false; justAddedIndicatorCrossed = null; highlightIndicator(null); marypoppins = null; document.onmouseup = null; document.onmousemove = null; } // END event handling // Page Setup document.onmousedown = miniqCapture; document.onmouseup = null; document.onmousemove = null; var custq = new Queue();