Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
業(yè)領(lǐng)先的.NET界面控件DevExpress Reporting全新發(fā)布了v19.1版本,本文主要為大家介紹WinForms、ASP.Net Core平臺、Visual Studio報表設(shè)計器中發(fā)布的新功能!
點擊“了解更多”獲取DevExpress Reporting v19.1下載
Visual Studio報表設(shè)計器
用于圖像屬性的Image Picker
現(xiàn)在使用Image Picker對話框在Visual Studio中編輯圖像屬性(例如XRPictureBox.ImageSource,XRCheckBox.CustomGlyphs),Image Picker將圖像存儲在項目資源文件(* .resx)中,并在不同的報表中共享它們,它還提供對光柵和矢量圖標(biāo)豐富集合的訪問。
ASP.Net Core Reporting
XRRichText Control
現(xiàn)在,可以在ASP.NET Core Reporting平臺內(nèi)使用XRRichText控件,它可以在Windows托管環(huán)境中使用。
Web報表設(shè)計器和HTML5文檔查看器 - 本地化
本地化服務(wù)現(xiàn)在提供JSON本地化文件。
TypeScript定義
現(xiàn)在,v19.1版本包括用于TypeScript的* .d.ts文件,因此您的開發(fā)環(huán)境中可以使用代碼完成功能。
Web報表設(shè)計器-自定義配置提供程序
使用此版本,您可以從各種配置源創(chuàng)建個性化配置,并將其傳遞給DevExpress Web報表設(shè)計器,您可以全局或僅在報表設(shè)計器中注冊配置。
WinForms Reporting
數(shù)據(jù)聯(lián)合
這個新的數(shù)據(jù)源使您可以組合多個報表數(shù)據(jù)源,并定義查詢和集合之間的聯(lián)接或關(guān)系:
可重復(fù)使用的Band模板
現(xiàn)在,您可以創(chuàng)建可重復(fù)使用的Band模板(頁面標(biāo)題、詳細(xì)信息、頁面頁腳等),并將其存儲在報表庫中。
報表設(shè)計器 - 外部拖放操作
Visual Studio報表設(shè)計器和WinForms最終用戶報表設(shè)計器現(xiàn)在支持使用資源管理器進行剪貼板復(fù)制/粘貼操作以及拖放操作:
增強最終用戶報表設(shè)計器功能
新報表的可配置默認(rèn)頁面。
現(xiàn)在,您可以在創(chuàng)建新報表時指定默認(rèn)頁面設(shè)置(通過'New Report' 或 'New Report via Wizard'命令),設(shè)置包括:
新選項由XRDesignMdiController.DefaultReportSettings屬性公開。
放大鏡對話框
使用新的放大鏡對話框在屏幕上的任何位置選擇RGB或十六進制顏色(從屬性面板中調(diào)用放大鏡)。
新頁面設(shè)置標(biāo)簽
在Properties Panel中引入一個新的標(biāo)簽,該新標(biāo)簽允許最終用戶快速配置給定報表的頁面設(shè)置。
通過工具提示顯示屬性表達(dá)式
最終用戶不在需要打開表達(dá)式編輯器來檢查哪個表達(dá)式分配給屬性,在此版本中,屬性表達(dá)式通過工具提示顯示。
XRChart - Segment Colorizers
現(xiàn)在,您可以使用以下segment colorizers來增強數(shù)據(jù)可視化。
打印重復(fù)的頁面和多個頁面范圍
現(xiàn)在,您可以指定要打印或?qū)С龅捻摯a和多個頁面范圍。 您可以指定重復(fù)的頁碼并打印同一頁的多個副本。
=======================================================
1024程序員節(jié)火熱開啟!致敬改變世界的程序猿!
https://www.devexpresscn.com/post/1424.html
DevExpress中文網(wǎng)官網(wǎng)QQ群:540330292 歡迎一起進群討論
開源精選》是我們分享Github、Gitee等開源社區(qū)中優(yōu)質(zhì)項目的欄目,包括技術(shù)、學(xué)習(xí)、實用與各種有趣的內(nèi)容。本期推薦的Atom 是一款適用于 21 世紀(jì)的多功能文本編輯器。
Sublime 和 TextMate 等編輯器提供了便利,但擴展性有限。另一方面,Emacs 和 Vim 提供了極大的靈活性,但它們不是很容易接受,只能使用特殊用途的腳本語言進行定制。
Web 瀏覽器非常適合瀏覽網(wǎng)頁,但編寫代碼是一項需要專用工具的專業(yè)活動。更重要的是,出于安全原因,瀏覽器嚴(yán)格限制了對本地系統(tǒng)的訪問,對我們來說,無法寫入文件或運行本地子進程的文本編輯器是行不通的。
出于這個原因,我們沒有將 Atom 構(gòu)建為傳統(tǒng)的 Web 應(yīng)用程序。相反,Atom 是 Chromium 的一種特殊變體,旨在成為文本編輯器而不是 Web 瀏覽器。每個 Atom 窗口本質(zhì)上都是一個本地呈現(xiàn)的網(wǎng)頁。
跨平臺編輯:Atom 可以跨操作系統(tǒng)工作。在 OS X、Windows 或 Linux 上使用它。
內(nèi)置包管理器:搜索并安裝新包或從 Atom 創(chuàng)建您自己的權(quán)限。
智能自動完成:Atom 通過智能且靈活的自動完成功能幫助您更快地編寫代碼。
文件系統(tǒng)瀏覽器:在一個窗口中輕松瀏覽和打開單個文件、整個項目或多個項目。
多個窗格:將您的 Atom 界面拆分為多個窗格,以跨文件比較和編輯代碼。
查找和替換:在文件或所有項目中鍵入時查找、預(yù)覽和替換文本。
安裝 Atom 應(yīng)該相當(dāng)簡單。通常,您可以訪問https://atom.io,您應(yīng)該會看到一個下載按鈕,如下所示:
在 Windows 上安裝 Atom
Atom 可用于 Windows 安裝程序,可從https://atom.io或Atom 發(fā)布頁面下載。用于AtomSetup.exe32 位系統(tǒng)和AtomSetup-x64.exe64 位系統(tǒng)。此安裝程序?qū)惭b Atom,將atom和apm命令添加到您的PATH.,并在桌面和開始菜單中創(chuàng)建快捷方式。
文件資源管理器中的上下文菜單Open with Atom以及使用 使 Atom 可用于文件關(guān)聯(lián)的選項Open with...由系統(tǒng)設(shè)置面板控制,如上所示。
打開 Atom,單擊File > Settings,然后單擊System左側(cè)的選項卡。選中 和 旁邊的Show in file context menus框Show in folder context menus。
更新Atom
在設(shè)置視圖的核心設(shè)置中默認(rèn)啟用“自動更新” ,這將允許 Atom 自動檢查更新。如果禁用此設(shè)置,您可以手動更新 Atom。
要執(zhí)行手動更新:
單擊Help > Check for Update菜單欄中的菜單項。
Application: About在命令面板中搜索并單擊Check now按鈕。
如果有可用更新,Atom 將開始更新。
便攜模式
Atom 將配置和狀態(tài)存儲在.atom通常位于您的主目錄(%userprofile%在 Windows 上)中的目錄中。但是,您可以在便攜模式下運行 Atom,其中應(yīng)用程序和配置都存儲在一起,例如在可移動存儲設(shè)備上。
要在便攜式模式下設(shè)置 Atom,請為您的系統(tǒng)下載 zip/tar.gz 包并將其解壓縮到可移動存儲中。然后.atom在包含 atom.exe 的目錄旁邊創(chuàng)建一個目錄,例如:
e:\atom-1.14\atom.exe
e:\.atom
便攜式筆記
原子選擇
Atom 中的文本選擇支持許多操作,例如范圍刪除、縮進和搜索操作,以及為引用和括號等操作標(biāo)記文本。
選擇反映了許多移動命令。它們實際上是與移動命令完全相同的鍵綁定,但Shift添加了一個鍵。
編輯和刪除文本
對于基本的文本操作,有一些很酷的鍵綁定可能會派上用場。這些范圍從移動文本行和復(fù)制行到更改大小寫。
Atom 還具有內(nèi)置功能,可以在給定的最大行長處重新排列段落以進行硬換行。您可以editor.preferredLineLength使用Alt+Ctrl+Q. 如果未選擇任何內(nèi)容,則將重排當(dāng)前段落。
多個光標(biāo)和選擇
Atom 可以開箱即用的一項很酷的事情是支持多個游標(biāo)。這對于處理長長的文本列表非常有幫助。
使用這些命令,您可以將光標(biāo)放置在文檔中的多個位置,并一次在多個位置有效地執(zhí)行相同的命令。
這對于執(zhí)行許多類型的重復(fù)性任務(wù)(例如重命名變量或更改某些文本的格式)非常有幫助。您可以將它與幾乎任何插件或命令一起使用 - 例如,更改大小寫以及移動或復(fù)制行。
拼寫檢查
如果您使用文本(默認(rèn)情況下包括純文本文件、GitHub markdown 和 Git 提交消息),Atom 將自動嘗試檢查您的拼寫。
任何拼寫錯誤的單詞都將突出顯示(默認(rèn)情況下,單詞下方有一條紅色虛線),您可以通過點擊Ctrl+Shift+;(或從右鍵單擊上下文菜單或命令中選擇“正確拼寫” )來拉出可能的更正菜單調(diào)色板)。
要將更多類型的文件添加到 Atom 將嘗試進行拼寫檢查的列表中,請轉(zhuǎn)到“設(shè)置”視圖中的“拼寫檢查”包設(shè)置,然后添加要進行拼寫檢查的任何語法。
拼寫檢查的默認(rèn)語法是text.plain, source.gfm, text.git-commit, source.asciidoc, source.rst,text.restructuredtext但如果您也想檢查這些類型的文件,您可以添加其他語法。
預(yù)覽
在使用標(biāo)記語言編寫散文時,了解內(nèi)容在呈現(xiàn)時的外觀通常非常有用。Atom 默認(rèn)附帶一個用于預(yù)覽 Markdown 的包。
當(dāng)您編輯文本時,預(yù)覽也會自動更新。這使得在鍵入時檢查語法變得相當(dāng)容易。
您還可以將預(yù)覽窗格中呈現(xiàn)的 HTML 復(fù)制到系統(tǒng)剪貼板中,當(dāng)預(yù)覽聚焦并按下Ctrl+C或右鍵單擊預(yù)覽窗格并選擇“復(fù)制為 HTML”時。
讀取配置設(shè)置
如果您正在編寫一個要使其可配置的包,則需要通過atom.config全局讀取配置設(shè)置。您可以使用以下命令讀取命名空間配置鍵的當(dāng)前值atom.config.get:
// read a value with `config.get`
if (atom.config.get("editor.showInvisibles")) {
this.showInvisibles()
}
或者您可以通過訂閱atom.config.observe來跟蹤來自任何視圖對象的更改。
const {View}=require('space-pen')
class MyView extends View {
function attached() {
this.fontSizeObserveSubscription=atom.config.observe('editor.fontSize', (newValue, {previous})=> {
this.adjustFontSize(newValue)
})
}
function detached() {
this.fontSizeObserveSubscription.dispose()
}
}
該atom.config.observe方法將立即使用指定鍵路徑的當(dāng)前值調(diào)用給定的回調(diào),并且將來只要該鍵路徑的值發(fā)生更改,它也會調(diào)用它。如果您只想在下次值更改時調(diào)用回調(diào),請atom.config.onDidChange改用。
編寫配置設(shè)置
數(shù)據(jù)庫從atom.config啟動時填充,但您可以通過以下方式以編程方式寫入它:%USERPROFILE%\.atom\config.csonatom.config.set
// basic key update
atom.config.set("core.showInvisibles", true)
Atom 在哪些平臺上運行?
Atom 的預(yù)構(gòu)建版本可用于 OS X 10.10 或更高版本、Windows 7 或更高版本、RedHat Linux 和 Ubuntu Linux。
安全模式有什么作用?
Atom 的安全模式可以通過完全退出 Atom 的所有實例并使用atom --safe命令行中的命令再次啟動它來激活,它執(zhí)行以下操作:
安全模式的目的是確定問題是由社區(qū)包引起還是由 Atom 的內(nèi)置功能引起。添加了禁用初始化腳本是因為人們傾向于通過添加代碼、命令和其他通常包含在程序包中的功能來將初始化腳本用作某種小型程序包。
如何讓 Atom 將擴展名為 X 的文件識別為語言 Y?
Atom 包含一個稱為“自定義文件類型”的功能,您可以通過在您的文件中添加一些條目來使用它config.cson,如下所示:
core:
customFileTypes:
'source.ruby': [
'Cheffile'
'this-is-also-ruby'
]
'source.cpp': [
'h'
]
關(guān)鍵(例如source.ruby在上面的代碼片段中)是語言的范圍名稱。該值是與該范圍名稱匹配的文件擴展名數(shù)組,不帶句點。
—END—
開源協(xié)議:MIT
開源地址:https://github.com/atom/atom
我們開發(fā)系統(tǒng)的時候,可能會接到這樣的需求:不要讓用戶復(fù)制頁面上的文字或者圖片,不要讓用戶調(diào)試我們的頁面,更甚至也不要讓用戶進行打印操作等等。
聽起來是不是讓人很頭大,這咋實現(xiàn)啊?這有必要嗎?這能禁住么?
如果你沒做過這些,或者沒接到過這樣的需求,那你也應(yīng)該看到過某個網(wǎng)站做了一些這樣的措施。
既然要做,我們就得想方案,先來看看禁止復(fù)制都有哪些方法。
假設(shè)我們有這樣一段代碼:
<div style="padding-left: 56px;">
<textarea rows="5" cols="33"></textarea>
</div>
<pre>
海客談瀛洲,煙濤微茫信難求,
越人語天姥,云霞明滅或可睹。
天姥連天向天橫,勢拔五岳掩赤城。
天臺四萬八千丈,對此欲倒東南傾。
我欲因之夢吳越,一夜飛度鏡湖月。
湖月照我影,送我至剡溪。
</pre>
接下來就通過這個例子來論述我們的方案:
x效果
這是一個css屬性,標(biāo)識了元素及其子元素的文本不可被選中,因此設(shè)定之后,文本將不能夠被選中,因此也就不能復(fù)制:
<pre style="user-select: none;">
海客談瀛洲,煙濤微茫信難求,
越人語天姥,云霞明滅或可睹。
天姥連天向天橫,勢拔五岳掩赤城。
天臺四萬八千丈,對此欲倒東南傾。
我欲因之夢吳越,一夜飛度鏡湖月。
湖月照我影,送我至剡溪。
</pre>
我們在這段文本上,加上這個樣式。
效果
可以看到,文字壓根就不能選擇,從鼠標(biāo)形狀也能看出來。
由于用在進行復(fù)制操作的時候,會觸發(fā)copy事件,我們可以通過監(jiān)聽它來做一些處理,使得復(fù)制的行為發(fā)生改變:
<div>
<div style="padding-left: 56px;">
<textarea rows="5" cols="33"></textarea>
</div>
<pre id="content">
海客談瀛洲,煙濤微茫信難求,
越人語天姥,云霞明滅或可睹。
天姥連天向天橫,勢拔五岳掩赤城。
天臺四萬八千丈,對此欲倒東南傾。
我欲因之夢吳越,一夜飛度鏡湖月。
湖月照我影,送我至剡溪。
</pre>
</div>
<script>
let c=document.getElementById('content')
c.removeEventListener("copy", copyFilter)
c.addEventListener("copy", copyFilter)
function copyFilter(e) {
let cp=e.clipboardData || window.clipboardData
if(!cp) {
return
}
let text=window.getSelection().toString()
if(text) {
e.preventDefault()
cp.setData("text/plain", "你復(fù)制了一段魔法")
}
}
</script>
先獲取到我們要禁止復(fù)制的元素,然后給它添加一個copy的事件監(jiān)聽,在添加監(jiān)聽之前,要先移除一下,這樣是為了避免局部刷新的時候重復(fù)添加,然后我們通過copyFilter函數(shù)來對這次操作進行處理。
先獲取剪貼板對象,如果當(dāng)前事件對象里不存在,那就從window里面取,然后我們通過getSelection再拿到選取的內(nèi)容,因為我們對剪貼板對象進行修改,所以要阻止默認(rèn)行為,然后把剪貼板的內(nèi)容重新賦值,可以是示例中那樣的一段文字,也可以設(shè)置為空,甚至是任意其他內(nèi)容,然后我們就可以看到產(chǎn)生的效果了:
效果
雖然能復(fù)制文本,但是由于我們攔截了復(fù)制操作,更改了它的行為,因此再粘貼的時候就變成了我們更改的樣子,也做到了禁止復(fù)制的功能。
這種方式對于使用快捷鍵或者右鍵的方式都是有效的。
這種情況主要是在可編輯區(qū)域,比如文本框、文本域、設(shè)置為contenteditable的元素等,用戶可以對文字進行剪切操作,雖然上面禁止了復(fù)制,但是剪切是另一個操作,不攔截的話還是相當(dāng)于能復(fù)制出來。
copy和cut只是觸發(fā)的事件不同而已,但是它們都是執(zhí)行相同的邏輯處理:
<div>
<div style="padding-left: 56px;">
<textarea rows="5" cols="33"></textarea>
</div>
<pre id="content" contenteditable>
海客談瀛洲,煙濤微茫信難求,
越人語天姥,云霞明滅或可睹。
天姥連天向天橫,勢拔五岳掩赤城。
天臺四萬八千丈,對此欲倒東南傾。
我欲因之夢吳越,一夜飛度鏡湖月。
湖月照我影,送我至剡溪。
</pre>
</div>
<script>
let c=document.getElementById('content')
c.removeEventListener("cut", copyFilter)
c.addEventListener("cut", copyFilter)
function copyFilter(e) {
let cp=e.clipboardData || window.clipboardData
if(!cp) {
return
}
let text=window.getSelection().toString()
if(text) {
e.preventDefault()
cp.setData("text/plain", "你復(fù)制了一段魔法")
}
}
</script>
這里我為了方便,給元素添加了contenteditable屬性,讓它變成可編輯的,copyFilter函數(shù)沒有變化,我們只是添加了一個剪切事件的監(jiān)聽,然后它們的處理函數(shù)都是copyFilter。看下效果:
效果
可以看到,首先我們對文字進行剪切,沒有出現(xiàn)預(yù)期的效果,這時因為我們在代碼里面對剪切進行了攔截,并阻止了它的默認(rèn)行為,然后我們在粘貼的時候,文字也改變成我們設(shè)置的了。
雖然我們可以通過上面的幾種方法禁止在頁面上復(fù)制,但是用戶也可能開啟打印預(yù)覽模式,在這種情況下,也是可以進行復(fù)制的,我們要想對打印頁面進行一些控制,那么就要用到媒體查詢,先看下打印的樣子:
效果
雖然我們做了限制,但是在打印頁面沒有生效,現(xiàn)在我們針對這個場景更改一下代碼:
@media print {
html {
display: none;
}
}
通過添加上面這個樣式規(guī)則,我們能夠使頁面在打印的時候,內(nèi)容隱藏起來,這樣就無法進行復(fù)制了:
效果
能夠看到,點擊打印的時候,預(yù)覽頁面一片空白,,這樣就禁止了在打印頁面進行復(fù)制的操作。當(dāng)然了,你其實也可以設(shè)置其他的樣式屬性來做些控制,但要記住寫在打印的媒體查詢里面,只有這樣才會在打印頁面生效。
還有一種方式就是,通過設(shè)定一個偽元素,讓它全面覆蓋文本內(nèi)容,這樣鼠標(biāo)就不能選到實際的文本,改造一下代碼:
.content {
position: relative;
}
.content::before {
content: '';
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
}
<pre id="content" class="content">
海客談瀛洲,煙濤微茫信難求,
越人語天姥,云霞明滅或可睹。
天姥連天向天橫,勢拔五岳掩赤城。
天臺四萬八千丈,對此欲倒東南傾。
我欲因之夢吳越,一夜飛度鏡湖月。
湖月照我影,送我至剡溪。
</pre>
現(xiàn)在就不能在元素上面選中文字了,不過用戶也可能還有一些操作,比如在內(nèi)容區(qū)域外面ctrl+a全選,或者在外面拖動鼠標(biāo)來全選,如果是這種情形,那么我們可以通過監(jiān)聽鍵盤和鼠標(biāo)事件來禁止全選等操作。
由于用戶有很多種操作的方式,鍵盤全選、鼠標(biāo)全選、鍵盤右鍵、鼠標(biāo)右鍵等等,我們?nèi)绻F舉的話,情況太多了,因此我們只監(jiān)聽鼠標(biāo)按下和抬起事件,以及鍵盤的按下事件:
document.removeEventListener("mousedown", haveSelect)
document.addEventListener("mousedown", haveSelect)
document.removeEventListener("mouseup", haveSelect)
document.addEventListener("mouseup", haveSelect)
document.removeEventListener("keydown", haveSelect)
document.addEventListener("keydown", haveSelect)
function haveSelect() {
window.getSelection().removeAllRanges()
}
主要就是在removeAllRanges方法上面,能夠在觸發(fā)上面事件的時候,將所選區(qū)域清空,也就是不管你選沒選,咋選的,反正就是你只要進行了操作,那我就那可能選擇的區(qū)域給你清空,這樣你就啥也干不了了。
效果
好,這樣就可以啦,無論怎么選,即使出現(xiàn)了選區(qū),但是只要你再按了鼠標(biāo)或者鍵盤,那么選區(qū)就會直接消失,就能達(dá)到不能復(fù)制的效果,因為你發(fā)現(xiàn)啥都做不了。
這里額外說明一下,對于事件的監(jiān)聽,一定要用addEventListener來實現(xiàn),因為它會將多個綁定的事件都添加上去,當(dāng)觸發(fā)的時候就會按照綁定的順序進行執(zhí)行,如果是用賦值的方式,那么后面的會覆蓋前面的,而且賦值的方式很容易被篡改,可以很輕松的讓你的綁定函數(shù)不能執(zhí)行從而失效,而使用addEventListener就不會被人為覆蓋,只能通過綁定的函數(shù)句柄來手動移除,也就是說要移除的時候,必須使用跟綁定時使用同一個函數(shù)才行。
通過CSS的方式禁止復(fù)制,可以很容易的被用戶解除,只能是設(shè)置的稍微復(fù)雜一點,增加難度。而通過JS的方式禁止復(fù)制,也可以通過禁用頁面JavaScript代碼來解除,因此我們可以將內(nèi)容通過js來渲染,這樣如果頁面禁用了js,那么內(nèi)容也不會渲染。
對于禁止調(diào)試,主要是指用戶打開控制臺,控制臺也就是開發(fā)者工具,我為了方便稱之為控制臺,想要對頁面進行調(diào)試時,我們做一些處理,阻止這種行為,最大可能的攔截通過控制臺對系統(tǒng)的調(diào)試。
主要的方法有幾種,由于打開控制臺是瀏覽器提供的調(diào)試功能,因此我們沒法攔截打開操作,即使通過事件監(jiān)聽不允許快捷鍵這樣做,但是也可以通過其他方式進行打開,因此我們的主要關(guān)注點就在于打開控制臺之后,我們能做哪些事情來限制用戶行為。
打開控制臺的快捷鍵主要有F12和ctrl+shift+i,我們先把這倆給禁用了:
document.removeEventListener("keydown", disableDevShortcut)
document.addEventListener("keydown", disableDevShortcut)
function disableDevShortcut(e) {
console.log(e)
if(e.keyCode===123) {
e.preventDefault()
}else if(e.keyCode===73 && e.ctrlKey && e.shiftKey) {
e.preventDefault()
}
}
這樣在使用這兩個快捷鍵的時候,頁面沒有任何反應(yīng),控制臺也不會喚起,因為我們阻止了它們的默認(rèn)行為。
除了通過快捷鍵,還可以使用右鍵的方式,并點擊檢查也會調(diào)出控制臺。
效果
這種情景,我們可以通過禁止在頁面上使用右鍵的方式,來阻止打開控制臺:
document.removeEventListener("contextmenu", cancelContextmenu)
document.addEventListener("contextmenu", cancelContextmenu)
function cancelContextmenu(e) {
e.preventDefault()
}
現(xiàn)在就不能通過右鍵打開控制臺了,但是相應(yīng)的整個右鍵功能也都不能使用了。
如果用戶最終打開了控制臺,比如通過在瀏覽器的更多功能中來打開的話,那么我就需要采取其他的措施,其中之一就是給代碼設(shè)置無限斷點,因為斷點只在控制臺打開的時候才會發(fā)生作用,從而不必?fù)?dān)心非調(diào)試模式下的程序正常運行。
無限斷點的主要思路就是利用定時器等手段,頻繁的觸發(fā)斷點效果,使得不能輕松的調(diào)試程序,先看下代碼:
;(()=> {
function breakDebugger() {
if(new checkDebugger().check) {
breakDebugger()
}
}
function checkDebugger() {
const now=new Date();
eval('(function () {debugger;false;})()')
const dur=Date.now() - now
if(dur < 5) {
return {check: false}
}else {
return {check: true}
}
}
setInterval(()=> {
eval('(function () {debugger;true;})()')
breakDebugger()
}, 500)
})()
我們利用一個立即執(zhí)行的自執(zhí)行函數(shù),來使我們的代碼被封裝在一個固定塊內(nèi),不與其他部分有任何影響。
這里主要做了兩步:
第一步設(shè)置一個重復(fù)執(zhí)行的定時器,其中包括了一個斷點和一個函數(shù)調(diào)用。
第二步通過函數(shù)來遞歸調(diào)用斷點,主要使用了實例化對象的方式和時間差的判斷。
這樣做的主要作用就是在設(shè)置無限斷點的同時,也能夠讓每次的斷點都是被重新生成的,看下效果,一目了然:
效果
發(fā)現(xiàn)沒有,我們通過這種方式,只要打開了控制臺,那么就會進入到無限斷點的循環(huán)中,使得不能做任何其他事情,而且每個斷點的生成都會開辟一個新的虛擬運行環(huán)境,這種情況下,只有關(guān)閉控制臺,才能結(jié)束斷點。
即使使用右鍵選擇Never parse here,也毫無作用,雖然可以通過Deactive breakpoints按鈕來徹底禁用斷點,就是下面這個按鈕:
按鈕
但是,如果這樣做的話,那么用戶也就同時失去了調(diào)試其他代碼的能力。
況且,我們接下來還會介紹其他的控制手段,可以配合著使用。
我們?nèi)绻苡幸环N手段,可以知道用戶開啟了控制臺,換句話說只要控制臺被打開,就通知我們或者被我們監(jiān)測到,那么我們就可以執(zhí)行一些控制手段,這種效果肯定是很理想的,遺憾的是還沒有這種api暴露給我們?nèi)プ屛覀兡軌蜻@樣做。
不過我們可以通過其他的方式,利用既有的一些能力來實現(xiàn)這一點,這里我還是使用循環(huán)定時器,來不斷的去嗅探用戶是否開啟了控制臺,直接看代碼:
;(()=> {
setInterval(function() {
let foo=document.createElement('a')
let a1=+new Date()
console.table(foo)
let a2=+new Date()
if(a2 - a1 > 1) {
location.href='about:blank'
}
console.clear()
}, 500)
})()
同樣,通過一個自執(zhí)行函數(shù),我們開啟了一個循環(huán)定時器,然后在回調(diào)方法里面,我們就去實現(xiàn)上面的目標(biāo),也是分為了兩步:
第一步創(chuàng)建一個a元素,然后通過表格的形式將它打印出來,并記錄下消耗的時間。
第二步判斷耗時的長短來控制是否跳轉(zhuǎn)到空白頁,然后清空控制臺。
這種方式主要是利用了console.table的特性,它會將元素以表格的形式輸出到控制臺,大概就像下面的樣子:
效果
由于太多了,我就沒有全部截下來,如果沒有打開控制臺的話,使用console.table輸出我們創(chuàng)建的a標(biāo)簽是很快的,有多快呢,就是js執(zhí)行一條語句的速度,所以打印a1和a2的時間間隔非常短,幾乎為0,因為他們快到差不多是同時執(zhí)行的,給大家打印看一下:
效果
我們先不打開控制臺,等輸出完畢再打開,很清楚的發(fā)現(xiàn),我們沒打開控制臺的時候,輸出的a就是它標(biāo)簽,而且時間間隔是0毫秒。
現(xiàn)在我們打開控制臺的時候刷新一下頁面,看看控制臺的輸出:
效果
這次就變成了以table的形式輸出a元素,而且它的耗時明顯增多,不再是0毫秒,而是耗費了10毫秒,雖然打開控制臺的時候多次刷新頁面,每次輸出的毫秒數(shù)是不同的,但是跟關(guān)閉控制臺的時候輸出的耗時差距非常明顯,因此我們就可以在這個上面做文章。
我在上面的代碼中假定了,只要是大于1毫秒的耗時,那就表示用戶打開了控制臺,然后我們就把頁面給跳轉(zhuǎn)到空白頁,當(dāng)然了你也可以做一些任何你想做的操作,比如彈出一個提示,或者把body內(nèi)容置空等等等等。
回到我們上面的代碼,看一下它實際發(fā)生的作用和帶給我們的效果:
效果
哈哈,古德古德,平時瀏覽一切正常,只要剛一打開控制臺,瞬間頁面就被跳轉(zhuǎn)走了,什么都干不了。這樣我們就通過這種方式,達(dá)到了限制打開控制臺的目的,也就是在當(dāng)前頁沒法調(diào)試,一打開就跳轉(zhuǎn)。
這種辦法由于是繞路實現(xiàn)的,那么你可能會有疑問,它穩(wěn)定嗎?會不會誤判,我可以對它絕對放心嗎?
理論上來說,通過輸出的執(zhí)行時間是不太能精確掌握的,但是我們可以再做一些其他的措施來逼近真相:
;(()=> {
setInterval(function() {
let foo=document.createElement('a')
let a1=+new Date()
console.table(foo)
let a2=+new Date()
if(a2 - a1 > 1) {
let time=0
for(let i=0; i < 10; i++) {
let a1=+new Date()
console.table(foo)
let a2=+new Date()
time +=a2 - a1
}
if(time > 20) {
location.href='about:blank'
}
}
console.clear()
}, 500)
})()
我又改造了一下判斷的邏輯,當(dāng)發(fā)現(xiàn)輸出耗時為2毫秒甚至更多的時候,我立馬再進行一次真?zhèn)闻袛啵簿褪钦f,萬一由于其他的影響,導(dǎo)致我第7行的代碼誤判了,那么我再同步執(zhí)行一個循環(huán),連續(xù)輸出10次,把他們的耗時總和計算出來,然后判斷是否大于20毫秒,如果還是耗時過高的話,那么就可以非常肯定的知道用戶是打開了控制臺,這個時候就可以放心的做一些處理了。
其實限制用戶行為的方法有很多很多,上面列出了一些主要的,多種方法還是要結(jié)合著使用。你也可以自由發(fā)揮,多使用一些其他的手段,也會增加用戶復(fù)制或者調(diào)試的難度,比如防止用戶重寫console的方法,或者清除所有定時器等。
甚至也可以將你的內(nèi)容繪制到canvas上面來防止復(fù)制,多加一些js的處理工作,防止禁用js的時候,我們的代碼不生效,只有在js可用的時候再去渲染內(nèi)容等。也可以在綜合考慮的情況下加上代碼混淆、代碼加密等措施。
話說回來,大家都是同路人,何必相互為難,哈哈哈,不過提這個需求的人也著實會為難我們,既然提了那就盡力去做,能做到什么程度,只能說是盡量做到極致。
希望上面的內(nèi)容能夠幫助到你,也希望能夠?qū)δ阌兴鶈l(fā)。
謝謝
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。