TouchDevelop/editor/tipManager.ts

195 строки
6.7 KiB
TypeScript

///<reference path='refs.ts'/>
module TDev {
export interface Tip {
tick?: Ticks;
tickArg?: string;
title: string;
description: string;
el?: HTMLElement;
decl?: AST.Decl;
forceTop?:boolean;
forceBottom?:boolean;
}
export module TipManager {
var currentTipDiv: HTMLElement;
var currentTipTarget: HTMLElement;
var currentTip: Tip;
var scheduledTip: Tip;
// = { tick : Ticks.codeRun, title : 'run your script', description : "tap 'run' to see the code in action" };
export function showScheduled()
{
if (scheduledTip)
setTip(scheduledTip)
}
export function scheduleTip(tip: Tip)
{
setTip(null)
scheduledTip = tip;
}
export function isCurrent(tip: Tip) {
return currentTip == tip;
}
export function isScheduled()
{
return !!scheduledTip
}
export function isVisible()
{
return !!currentTip
}
export function setTip(tip: Tip)
{
var needsAnimation = tip && (!currentTip || currentTip.description != tip.description)
currentTip = tip;
scheduledTip = null;
update(needsAnimation)
}
export function update(needsAnimation = false) {
if (!currentTip) {
hideTip();
} else {
updateTip(needsAnimation);
}
}
function hideTip() {
if (currentTipDiv) {
currentTipDiv.removeSelf();
currentTipDiv = undefined;
}
if (currentTipTarget) {
currentTipTarget.setFlag("tipped", false);
currentTipTarget = undefined;
}
}
function isHidden(el : HTMLElement) {
var e = el;
while(e && e.tagName != 'BODY') {
if (!e || e.style.display == 'none') return true;
e = e.parentElement;
}
return false;
}
function updateTip(needsAnimation:boolean)
{
var tip = currentTip;
// special handling for case when back button is not visible
if (tip.tick === Ticks.wallBack && !TheEditor.host.showBackButton()) {
var root = document.getElementById('root');
placeTip(tip, -1, -1, root, root, true);
return;
}
var el = tip.el;
if (!el && tip.tick) el = document.getElementById('btn-' + Ticker.tickName(tip.tick) + (tip.tickArg ? tip.tickArg : ""));
if (!el && tip.decl) el = TheEditor.scriptNav.htmlForDecl(tip.decl)
if (!el || isHidden(el)) {
// special handling of hardware buttons
hideTip();
return;
}
// find the offset parent
var top = el.offsetTop;
var left = el.offsetLeft;
var width = el.offsetWidth
var parent = <HTMLElement>el.offsetParent;
while (parent) {
if (parent.id == 'root') break;
if ((<any>parent).scrollEnabled) break;
if (/sideTabScroll/.test(parent.className) || /scriptEditor|leftPaneContent/.test(parent.id))
break;
top += parent.offsetTop;
left += parent.offsetLeft;
parent = <HTMLElement>parent.offsetParent;
}
if (parent == null) parent = document.body;
if (width > SizeMgr.topFontSize * 5)
left += width / 3;
placeTip(tip, left, top, el, parent, needsAnimation);
if (currentTipTarget && currentTipTarget != el)
currentTipTarget.setFlag("tipped", false)
currentTipTarget = el
currentTipTarget.setFlag("tipped", true)
var tp = currentTipDiv
Util.setTimeout(1, () => {
if (tp == currentTipDiv) {
Util.ensureVisible(tp);
Util.ensureVisible(el, parent)
}
})
}
function placeTip(tip : Tip, left : number, top : number, el : HTMLElement, parent : HTMLElement, needsAnimation : boolean) {
// create new div on demand
if (currentTipDiv) currentTipDiv.removeSelf()
currentTipDiv = div('tip', div('tipInner'))
// placing it on tipInner doesn't really help
currentTipDiv.withClick(() => {
// Util.coreAnim("shakeTip", 500, currentTipDiv);
if (el)
Util.coreAnim("pulseTarget", 1500, el);
})
var innerTip = <HTMLElement>currentTipDiv.firstChild
currentTipDiv.className = 'tip';
innerTip.innerHTML = "";
var triangleClass = 'tip-';
// special handling for left hardware button
if (left == -1 && top == -1) {
currentTipDiv.style.bottom = '12px';
currentTipDiv.style.left = '25px';
currentTipDiv.style.right = '';
currentTipDiv.style.top = '';
tip.title = 'press the phone back button';
triangleClass += 'bl';
}
else {
if (tip.forceBottom || (!tip.forceTop && top < parent.clientHeight / 2)) {
currentTipDiv.style.bottom = '';
currentTipDiv.style.top = (top + 5 + el.clientHeight) + 'px';
triangleClass += 't';
}
else {
currentTipDiv.style.bottom = (parent.clientHeight - (top - 5)) + 'px';
currentTipDiv.style.top = '';
triangleClass += 'b';
}
if (left > parent.clientWidth/2) {
currentTipDiv.style.right = (parent.clientWidth - (left + el.clientWidth / 2 - 8)) + 'px';
currentTipDiv.style.left = '';
triangleClass += 'r';
}
else {
currentTipDiv.style.right = '';
currentTipDiv.style.left = (left + 8) + 'px';
triangleClass += 'l';
}
}
currentTipDiv.className += ' ' + triangleClass;
innerTip.setChildren([
div('tipTitle', tip.title),
div('tipDescr', tip.description)])
parent.appendChild(currentTipDiv);
if (needsAnimation)
Util.coreAnim("showTip", 1000, currentTipDiv);
dirAuto(currentTipDiv)
}
}
}