// ==UserScript== // @name extension for axure // @namespace http://tampermonkey.net/ // @version 1.2.0 // @description axure 原型页面辅助 // @author gkeeno // @match http://192.168.1.5:30032/* // @grant none // @run-at document-idle // @noframes // @downloadURL none // ==/UserScript== // axure增强 setTimeout(() => { if (self != top) return; // 不是顶层页面 if (RP_VERSION && RP_VERSION > 9) { loadFoldBtn(); // 折叠功能 loadFolderTextClick(); // 给文件夹的整个元素加上点击即toggle的功能 } else { console.warn('[extension for axure] 非axure页面或axure版本过高') } }, 1000) // TFS增强 setTimeout(() => { if (self != top) return; // 不是顶层页面 if (TFS) { loadTFSImageImprove(); } else { console.warn('[extension for axure] 非TFS页面') } }, 1000) // ******************************************************************************************* axure功能 BEGIN function loadFolderTextClick() { // 所有文件夹元素 const sitemapPageLinkContainers = document.querySelectorAll("div.sitemapPageLinkContainer"); for (const div of sitemapPageLinkContainers) { const toggleBtn = div.querySelector(".sitemapPlusMinusLink") if (toggleBtn) { // 判断是可折叠的页面,可以不用处理(原本点击页面三角仅触发加载页面和折叠) //const toggleSibling = toggleBtn.nextElementSibling //if(toggleSibling && toggleSibling.querySelector('.sitemapPageIcon')) continue; // 给整个文件夹元素添加点击事件,点击时触发左边三角形按钮的点击 div.addEventListener("click", function (e) { console.log("clicked") // 如果点击的是三角按钮本身,不需要处理,避免触发两次 if (e.target === toggleBtn || toggleBtn.contains(e.target)) { return; } e.stopPropagation(); // 模拟点击三角形按钮 toggleBtn.click(); }); // 设置鼠标样式为指针,提示用户可以点击 div.style.cursor = "pointer"; } } console.info('[extension for axure] 文件单击折叠已添加') } function loadFoldBtn() { const headerBtnMenu = document.querySelector("#sitemapToolbar"); // tips: 等1s后再添加较为安全(依据菜单数量) const foldMenuBtn = document.createElement("div"); foldMenuBtn.title = "折叠菜单"; foldMenuBtn.classList.add('sitemapToolbarButton'); foldMenuBtn.textContent = '🧺'; foldMenuBtn.onclick = function () { const userRes = prompt("从几级开始折叠?(最小为1级)", 2); if (!userRes) return; const level = Math.floor(Number(userRes)); if (isNaN(level) || level < 0) return alert("必须输入正整数"); foldLeftMenu(level); }; if (headerBtnMenu) { headerBtnMenu.insertBefore(foldMenuBtn, headerBtnMenu.firstChild); } console.info('[extension for axure] 折叠按钮已添加') } function foldLeftMenu(level) { const leftMenuTree = document.querySelector("#sitemapTreeContainer > ul.sitemapTree"); const levelFlag = { lvMax: 10, lvMin: level || 1, lvCur: 1, isExceed: function () { return this.lvCur > this.lvMax; }, completeFold: function () { this.lvCur++; } }; const nodeList = Array.from(leftMenuTree.querySelectorAll(":scope > .sitemapNode")); foldAllNodeByNodeList(nodeList, levelFlag, []); } function foldAllNodeByNodeList(nodeList, flag, foldCallBacks) { if (flag.isExceed()) { executedAllCallBacks(foldCallBacks); return; } const nextFoldNodeList = []; const needSkipLevel = isSkipLevel(flag); nodeList.forEach(node => { const subNodes = Array.from(node.querySelectorAll(":scope > ul > .sitemapNode")); nextFoldNodeList.push(...subNodes); if (!needSkipLevel) { foldCallBacks.push(() => foldNode(node)); } }); flag.completeFold(); if (nextFoldNodeList.length === 0) { executedAllCallBacks(foldCallBacks); return; } foldAllNodeByNodeList(nextFoldNodeList, flag, foldCallBacks); } function foldNode(node) { const foldBtn = node.querySelector(":scope > div > div.sitemapPageLinkContainer .sitemapPlusMinusLink"); if (!foldBtn) return; const isFolded = foldBtn.querySelector(".sitemapPlus"); if (isFolded) return; foldBtn.click(); } function executedAllCallBacks(arrCallBacks) { arrCallBacks.reverse().forEach(cb => cb()); } function isSkipLevel(flag) { return flag.lvCur < flag.lvMin; } // ******************************************************************************************* axure功能 END // ******************************************************************************************* TFS功能 BEGIN function loadTFSImageImprove() { const iframes = document.querySelectorAll('iframe'); // 遍历所有iframe iframes.forEach(iframe => { try { // 确保iframe已加载并且可以访问 if (iframe.contentDocument) { // 给iframe中的所有图片添加点击事件 addClickHooksToImages(iframe.contentDocument); // 给iframe添加load事件,以防iframe内容在后期加载 iframe.addEventListener('load', function () { try { addClickHooksToImages(this.contentDocument); } catch (e) { console.warn('Error adding hooks to iframe on load:', e); } }); // 监视iframe内部DOM变化 observeIframeChanges(iframe); } } catch (e) { // 处理跨域iframe的访问错误 console.warn('[TFS Image Hook] Cannot access iframe content due to same-origin policy:', e); } }); // 监听页面上动态添加的iframe observeDocumentForNewIframes(); // 新增: 监听评论框的变化 observeCommentBoxChanges(); console.info('[extension for axure] TFS 富文本图片单击浏览已添加') } // 给指定文档中的所有图片添加点击事件 function addClickHooksToImages(doc) { if (!doc) return; const images = doc.querySelectorAll('img'); images.forEach(img => { img.setAttribute('data-hook-added', 'true'); // 添加鼠标悬停样式,表明图片可点击 img.style.cursor = 'pointer'; // 添加点击事件 img.addEventListener('click', function (e) { e.preventDefault(); e.stopPropagation(); // 在这里实现您想要的点击功能 // 例如:显示大图、复制图片链接等 handleImageClick(this); }); }); } // 处理图片点击事件 function handleImageClick(img) { // 示例功能:在新窗口中打开大图 const imgSrc = img.src; const imgAlt = img.alt || 'Image'; // 创建一个模态框来显示大图 const modal = document.createElement('div'); modal.style.position = 'fixed'; modal.style.top = '0'; modal.style.left = '0'; modal.style.width = '100%'; modal.style.height = '100%'; modal.style.backgroundColor = 'rgba(0,0,0,0.8)'; modal.style.display = 'flex'; modal.style.justifyContent = 'center'; modal.style.alignItems = 'center'; modal.style.zIndex = '10000'; // 创建大图 const largeImg = document.createElement('img'); largeImg.src = imgSrc; largeImg.style.maxWidth = '90%'; largeImg.style.maxHeight = '90%'; largeImg.style.objectFit = 'contain'; largeImg.alt = imgAlt; // 创建关闭按钮 const closeBtn = document.createElement('div'); closeBtn.textContent = '×'; closeBtn.style.position = 'absolute'; closeBtn.style.top = '20px'; closeBtn.style.right = '20px'; closeBtn.style.color = 'white'; closeBtn.style.fontSize = '30px'; closeBtn.style.cursor = 'pointer'; // 点击模态框背景或关闭按钮时关闭 modal.addEventListener('click', function () { document.body.removeChild(modal); document.removeEventListener('keydown', escKeyHandler); }); // 阻止点击图片时关闭模态框 largeImg.addEventListener('click', function (e) { e.stopPropagation(); }); // 添加ESC键监听器退出大图 const escKeyHandler = function (e) { if (e.key === 'Escape' || e.keyCode === 27) { document.body.removeChild(modal); document.removeEventListener('keydown', escKeyHandler); } }; document.addEventListener('keydown', escKeyHandler); // 添加元素到模态框 modal.appendChild(largeImg); modal.appendChild(closeBtn); // 添加模态框到页面 document.body.appendChild(modal); } // 监视iframe内部DOM变化 function observeIframeChanges(iframe) { try { if (iframe.contentDocument) { const observer = new MutationObserver(function (mutations) { // DOM变化时,重新给所有图片添加点击事件 addClickHooksToImages(iframe.contentDocument); }); observer.observe(iframe.contentDocument.body, { childList: true, subtree: true }); } } catch (e) { console.log('Cannot observe iframe content due to same-origin policy:', e); } } // 监视页面上动态添加的iframe function observeDocumentForNewIframes() { const observer = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { if (!mutation.addedNodes) return; mutation.addedNodes.forEach(function (node) { // 如果添加的是iframe if (node.nodeName !== 'IFRAME') return; // 给新iframe添加load事件 node.addEventListener('load', function () { try { addClickHooksToImages(this.contentDocument); observeIframeChanges(this); } catch (e) { console.log('Error processing dynamically added iframe:', e); } }); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); } // 新增: 处理评论框中的图片 function processCommentBoxImages() { // 获取所有评论框 const commentBoxes = document.querySelectorAll('div.discussion-messages-container > div.discussion-messages > div:nth-child(1) > div.discussion-messages-right > div.discussion-messages-messagecontent'); commentBoxes.forEach(commentBox => { // 找到评论框中的所有图片 const images = commentBox.querySelectorAll('img'); // 为每个图片添加点击事件 images.forEach(img => { if (!img.dataset.hookAdded) { img.dataset.hookAdded = 'true'; img.style.cursor = 'pointer'; img.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); console.log("Comment box image clicked:", this.src); handleImageClick(this); }); } }); }); } // 新增: 观察评论框变化 function observeCommentBoxChanges() { // 获取评论容器 const commentsContainer = document.querySelector('div.discussion-messages-container'); // 如果找不到评论容器,可能页面还没加载完或者不在评论页面 if (!commentsContainer) { console.log("Comments container not found, will retry later"); // 稍后再尝试 setTimeout(observeCommentBoxChanges, 2000); return; } // 创建一个观察器实例 const observer = new MutationObserver(function(mutations) { // 当评论框内容变化时,重新处理图片 processCommentBoxImages(); }); // 配置观察选项 const config = { childList: true, subtree: true, attributes: false, characterData: false }; // 开始观察 observer.observe(commentsContainer, config); // 立即处理一次当前的评论框图片 processCommentBoxImages(); } // ******************************************************************************************* TFS功能 END