/*
Copyright 2015 Adobe Systems Incorporated.  All rights reserved. 
*/

/*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true, maxerr: 50 */
/*global window, rgba, DW_INSPECT_INSERT_POSITION, DW_LIVEEDIT_CONSTANTS, DW_INSPECT_CONST, DW_EXTENSION_EVENT, liveViewExtensionsObject, DW_VISUAL_DOM_POSITIONS, dwNotifyInspectSelectionChange, dwNotifyClickInInspect, DW_LiveEdit_SetInsertPosition, Node, dwSelectRun, dwGetInspectModeArgs*/

function CEFDWInspectModeUtil() {
    'use strict';

	// Log message
	this.Log = function (message) {
		if (window.writeDWLog) {
			window.writeDWLog(message);
		} else {
			// Can't write log, replace Log with dummy function
			this.Log = function (message) { };
		}
	};

	// Disable Indpect mode.
	this.disableInspect = function (inspectMode) {
		this.inspectMode = inspectMode;
		if (this.inspectMode === DW_INSPECT_CONST.InspectModeOff) {
			if (this.lastIFrame) {
				this.removeEventListenersFromLastIFrame();
			}

			var extensionIFrame = window.liveViewExtensionsObject.getExtensionIFrameById("InspectModeUI");
			if (extensionIFrame) {
				this.Log("handleDWInspectModeChange : hide Inspector");
				extensionIFrame.style.display = 'none';
                this.currentElement = null;
                this.lastInspectedId = null;
			}
		}
	};

	// Reset Inspect canvas
	this.resetInspector = function (iframeObj) {
		var viewPortSize = this.getViewPortSize();

		// Reset
		var obj = { "pageScaleFactor": 1,
			"deviceScaleFactor": window.devicePixelRatio,
			"pageZoomFactor": 1,
			"scrollX": window.scrollX,
			"scrollY": window.scrollY,
			"viewportSize": {"width": viewPortSize.width, "height": viewPortSize.height},
			"frameViewFullSize": {"width": 0, "height": 0}};

		iframeObj.contentWindow.reset(obj);

        iframeObj.contentWindow.setPlatform(this.platform);
	};

	// Get the Quad object for the given rectangle
	this.getQuadsForRect = function (rect) {
		var quads = [{"x": Math.floor(rect.left), "y": Math.floor(rect.top)},
					{"x": Math.floor(rect.right), "y": Math.floor(rect.top)},
					{"x": Math.floor(rect.right), "y": Math.floor(rect.bottom)},
					{"x": Math.floor(rect.left), "y": Math.floor(rect.bottom)}];
		return quads;
	};

	// Get Live view scroll bar width
	this.getScrollBarWidth = function () {
		if (this.scrollBarWidth) {
			return this.scrollBarWidth;
		}

		// Add dummy container and bigger inner element to
		// calculate scroll bar width.
		var inner = document.createElement('p');
		inner.style.width = "100%";
		inner.style.height = "200px";
        inner.style.padding = "0px";
        inner.style.margin = "0px";
        inner.style.border = "0px";

		var outer = document.createElement('div');
		outer.style.position = "absolute";
        outer.style.padding = "0px";
        outer.style.margin = "0px";
        outer.style.border = "0px";
		outer.style.top = "0px";
		outer.style.left = "0px";
		outer.style.visibility = "hidden";
		outer.style.width = "200px";
		outer.style.height = "150px";
		outer.style.overflow = "hidden";

		outer.appendChild(inner);
		document.body.appendChild(outer);

		// Calculate visible width of elements
		var w1 = inner.offsetWidth;
		outer.style.overflow = 'scroll';
		var w2 = inner.offsetWidth;
		if (w1 === w2) {
			w2 = outer.clientWidth;
		}

		// Remove dummy elements
		document.body.removeChild(outer);

		// Remember scroll bar width
		this.scrollBarWidth = (w1 - w2);

		return this.scrollBarWidth;
	};

	// Get Scroll bar state
	this.getScrollBarState = function () {
		var result = {vScrollbar: true, hScrollbar: true, hScrollbarHeight: 0, vScrollbarWidth: 0};
		try {
			var root = document.compatMode === 'BackCompat' ? document.body : document.documentElement;
			result.vScrollbar = root.scrollHeight > root.clientHeight;
			result.hScrollbar = root.scrollWidth > root.clientWidth;
			result.hScrollbarHeight = this.getScrollBarWidth();
			result.vScrollbarWidth = this.getScrollBarWidth();
		} catch (e) {}
		return (result);
	};

	// Get view port size
	this.getViewPortSize = function () {
		var scrollState = this.getScrollBarState(),
			viewPortSize = { height: window.innerHeight, width: window.innerWidth };

		if (scrollState.hScrollbar) {
			viewPortSize.height = (window.innerHeight - scrollState.hScrollbarHeight);
		}

		if (scrollState.vScrollbar) {
			viewPortSize.width = (window.innerWidth - scrollState.vScrollbarWidth);
		}

		return viewPortSize;
	};

	// Get Element Information.
	this.getElementInfo = function (element) {
		var info = { tagName: "", idValue: "", className: "", nodeWidth: "", nodeHeight: "" };
		if (!element) {
			return info;
		}

		info.tagName = element.tagName.toLowerCase();
		info.nodeWidth = element.offsetWidth.toString();
		info.nodeHeight = element.offsetHeight.toString();

		if (element.className) {
			info.className = "." + element.className.replace(" ", ".");
		}

		if (element.id) {
			info.idValue = element.id;
		}

		return info;
	};

	// Get Quads for element highlighting
	this.getElementQuads = function (element) {
		// Quad array
		var quadArray = [];

		if (!element) {
			return quadArray;
		}

		// Get Element style
		var style = element.currentStyle || window.getComputedStyle(element);

		//
		// Calculate Quads for node highlighting
		//
		var elemRect = element.getBoundingClientRect(),
			bodyElm = document.body;

		var padRect = {"top": (elemRect.top + bodyElm.scrollTop), "left": (elemRect.left + bodyElm.scrollLeft),
				"bottom": (elemRect.top + bodyElm.scrollTop + elemRect.height),
				"right": (elemRect.left + bodyElm.scrollLeft + elemRect.width) };
		var contRect = {"top": padRect.top + parseFloat(style.paddingTop), "left": padRect.left + parseFloat(style.paddingLeft),
				"bottom": padRect.bottom - parseFloat(style.paddingBottom), "right": padRect.right - parseFloat(style.paddingRight) };
		var borderRect = {"top": padRect.top - parseFloat(style.borderTop), "left": padRect.left - parseFloat(style.borderLeft),
				"bottom": padRect.bottom + parseFloat(style.borderBottom), "right": padRect.right + parseFloat(style.borderRight) };
		var marginRect = { "top": borderRect.top - parseFloat(style.marginTop), "left": borderRect.left - parseFloat(style.marginLeft),
				"bottom": borderRect.bottom + parseFloat(style.marginBottom), "right": borderRect.right + parseFloat(style.marginRight) };

		var marginQuad = this.getQuadsForRect(marginRect);
		var borderQuad = this.getQuadsForRect(borderRect);
		var padQuad = this.getQuadsForRect(padRect);
		var contQuad = this.getQuadsForRect(contRect);

		quadArray = [marginQuad, borderQuad, padQuad, contQuad, padQuad];

		return quadArray;
	};

    /*
    function:getElementRect - Returns the containing rect for the element in the document

    Arguments: elem - DOM element

    Return: containing rect(top, left, width, height) of the element
    */
    this.getElementRect = function (elem) {
        var rect = { top : 0, left : 0, width : 0, height : 0 };
        if (elem) {
            var boudingRect = elem.getBoundingClientRect();
            rect.top = boudingRect.top + window.scrollY;
            rect.left = boudingRect.left + window.scrollX;
            rect.width = boudingRect.right - boudingRect.left;
            rect.height = boudingRect.bottom - boudingRect.top;
        }
        return rect;
    };

	// Set Inspect extension into view visible rectangle
	this.setExtentionInViewRect = function (extensionIFrame) {
		if (!extensionIFrame) {
			return;
		}

		if (extensionIFrame.style.display === 'none') {
			extensionIFrame.style.display = 'block';
		}

        //if document body or html element is positioned relative
        //then their top left may not be (0,0). We should reduce this
        //in our computations
        var topOffset = 0, leftOffset = 0, documentHtmlStyle = null;
        var documentBodyStyle = window.getComputedStyle(document.body);
        var documentHTMLElement = document.getElementsByTagName("html")[0];
        if (documentHTMLElement) {
            documentHtmlStyle = window.getComputedStyle(documentHTMLElement);
        }
        if (documentBodyStyle.position === "relative") {
            var bodyRect = this.getElementRect(document.body);
            topOffset = bodyRect.top;
            leftOffset = bodyRect.left;
        } else if (documentHtmlStyle && documentHtmlStyle.position === "relative") {
            var htmlRect = this.getElementRect(documentHTMLElement);
            topOffset = htmlRect.top;
            leftOffset = htmlRect.left;
        }

		var bodyElm = document.body;

		extensionIFrame.style.padding = "0px";
		extensionIFrame.style.border = "0px";
		extensionIFrame.style.margin = "0px";
		extensionIFrame.style.overflow = "hidden";
		extensionIFrame.style.top = (bodyElm.scrollTop - topOffset) + "px";
		extensionIFrame.style.left = (bodyElm.scrollLeft - leftOffset) + "px";

		var viewPortSize = this.getViewPortSize();
		extensionIFrame.style.height = viewPortSize.height + "px";
		extensionIFrame.style.width = viewPortSize.width + "px";
	};

    // Get Position of Guide
    this.getGuidePosition = function (element, mouseX, mouseY) {
        if (!element) {
            return DW_INSPECT_INSERT_POSITION.none;
        }

        var point,
            curPos = DW_INSPECT_INSERT_POSITION.none,
            elemRect = element.getBoundingClientRect(),
            style = window.getComputedStyle(element);

        if (style.display === "inline" || style.display === "inline-block") {
            point = mouseX - elemRect.left;

            if (point < elemRect.width * 0.3) {
                curPos = DW_INSPECT_INSERT_POSITION.left;
            } else if (point < elemRect.width * 0.7) {
				curPos = DW_INSPECT_INSERT_POSITION.inside;
            } else {
                curPos = DW_INSPECT_INSERT_POSITION.right;
            }
        } else {
            point = mouseY - elemRect.top;
            if (point < elemRect.height * 0.3) {
                curPos = DW_INSPECT_INSERT_POSITION.top;
            } else if (point < elemRect.height * 0.7) {
				curPos = DW_INSPECT_INSERT_POSITION.inside;
            } else {
                curPos = DW_INSPECT_INSERT_POSITION.bottom;
            }
        }

        var positionString = "none";
        switch (curPos) {
        case DW_INSPECT_INSERT_POSITION.left:
        case DW_INSPECT_INSERT_POSITION.top:
            positionString = "before";
            break;
        case DW_INSPECT_INSERT_POSITION.right:
        case DW_INSPECT_INSERT_POSITION.bottom:
            positionString = "after";
            break;
        case DW_INSPECT_INSERT_POSITION.inside:
            positionString = "nest";
            break;
        }

        window.DW_LiveEdit_SetInsertPosition(positionString);

        return curPos;
    };

	// Get highlight configuration
	this.getHighlightConfig = function (element, mouseX, mouseY, position) {
		var config = {};

		if (this.inspectMode === DW_INSPECT_CONST.InspectModeMouseOver) {
			config.showRulers = true;
			config.contentColor = DW_INSPECT_CONST.contentColor;
			config.contentOutlineColor = DW_INSPECT_CONST.contentOutlineColor;
			config.paddingColor = DW_INSPECT_CONST.paddingColor;
			config.borderColor = DW_INSPECT_CONST.borderColor;
			config.marginColor = DW_INSPECT_CONST.marginColor;
			config.eventTargetColor = DW_INSPECT_CONST.eventTargetColor;
			config.guidePosition = 0;
		} else if (this.inspectMode === DW_INSPECT_CONST.InspectModeWithGuides
                   || this.inspectMode === DW_INSPECT_CONST.InspectModeWithoutGuides) {
			config.showRulers = false;
			config.contentColor = DW_INSPECT_CONST.guidGenericColor;
			config.contentOutlineColor = DW_INSPECT_CONST.guidContentOutlineColor;
			config.paddingColor = DW_INSPECT_CONST.guidGenericColor;
			config.borderColor = DW_INSPECT_CONST.guidGenericColor;
			config.marginColor = DW_INSPECT_CONST.guidGenericColor;
			config.eventTargetColor = DW_INSPECT_CONST.eventTargetColor;
			// guidePosition - position for feedback during drag insert. Refer WebSettingsInspectHighlightPosition.
			// TODO: Hardcoding it for now. Needs implementation.
            if (position === DW_INSPECT_INSERT_POSITION.none
                    && this.inspectMode === DW_INSPECT_CONST.InspectModeWithGuides) {
                position = this.getGuidePosition(element, mouseX, mouseY);
            }
			config.guidePosition = position;
		}

		return config;
	};

	// Draw node highlight in extension layer.
	this.drawHighlight = function (extension, drawConfig) {
		try {
			// TODO: some color difference when we send the drawConfig to drawNodeHighlight.
			//    Converting that to JSON string and converting back to object fixes that issue.
			//    Need to investingate what is the difference
			var str1 = JSON.stringify(drawConfig);
			drawConfig = JSON.parse(str1);
			extension.contentWindow.drawGutter();
			extension.contentWindow.drawNodeHighlight(drawConfig);
		} catch (e) {
			this.Log(e.message);
		}
	};

    // Convert visual DOM panel's selection position to live insert guid position.
    this.visDOMPositionToInsertGuidPostion = function (element, visDOMPosition) {
        // Set right guid position if this is call from Visual DOM
        var position = DW_INSPECT_INSERT_POSITION.none,
            style = window.getComputedStyle(element);

        if (visDOMPosition === DW_VISUAL_DOM_POSITIONS.Nest) {
            position = DW_INSPECT_INSERT_POSITION.inside;
        } else if (style.display === "inline" || style.display === "inline-block") {
            if (visDOMPosition === DW_VISUAL_DOM_POSITIONS.Before) {
                position = DW_INSPECT_INSERT_POSITION.left;
            } else if (visDOMPosition === DW_VISUAL_DOM_POSITIONS.After) {
                position = DW_INSPECT_INSERT_POSITION.right;
            }
        } else {
            if (visDOMPosition === DW_VISUAL_DOM_POSITIONS.Before) {
                position = DW_INSPECT_INSERT_POSITION.top;
            } else if (visDOMPosition === DW_VISUAL_DOM_POSITIONS.After) {
                position = DW_INSPECT_INSERT_POSITION.bottom;
            }
        }

        return position;
    };

    // Get the number of similar elements that present before the given element.
    this.getNumberOfSimilarElementsBefore = function (element, outerHTML, count) {
        if (!count) {
            count = 0;
        }

        if (!element) {
            return count;
        }

        if (element.nodeType === Node.ELEMENT_NODE) {

            // if no outerHTML arg, then this is the original element
            // and we need to search similar occurances before this element.
            if (!outerHTML) {
                // Check element has DW unique ID, if yes we don't need
                // to search for similar element occurance. 
                if (element.hasOwnProperty(DW_LIVEEDIT_CONSTANTS.DWUniqueId)) {
                    return 1;
                }

                outerHTML = element.outerHTML;
            }

            var currElemHTML = element.outerHTML;

            var startIndex = 0;
            while (true) {
                startIndex = currElemHTML.indexOf(outerHTML, startIndex);
                if (startIndex === -1) {
                    break;
                }
                count++;
                startIndex += outerHTML.length;
            }
        }

        var searchAt = element.previousSibling;
        if (searchAt) {
            count = this.getNumberOfSimilarElementsBefore(searchAt, outerHTML, count);
        } else if (element.parentNode && element.parentNode.previousSibling) {
            count = this.getNumberOfSimilarElementsBefore(element.parentNode.previousSibling, outerHTML, count);
        }

        return count;
    };

	// Mouse down handler for IFRame
    this.iFrameMouseDown = function (event) {
		if (!this.lastIFrame) {
			return;
		}

		this.removeEventListenersFromLastIFrame();
		this.inspectMouseDown(event);
	};

	// Add event handlers for IFrame element
	this.addEventListenersToIFrame =  function (element) {
		if (!element || this.lastIFrame === element || element.tagName !== "IFRAME") {
			return;
		}

		if (this.lastIFrame) {
			this.removeEventListenersFromLastIFrame();
		}

		var contentDocument = (element.contentWindow || element.contentDocument);
		if (contentDocument && contentDocument.document) {
			contentDocument = contentDocument.document;
		}

		if (!contentDocument) {
			return;
		}

		this.lastIFrame = element;

		contentDocument.addEventListener("mousedown", this.iFrameMouseDown.bind(this), false);
		contentDocument.addEventListener("mouseup", this.stopMouseEvent.bind(this), false);
        contentDocument.addEventListener("mouseclick", this.stopMouseEvent.bind(this), false);

		this.Log("Added Event Listeners to :" + this.lastIFrame);
	};

	// Remove event handlers from IFrame element
	this.removeEventListenersFromLastIFrame =  function () {
		if (this.lastIFrame) {
			var contentDocument = (this.lastIFrame.contentWindow || this.lastIFrame.contentDocument);
			if (contentDocument && contentDocument.document) {
				contentDocument = contentDocument.document;
			}

			if (!contentDocument) {
				this.lastIFrame = null;
				return;
			}

			contentDocument.removeEventListener("mousedown", this.iFrameMouseDown.bind(this));
			contentDocument.removeEventListener("mouseup", this.stopMouseEvent.bind(this));
			contentDocument.removeEventListener("mouseclick", this.stopMouseEvent.bind(this));

			this.Log("Removed Event Listeners to :" + this.lastIFrame);

			this.lastIFrame = null;
		}
	};

	// Inspect element
	this.inspectElement = function (element, mouseX, mouseY, position) {

		if (!element) {
			this.Log("inspectElement - No element target - invalid argument!!!");
			return;
		}

		var extensionIFrame = window.liveViewExtensionsObject.getExtensionIFrameById(DW_INSPECT_CONST.ExtID);
		if (!extensionIFrame) {
			this.Log("Inspect Extension is not loaded!!!");
			return;
		}

		try {
			if (this.inspectMode !== DW_INSPECT_CONST.InspectModeOff) {

				// Get Quads for node highlighting.
				var quadArray = this.getElementQuads(element);

				// Set Inspect extension into view visible rectangle
				this.setExtentionInViewRect(extensionIFrame);

				// Get Highlight properties
				var drawConfig = this.getHighlightConfig(element, mouseX, mouseY, position);
				drawConfig.quads = quadArray;

                if (this.inspectMode === DW_INSPECT_CONST.InspectModeMouseOver) {
                    drawConfig.elementInfo = this.getElementInfo(element);

					if (element !== this.lastIFrame) {
						if (this.lastIFrame) {
							this.removeEventListenersFromLastIFrame();
						}

						if (element.tagName === "IFRAME") {
							this.addEventListenersToIFrame(element);
						}
					}
                }

				// Reset Inspector
				this.resetInspector(extensionIFrame);

				// Draw Highlighting
				this.drawHighlight(extensionIFrame, drawConfig);

				// If we can calculate offset of element efficiently here, then do it here
				// and update the DW through callback dwNotifyInspectSelectionChange
				// or pass outer html in callback to render process to calculate offset.
				/*var source = document.documentElement.outerHTML;
				this.Log("element index at source: " + source.indexOf(element.outerHTML));
				this.Log("element length at source: " + element.outerHTML.length);
				this.Log("Source code:" + source);
				dwNotifyInspectSelectionChange(true, source.indexOf(element.outerHTML), source.indexOf(element.outerHTML) + element.outerHTML.length);*/

				// Remove marquee selection if any
                var	sel = window.getSelection();
                sel.removeAllRanges();

				// Old inspect offset based selection fails some times
				// Using unique id for making selection.
                var dwUniqueID = element.getAttribute(DW_LIVEEDIT_CONSTANTS.DWUniqueId);
                if (dwUniqueID) {
                    // For avoiding duplicate selection notification check
                    // last inspected node is same as current one.
                    if (dwUniqueID !== this.lastInspectedId) {
						dwSelectRun(dwUniqueID);
                    }
                } else {
					// If element is dynamic, falling back to offset based selection
					var selectRange = document.createRange();
					selectRange.selectNode(element);
					var selectionObject = window.getSelection();
					selectionObject.removeAllRanges();
					selectionObject.addRange(selectRange);
                    // We will calculate offsets here and pass them while sending selection change notification
                    var offsets = window.DW_GetOffsetsForCurrentSelection();
                    if (offsets.length === 0) // If the offset string is empty, use dummy values
                        offsets = "0,0";
                    var offsetArray= offsets.split(",");
                    // Notify selection change.
                    dwNotifyInspectSelectionChange(true, Number(offsetArray[0]), Number(offsetArray[1]));
					// Clear selection 
					selectionObject.removeAllRanges();
                }

                // Remember last inspected node
                this.lastInspectedId = dwUniqueID;

				return;
			}
		} catch (e) {
			this.Log(e.message);
		}
	};

	// For debugging/unittesting purpose: If any mismatch in target element when we track mouse move through
	// CEF vs JS event handling.
	/*this.myMouseMove = function (event) {
		if (this.inspectMode === DW_INSPECT_CONST.InspectModeOff) {
			return;
		}

		var element = event.target;
		this.Log("myMouseMove eventTarget:" + element);
		this.Log("myMouseMove eventTarget X and Y : " + event.clientX + "," + event.clientY);
		try {
			this.inspectElement(element, event.clientX, event.clientY);
		} catch (e) {
			this.Log(e.message);
		} finally {
			event.stopPropagation();
            event.preventDefault();
		}
	};*/

    // Refresh Inspect Highlight
    this.refreshHighlight = function () {
        var extensionIFrame = window.liveViewExtensionsObject.getExtensionIFrameById("InspectModeUI");
        if (this.currentElement && extensionIFrame) {
            try {
                extensionIFrame.style.display = 'none';
                this.inspectElement(this.currentElement, 0, 0, DW_INSPECT_INSERT_POSITION.none);
            } catch (e) {}
        }
    };

	// shouldHandleMouseClick - validate mouse position and return whether to handle in the 
    // context of live inspect.
	this.shouldHandleMouseClick = function (mouseX, mouseY, screenX, screenY) {

        try {
            var viewPortSize = this.getViewPortSize();

            if ((mouseX - screenX) <= viewPortSize.width && (mouseY - screenY) <= viewPortSize.height) {
                return true;
            }
        } catch (e) {}

        return false;
	};

    // Check and get valid user element. We get our IFrame as target some time
    // and need to hide and get valid target.
    this.checkNGetUserElement =  function (targetElement, clientX, clientY) {
        // Check whether the target element is DW extension.
        var oldState = [];
        while (targetElement && targetElement.tagName === "IFRAME") {
            if (window.liveViewExtensionsObject.getExtensionIFrameById(targetElement.id) !== targetElement) {
                break;
            }

            this.Log("target Element is \"DW\" extension");
            oldState.push({id: targetElement.id,  display: targetElement.style.display});
            targetElement.style.display = 'none';
            targetElement = document.elementFromPoint(clientX, clientY);
        }

        // Restore the DW extensions state.
        if (oldState.length) {
            var index;
            for	(index = 0; index < oldState.length; index++) {
                var st = oldState[index],
                    ext = window.liveViewExtensionsObject.getExtensionIFrameById(st.id);
                if (ext) {
                    ext.style.display = st.display;
                }
            }
        }

        return targetElement;
    };

    // Find target element for inspect highlighting from args. 
    this.findTargetElementFromArgs = function (args) {
        var targetElement = null;

        if (!args) {
            return targetElement;
        }

        if (args.inspectMode === DW_INSPECT_CONST.InspectModeLast) {
            return this.currentElement;
        }

        if (args.uniqueID) {
            // The attribute used for node identification.
            var attrName = DW_LIVEEDIT_CONSTANTS.DWUniqueId;
            if (!args.editable) {
                attrName = DW_INSPECT_CONST.DWInspectId;
            }

            var querySelectorString = '[' + attrName + '="' + args.uniqueID + '"]';
            try {
                var elems = document.querySelectorAll(querySelectorString);
                if (elems && elems.length > 0) {
                    targetElement = elems[0];
                }
            } catch (e) {
                this.Log("event Target:" + e.message);
                return false;
            }

            if (args.editable !== true && targetElement) {
                targetElement.removeAttribute(attrName);
            }

            if (!targetElement) {
                this.Log("findTargetElementFromArgs - No targetElement found for " + querySelectorString);
            }
        }

        if (!targetElement && args.inspectMode !== DW_INSPECT_CONST.InspectModeOff) {
            targetElement = document.elementFromPoint(args.clientX, args.clientY);
        }

        // Check whether the target element is DW extension.
        var oldState = [];
        while (targetElement && targetElement.tagName === "IFRAME") {
            if (window.liveViewExtensionsObject.getExtensionIFrameById(targetElement.id) !== targetElement) {
                break;
            }

            this.Log("target Element is \"DW\" extension");
            oldState.push({id: targetElement.id,  display: targetElement.style.display});
            targetElement.style.display = 'none';
            targetElement = document.elementFromPoint(args.clientX, args.clientY);
        }

        // Restore the DW extensions state.
        if (oldState.length) {
            var index;
            for	(index = 0; index < oldState.length; index++) {
                var st = oldState[index],
                    ext = window.liveViewExtensionsObject.getExtensionIFrameById(st.id);
                if (ext) {
                    ext.style.display = st.display;
                }
            }
        }

        return targetElement;
    };

	//
	// Handle Inspect on mouse move.
	// 
	this.dwInspectHandleMouseMove = function (args) {
        if (!args) {
            this.inspectMode = DW_INSPECT_CONST.InspectModeOff;
            this.Log("dwInspectHandleMouseMove - No Args");
            return;
        }

        // Calculate client X
        args.clientX = args.mouseX - args.screenX;
        args.clientY = args.mouseY - args.screenY;

        this.Log(JSON.stringify(args));
        var targetElement = this.findTargetElementFromArgs(args);
		if (targetElement) {

            if (args.inspectMode !== DW_INSPECT_CONST.InspectModeLast) {
                this.inspectMode = args.inspectMode;
            }

            // Remember the element that is being highlighted
            this.currentElement = targetElement;
            this.platform = args.platform;

            // Set right inspect mode if this is call from Visual DOM
            if (this.inspectMode === DW_INSPECT_CONST.InspectModeOff) {
                if (args.position === DW_VISUAL_DOM_POSITIONS.NoGuides) {
                    this.inspectMode = DW_INSPECT_CONST.InspectModeWithoutGuides;
                } else if (args.position !== DW_VISUAL_DOM_POSITIONS.NoInput) {
                    this.inspectMode = DW_INSPECT_CONST.InspectModeWithGuides;
                }
            }

            // Set right guid position if this is call from Visual DOM
            var position = this.visDOMPositionToInsertGuidPostion(targetElement, args.position);

			this.inspectElement(targetElement, args.clientX, args.clientY, position);
		} else {
            this.disableInspect(DW_INSPECT_CONST.InspectModeOff);
			return false;
		}

		return true;
	};

    // Scroll handler
	this.dwInspectHandleScroll = function (inspectMode) {
		if (this.inspectMode === DW_INSPECT_CONST.InspectModeOff) {
			return;
		}

        if (inspectMode !== DW_INSPECT_CONST.InspectModeLast) {
            this.inspectMode = inspectMode;
        }

		// Use last inspected element while scrolling.
        var currentElem = this.currentElement;
        if (!currentElem) {
            currentElem = document.body;
        }

		this.inspectElement(currentElem, 0, 0, DW_INSPECT_INSERT_POSITION.none);
	};

    // DW Message handler
	this.messageHandler = function (e) {
		if (e.data && e.data.type) {
            try {
                if (e.data.type === DW_EXTENSION_EVENT.INSPECT_MODE_CHANGE) {
                    if (e.data.mode === DW_INSPECT_CONST.InspectModeOff
                            && (this.inspectMode === DW_INSPECT_CONST.InspectModeWithGuides
                            || this.inspectMode === DW_INSPECT_CONST.InspectModeWithoutGuides)) {
                        this.disableInspect(DW_INSPECT_CONST.InspectModeOff);
                    }
                } else if (e.data.type === DW_EXTENSION_EVENT.ALL_EXTENSIONS_DISABLED) {
                    if (this.inspectMode === DW_INSPECT_CONST.InspectModeMouseOver
                              || this.inspectMode === DW_INSPECT_CONST.InspectModeLast) {
                        this.refreshHighlight();
                    }
                }
            } catch (err) {}
		}
	};

    // Mouse down handler
    this.inspectMouseDown = function (event) {
		if (this.inspectMode === DW_INSPECT_CONST.InspectModeOff) {
			return;
		}

        var viewPortSize = this.getViewPortSize();
        if (event.clientX > viewPortSize.width || event.clientY > viewPortSize.height) {
            this.Log("Mouse down on scroll bar...");
            return;
        }

		try {
            this.Log("Mouse down in Inspect..");
			dwNotifyClickInInspect();
		} catch (e) {
			this.Log(e.message);
		} finally {
			event.stopPropagation();
            event.preventDefault();
		}
	};

    // Mouse down handler
    this.stopMouseEvent = function (event) {
		if (this.inspectMode === DW_INSPECT_CONST.InspectModeOff) {
			return;
		}

		event.stopPropagation();
        event.preventDefault();
	};

    this.drawNodeHighlight = function (element, mouseX, mouseY, pos, inspectMode) {
        if (inspectMode === DW_INSPECT_CONST.InspectModeLast) {
            element = this.currentElement;
        } else {
            this.inspectMode = inspectMode;
            element = this.checkNGetUserElement(element, mouseX, mouseY);
        }

        if (!element) {
            return;
        }

        var args = dwGetInspectModeArgs();

        // Remember the element that is being highlighted
        this.currentElement = element;
        this.platform = args.platform;

        // Set right guid position if this is call from Visual DOM
        var position = this.visDOMPositionToInsertGuidPostion(element, pos);

        this.inspectElement(element, mouseX, mouseY, position);
    };

	/* Initializer */
	this.initialize = function () {
		this.inspectMode = DW_INSPECT_CONST.InspectModeOff;
		//window.addEventListener("mousemove", this.myMouseMove.bind(this), true);
		window.dwInspectHandleMouseMove = this.dwInspectHandleMouseMove.bind(this);
		window.disableInspect = this.disableInspect.bind(this);
		window.shouldHandleMouseClick = this.shouldHandleMouseClick.bind(this);
		window.dwInspectHandleScroll = this.dwInspectHandleScroll.bind(this);
        window.dwDrawNodeHighlight = this.drawNodeHighlight.bind(this);
        window.dwRefreshNodeHighlight = this.refreshHighlight.bind(this);
        window.addEventListener("message", this.messageHandler.bind(this), false);
        window.addEventListener("mousedown", this.inspectMouseDown.bind(this), false);
        window.addEventListener("mouseup", this.stopMouseEvent.bind(this), false);
        window.addEventListener("mouseclick", this.stopMouseEvent.bind(this), false);
	};
}

var dwInspectModeUtil = new CEFDWInspectModeUtil();
dwInspectModeUtil.initialize();
