Wiktionary
viwiktionary
https://vi.wiktionary.org/wiki/Wiktionary:Trang_Ch%C3%ADnh
MediaWiki 1.47.0-wmf.15
case-sensitive
Phương tiện
Đặc biệt
Thảo luận
Thành viên
Thảo luận Thành viên
Wiktionary
Thảo luận Wiktionary
Tập tin
Thảo luận Tập tin
MediaWiki
Thảo luận MediaWiki
Bản mẫu
Thảo luận Bản mẫu
Trợ giúp
Thảo luận Trợ giúp
Thể loại
Thảo luận Thể loại
Phụ lục
Thảo luận Phụ lục
TimedText
TimedText talk
Mô đun
Thảo luận Mô đun
Event
Event talk
vốn
0
93465
2513878
2419253
2026-08-14T02:15:29Z
~2026-44581-94
61744
xóa thông tin người không liên quan
2513878
wikitext
text/x-wiki
{{-vie-}}
{{-etym-}}
{{vi-etym-sino|hv=n|本||[[bản]]/[[bổn]]}}. {{doublet|vi|bổn|bản}}.
{{-pron-}}
{{vi-pron}}
{{-nôm-}}
{{đầu}}
*[[呠]]: [[nhổn]], [[bõm]], [[bỏm]], [[bọn]], [[vốn]]
*[[本]]: [[bổn]], [[thao]], [[bỏn]], [[bản]], [[bốn]], [[bủn]], [[vỏn]], [[bộn]], [[vốn]]
*[[]]: [[vốn]]
{{mid}}
*[[夲]]: [[bổn]], [[thao]], [[bản]], [[vốn]]
*[[板]]: [[bẩng]], [[phản]], [[bổn]], [[bửng]], [[ván]], [[bản]], [[vốn]]
*[[𢏿]]: [[vốn]]
{{cuối}}
{{-paro-}}
:* [[vờn]]
{{-noun-}}
{{vie-noun}}
# [[tiền|Tiền]] [[gốc]] [[bỏ]] vào một cuộc [[kinh doanh]].
#: ''Các doanh nghiệp có '''vốn''' đầu tư nước ngoài.''
#: ''Chị ta đi buôn mà mất cả '''vốn''' lẫn lãi.''
# [[cái|Cái]] [[do]] [[trí tuệ]] [[tích lũy]].
#: ''Phải phục hồi, khai hoá, phát huy '''vốn''' cổ ([[w:Phạm Văn Đồng|Phạm Văn Đồng]])''
#: '''''Vốn''' văn chương dân gian.''
#: '''''Vốn''' ngoại ngữ của anh ấy cũng khá.''
# [[trgt|Trgt]] [[Nguyên]] là.
#: ''Nguyên người quanh quất đâu xa, họ.''
#: ''Kim, tên.''
#: ''Trọng, '''vốn''' nhà trâm anh (<u>[[w:Truyện Kiều|Truyện Kiều]]</u>)''
#: ''Vấn đề giải quyết việc làm '''vốn''' đã bức xúc càng thêm gay gắt ([[w:Phan Văn Khải|Phan Văn Khải]])''
{{-ref-}}
{{R:FVDP}}
{{R:WinVNKey:Lê Sơn Thanh}}
5x1nzklfw0eijujqtg0okivof37li44
Bản mẫu:gl
10
107778
2513979
2388463
2026-08-14T05:27:54Z
TheHighFighter2
42988
/* */
2513979
wikitext
text/x-wiki
#redirect [[Bản mẫu:gloss]]
1qjtaxe76s6aamzn7gr9hzhpi9o5uti
assignment operator
0
177652
2513870
2452054
2026-08-13T15:59:00Z
Hiyuune
50834
2513870
wikitext
text/x-wiki
=={{langname|en}}==
==={{section|n}}===
{{en-noun}}
# {{lb|en|computing}} [[toán tử|Toán tử]] [[gán]], [[toán tử]] [[chỉ định]].
===={{section|hyper}}====
* {{l|en|operator}}
===={{sectio|hypo}}====
{{col2
|en|augmented assignment operator
|compound assignment operator
}}
==={{section|ref}}===
* {{R:FVDP|db=ev}}
aeopzjlt0o98bsn932b4kpae7efsc83
2513871
2513870
2026-08-13T15:59:13Z
Hiyuune
50834
2513871
wikitext
text/x-wiki
=={{langname|en}}==
==={{section|n}}===
{{en-noun}}
# {{lb|en|computing}} [[toán tử|Toán tử]] [[gán]], [[toán tử]] [[chỉ định]].
===={{section|hyper}}====
* {{l|en|operator}}
===={{section|hypo}}====
{{col2
|en|augmented assignment operator
|compound assignment operator
}}
==={{section|ref}}===
* {{R:FVDP|db=ev}}
1y01ue7ol22ft8rurtmluit7qhi7u5r
阿尔法粒子
0
213996
2513823
1988759
2026-08-13T13:08:40Z
Kelly zhrm
58416
2513823
wikitext
text/x-wiki
=={{langname|zh}}==
{{zh-see|阿爾法粒子}}
5be31zx6s4g06arheseej3l7j8k15a2
禁用
0
215678
2513853
1987903
2026-08-13T15:35:05Z
Hiyuune
50834
2513853
wikitext
text/x-wiki
=={{langname|zh}}==
==={{section|etym}} 1===
{{zh-forms|bị cấm}}
===={{section|pron}}====
{{zh-pron
|m=jìnyòng
|c=gam3 jung6
|mn=tw:kìm-iōng
|cat=v
}}
===={{section|v}}====
{{head|zh|Động từ}}
# [[cấm|Cấm]] [[sử dụng]].
#: {{zh-x|禁用 手機。|Cấm sử dụng điện thoại.}}
# {{lb|zh|computing}} [[vô hiệu hóa|Vô hiệu hóa]], [[tắt]].
====={{section|ant}}=====
* {{as|vô hiệu hóa}} {{zh-l|啟用}}
==={{section|etym}} 2===
{{zh-forms|chịu đựng, chống chịu|alt=襟用-Quảng Đông}}
===={{section|pron}}====
{{zh-pron
|c=kam1 jung6
|cat=a
}}
===={{section|adj}}====
{{head|zh|Tính từ}}
# {{lb|zh|Cantonese}} [[bền|Bền]]; [[bền]] [[chắc]], chịu [[ma sát]] [[tốt]].
====={{section|syn}}=====
* {{zh-l|耐用}}
ebue9xci82bka7zy9inty7qrse5s9fq
2513854
2513853
2026-08-13T15:35:52Z
Hiyuune
50834
/* Tiếng Trung Quốc */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2513854
wikitext
text/x-wiki
=={{langname|zh}}==
==={{section|etym}} 1===
{{zh-forms|bị cấm|dùng, sử dụng}}
===={{section|pron}}====
{{zh-pron
|m=jìnyòng
|c=gam3 jung6
|mn=tw:kìm-iōng
|cat=v
}}
===={{section|v}}====
{{head|zh|Động từ}}
# [[cấm|Cấm]] [[sử dụng]].
#: {{zh-x|禁用 手機。|Cấm sử dụng điện thoại.}}
# {{lb|zh|computing}} [[vô hiệu hóa|Vô hiệu hóa]], [[tắt]].
====={{section|ant}}=====
* {{as|vô hiệu hóa}} {{zh-l|啟用}}
==={{section|etym}} 2===
{{zh-forms|chịu đựng, chống chịu|alt=襟用-Quảng Đông}}
===={{section|pron}}====
{{zh-pron
|c=kam1 jung6
|cat=a
}}
===={{section|adj}}====
{{head|zh|Tính từ}}
# {{lb|zh|Cantonese}} [[bền|Bền]]; [[bền]] [[chắc]], chịu [[ma sát]] [[tốt]].
====={{section|syn}}=====
* {{zh-l|耐用}}
agc875uew5w50fdgy3znbwe91qzbko9
琉璃瓦
0
215736
2513855
1987707
2026-08-13T15:37:01Z
Hiyuune
50834
2513855
wikitext
text/x-wiki
=={{langname|zh}}==
{{zh-forms|type=21}}
==={{section|pron}}===
{{zh-pron
|m=liúlíwǎ
|c=lau4 lei1 ngaa5
|cat=n
}}
==={{section|n}}===
{{head|zh|Danh từ}}
# {{w|Ngói lưu ly}}.
qkiesgq1bojk76xcu52x44gdul7xeyv
2513856
2513855
2026-08-13T15:37:27Z
Hiyuune
50834
/* {{langname|zh}} */
2513856
wikitext
text/x-wiki
=={{langname|zh}}==
{{zh-forms|type=21||gạch, ngói}}
==={{section|pron}}===
{{zh-pron
|m=liúlíwǎ
|c=lau4 lei1 ngaa5
|cat=n
}}
==={{section|n}}===
{{head|zh|Danh từ}}
# {{w|Ngói lưu ly}}.
f7w7twq4n5tuiaytqhhgo93n9bhdkkg
固体
0
215760
2513862
1986737
2026-08-13T15:48:46Z
Hiyuune
50834
2513862
wikitext
text/x-wiki
{{also|固體}}
=={{langname|zh}}==
{{zh-see|固體}}
g7l7htbnkbgbve8mofuzhin7mlscgd5
2513864
2513862
2026-08-13T15:51:39Z
Hiyuune
50834
/* Tiếng Trung Quốc */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2513864
wikitext
text/x-wiki
{{also|固體}}
=={{langname|ja}}==
{{ja-kanjitab|こ|たい|yomi=o}}
==={{section|pron}}===
* {{ja-pron|こたい|acc=0|acc_ref=NHK}}
==={{section|n}}===
{{ja-noun|こたい}}
# Thể [[rắn]].
===={{section|rel}}====
* {{ja-r|液体|えきたい|gloss=[[chất lỏng]]}}
* {{ja-r|気体|きたい|gloss=[[chất khí]]}}
==={{section|ref}}===
<references/>
:* {{R:Kanjipedia Kotoba|0002034600}}
{{C|ja|Vật lý học|sort=こたい}}
=={{langname|zh}}==
{{zh-see|固體}}
9q3c3ksapf7qtwuo5sr7wbf025o1uvz
阿拉斯加州
0
233584
2513790
1988768
2026-08-13T12:01:30Z
Hiyuune
50834
2513790
wikitext
text/x-wiki
=={{langname|zh}}==
{{zh-forms|type=41}}
==={{section|pron}}===
{{zh-pron
|m=Ālāsījiā zhōu
|c=aa3 laai1 si1 gaa1 zau1
|cat=pn
}}
==={{section|pr-noun}}===
{{head|zh|Danh từ riêng}}
# [[Alaska]].
r7x4q2irpw90ho0afte9pyzk532zhox
2513791
2513790
2026-08-13T12:02:17Z
Hiyuune
50834
/* {{langname|zh}} */
2513791
wikitext
text/x-wiki
=={{langname|zh}}==
{{zh-forms|type=41}}
{{wp|zh:}}
==={{section|pron}}===
{{zh-pron
|m=Ālāsījiā zhōu
|c=aa3 laai1 si1 gaa1 zau1
|cat=pn
}}
==={{section|pr-noun}}===
{{head|zh|Danh từ riêng}}
# [[Alaska]].
1m3ts8ui6qdk3za8tse7ofu8iutefkk
Thể loại:Tiếng Việt Nam Việt Nam
14
240326
2513844
2462632
2026-08-13T15:00:00Z
Hiyuune
50834
2513844
wikitext
text/x-wiki
{{auto cat|lect=1|lang=vi|cat=Tiếng Việt Việt Nam|miền Nam Việt Nam}}
atgdg1b7kgifh55qn3k1llzoka1jwt3
Nhật Bản
0
246116
2513828
2320273
2026-08-13T13:21:35Z
Hiyuune
50834
/* Dịch */
2513828
wikitext
text/x-wiki
=={{langname|vi}}==
[[File:Flag of Japan.svg|thumb|240px|Quốc kỳ '''Nhật Bản''']]
[[Image:Japan satellite.jpg|thumb|240px|Ảnh chụp vệ tinh '''Nhật Bản''']]
{{wp}}
==={{section|alt}}===
*{{alt|vi|Nhật-Bản|Nhật-bản|Nhật Bổn|Nhật-Bổn|Nhật-bổn|Nhựt Bản|Nhựt-Bản|Nhựt-bản|Nhựt Bổn|Nhựt-Bổn|Nhựt-bổn}}
==={{section|etym}}===
{{vi-etym-sino|日本}}, từ {{bor|vi|ja|日本|tr=Nippon, Nihon}}.
==={{section|pron}}===
{{vi-pron}}
==={{section|pr-noun}}===
{{vi-pr-noun|head=[[Nhật]] [[bản|Bản]]}}
# {{senseid|vi|Q17}}{{place|vi|quốc gia|r/Đông Á|caplc=Tokyo}}.
#: {{ux|vi|Quốc hội '''Nhật Bản'''.}}
#: {{ux|vi|Biển '''Nhật Bản'''.}}
#: {{ux|vi|Rất nhiều thực tập sinh Việt Nam bị trục xuất khỏi '''Nhật Bản''', nguyên nhân chủ yếu là do vi phạm pháp luật như trộm cắp, cư trú và làm việc bất hợp pháp hoặc không hợp tác với cảnh sát địa phương.}}
#* {{w|Phan Chu Trinh|Phan Châu Trinh}} ''{{lang|vi|Đạo đức và luân lí Đông Tây}}'':
#*: {{uxi|vi|Người nước ta thường tự xưng là đồng loại, đồng đạo, đồng văn với '''Nhật Bản'''; thấy họ tiến thì nức nở khen chớ không khi nào chịu xét vì sao mà họ tiến tới như thế? Họ chỉ đóng tàu súng mà được giàu mạnh hay là họ còn trao dồi đạo đức, sửa đổi luân lý mới được như ngày nay? Ai có đọc đến lịch sử Nhật Bản mới biết '''Nhật Bản''' họ cũng bồi đắp nền đạo đức của họ lắm. Từ lúc Minh Trị duy tân cho đến 24 năm sau hạ chiếu lập hiến trong nước Nhật biết bao nhiêu người lo khuynh Mạc Phủ lo lập hiến pháp, biết bao nhiêu kẻ đổ máu rát cổ mới gây dựng nên một nước tân tiến rất giàu mạnh như bây giờ.}}
===={{section|trans}}====
{{trans-top|một quốc gia ở Đông Á}}
* {{langname|ar}}: {{t+|ar|الْيَابَان|f}}
* {{langname|ab}}: {{t+|ab|Иапониа}}
* {{langname|ace}}: {{t+|ace|Jeupun}}
* {{langname|ady}}: {{t+|ady|Японие}}
* {{langname|af}}: {{t+|af|Japan}}
* {{langname|en}}: {{t+|en|Japan}}
* {{langname|et}}: {{t+|et|Jaapan}}
* {{langname|ln}}: {{t|ln|Zapɔ́}}
* {{langname|lt}}: {{t+|lt|Japonija|f}}
* {{langname|nah}}: {{t|nah|Xapōn}}
* {{langname|na}}: {{t|na|Djapan}}
* {{langname|nv}}: {{t|nv|Binááʼádaałtsʼózí dineʼé bikéyah}}
* {{langname|fr}}: le {{t+|fr|Japon|m}}
* {{langname|cs}}: {{t+|cs|Japonsko|n}}
* {{langname|zh}}:
*: {{langname|nan-hbl}}: {{t+|nan-hbl|日本|tr=Ji̍t-pún, Li̍t-pún}}
*: {{langname|cmn}}: {{t+|cmn|日本}}
*: {{langname|cjy}}: {{t+|cjy|日本}}
* {{langname|zu}}: {{t+|zu|IJapani}}
{{trans-bottom}}
==={{section|adj}}===
{{vi-adj|head=[[Nhật]] [[bản|Bản]]}}
# {{lb|vi|uncommon}} Thuộc về Nhật Bản.
==={{section|syn}}===
* {{vi-l|Nhật}}
* {{vi-l|Phù Tang}}
==={{section|see}}===
{{danh sách:quốc gia châu Á/vi}}
lg21g100bxkmlx2ghjvboeje3hx5afx
2513831
2513828
2026-08-13T13:25:20Z
Hiyuune
50834
/* Danh từ riêng */
2513831
wikitext
text/x-wiki
=={{langname|vi}}==
[[File:Flag of Japan.svg|thumb|240px|Quốc kỳ '''Nhật Bản''']]
[[Image:Japan satellite.jpg|thumb|240px|Ảnh chụp vệ tinh '''Nhật Bản''']]
{{wp}}
==={{section|alt}}===
*{{alt|vi|Nhật-Bản|Nhật-bản|Nhật Bổn|Nhật-Bổn|Nhật-bổn|Nhựt Bản|Nhựt-Bản|Nhựt-bản|Nhựt Bổn|Nhựt-Bổn|Nhựt-bổn}}
==={{section|etym}}===
{{vi-etym-sino|日本}}, từ {{bor|vi|ja|日本|tr=Nippon, Nihon}}.
==={{section|pron}}===
{{vi-pron}}
==={{section|pr-noun}}===
{{vi-pr-noun|head=[[Nhật]] [[bản|Bản]]}}
# {{senseid|vi|Q17}}{{place|vi|quốc gia|r/Đông Á|caplc=Tokyo}}.
#: {{ux|vi|Quốc hội '''Nhật Bản'''.}}
#: {{ux|vi|Biển '''Nhật Bản'''.}}
#: {{ux|vi|Rất nhiều thực tập sinh Việt Nam bị trục xuất khỏi '''Nhật Bản''', nguyên nhân chủ yếu là do vi phạm pháp luật như trộm cắp, cư trú và làm việc bất hợp pháp hoặc không hợp tác với cảnh sát địa phương.}}
#: {{ux|vi|Không ít người '''Nhật Bản''' có góc nhìn rất tiêu cực về Việt Nam vì những hành vi làm ngược lại tôn chỉ nhập gia tùy tục, và khả năng thay đổi hay loại bỏ định kiến này là khá khó.}}
#* {{w|Phan Chu Trinh|Phan Châu Trinh}} ''{{lang|vi|Đạo đức và luân lí Đông Tây}}'':
#*: {{uxi|vi|Người nước ta thường tự xưng là đồng loại, đồng đạo, đồng văn với '''Nhật Bản'''; thấy họ tiến thì nức nở khen chớ không khi nào chịu xét vì sao mà họ tiến tới như thế? Họ chỉ đóng tàu súng mà được giàu mạnh hay là họ còn trao dồi đạo đức, sửa đổi luân lý mới được như ngày nay? Ai có đọc đến lịch sử Nhật Bản mới biết '''Nhật Bản''' họ cũng bồi đắp nền đạo đức của họ lắm. Từ lúc Minh Trị duy tân cho đến 24 năm sau hạ chiếu lập hiến trong nước Nhật biết bao nhiêu người lo khuynh Mạc Phủ lo lập hiến pháp, biết bao nhiêu kẻ đổ máu rát cổ mới gây dựng nên một nước tân tiến rất giàu mạnh như bây giờ.}}
===={{section|trans}}====
{{trans-top|một quốc gia ở Đông Á}}
* {{langname|ar}}: {{t+|ar|الْيَابَان|f}}
* {{langname|ab}}: {{t+|ab|Иапониа}}
* {{langname|ace}}: {{t+|ace|Jeupun}}
* {{langname|ady}}: {{t+|ady|Японие}}
* {{langname|af}}: {{t+|af|Japan}}
* {{langname|en}}: {{t+|en|Japan}}
* {{langname|et}}: {{t+|et|Jaapan}}
* {{langname|ln}}: {{t|ln|Zapɔ́}}
* {{langname|lt}}: {{t+|lt|Japonija|f}}
* {{langname|nah}}: {{t|nah|Xapōn}}
* {{langname|na}}: {{t|na|Djapan}}
* {{langname|nv}}: {{t|nv|Binááʼádaałtsʼózí dineʼé bikéyah}}
* {{langname|fr}}: le {{t+|fr|Japon|m}}
* {{langname|cs}}: {{t+|cs|Japonsko|n}}
* {{langname|zh}}:
*: {{langname|nan-hbl}}: {{t+|nan-hbl|日本|tr=Ji̍t-pún, Li̍t-pún}}
*: {{langname|cmn}}: {{t+|cmn|日本}}
*: {{langname|cjy}}: {{t+|cjy|日本}}
* {{langname|zu}}: {{t+|zu|IJapani}}
{{trans-bottom}}
==={{section|adj}}===
{{vi-adj|head=[[Nhật]] [[bản|Bản]]}}
# {{lb|vi|uncommon}} Thuộc về Nhật Bản.
==={{section|syn}}===
* {{vi-l|Nhật}}
* {{vi-l|Phù Tang}}
==={{section|see}}===
{{danh sách:quốc gia châu Á/vi}}
aukk78lricqu86tmqvpknurbnjj4rgu
2513868
2513831
2026-08-13T15:56:53Z
Hiyuune
50834
/* {{section|trans}} */
2513868
wikitext
text/x-wiki
=={{langname|vi}}==
[[File:Flag of Japan.svg|thumb|240px|Quốc kỳ '''Nhật Bản''']]
[[Image:Japan satellite.jpg|thumb|240px|Ảnh chụp vệ tinh '''Nhật Bản''']]
{{wp}}
==={{section|alt}}===
*{{alt|vi|Nhật-Bản|Nhật-bản|Nhật Bổn|Nhật-Bổn|Nhật-bổn|Nhựt Bản|Nhựt-Bản|Nhựt-bản|Nhựt Bổn|Nhựt-Bổn|Nhựt-bổn}}
==={{section|etym}}===
{{vi-etym-sino|日本}}, từ {{bor|vi|ja|日本|tr=Nippon, Nihon}}.
==={{section|pron}}===
{{vi-pron}}
==={{section|pr-noun}}===
{{vi-pr-noun|head=[[Nhật]] [[bản|Bản]]}}
# {{senseid|vi|Q17}}{{place|vi|quốc gia|r/Đông Á|caplc=Tokyo}}.
#: {{ux|vi|Quốc hội '''Nhật Bản'''.}}
#: {{ux|vi|Biển '''Nhật Bản'''.}}
#: {{ux|vi|Rất nhiều thực tập sinh Việt Nam bị trục xuất khỏi '''Nhật Bản''', nguyên nhân chủ yếu là do vi phạm pháp luật như trộm cắp, cư trú và làm việc bất hợp pháp hoặc không hợp tác với cảnh sát địa phương.}}
#: {{ux|vi|Không ít người '''Nhật Bản''' có góc nhìn rất tiêu cực về Việt Nam vì những hành vi làm ngược lại tôn chỉ nhập gia tùy tục, và khả năng thay đổi hay loại bỏ định kiến này là khá khó.}}
#* {{w|Phan Chu Trinh|Phan Châu Trinh}} ''{{lang|vi|Đạo đức và luân lí Đông Tây}}'':
#*: {{uxi|vi|Người nước ta thường tự xưng là đồng loại, đồng đạo, đồng văn với '''Nhật Bản'''; thấy họ tiến thì nức nở khen chớ không khi nào chịu xét vì sao mà họ tiến tới như thế? Họ chỉ đóng tàu súng mà được giàu mạnh hay là họ còn trao dồi đạo đức, sửa đổi luân lý mới được như ngày nay? Ai có đọc đến lịch sử Nhật Bản mới biết '''Nhật Bản''' họ cũng bồi đắp nền đạo đức của họ lắm. Từ lúc Minh Trị duy tân cho đến 24 năm sau hạ chiếu lập hiến trong nước Nhật biết bao nhiêu người lo khuynh Mạc Phủ lo lập hiến pháp, biết bao nhiêu kẻ đổ máu rát cổ mới gây dựng nên một nước tân tiến rất giàu mạnh như bây giờ.}}
===={{section|trans}}====
{{trans-top|một quốc gia ở Đông Á}}
* {{langname|ar}}: {{t+|ar|الْيَابَان|f}}
* {{langname|ab}}: {{t+|ab|Иапониа}}
* {{langname|ace}}: {{t+|ace|Jeupun}}
* {{langname|ady}}: {{t+|ady|Японие}}
* {{langname|af}}: {{t+|af|Japan}}
* {{langname|ain}}: {{t|ain|シサㇺ|tr=Sisam}}
* {{langname|ak}}: {{t|ak|Yapan}}, {{t|ak|Gyapan}}
* {{langname|en}}: {{t+|en|Japan}}
* {{langname|he}}: {{t+|he|יַפָּן|f|tr=yapán}}
* {{langname|et}}: {{t+|et|Jaapan}}
* {{langname|hi}}: {{t+|hi|जापान|m}}
* {{langname|hu}}: {{t+|hu|Japán}}
* {{langname|hrx}}: {{t|hrx|Schapong|n}}
* {{langname|is}}: {{t+|is|Japan|n}}
* {{langname|io}}: {{t+|io|Japonia}}
* {{langname|ilo}}: {{t|ilo|Japon}}
* {{langname|ln}}: {{t|ln|Zapɔ́}}
* {{langname|lt}}: {{t+|lt|Japonija|f}}
* {{langname|nah}}: {{t|nah|Xapōn}}
* {{langname|na}}: {{t|na|Djapan}}
* {{langname|nv}}: {{t|nv|Binááʼádaałtsʼózí dineʼé bikéyah}}
* {{langname|fr}}: le {{t+|fr|Japon|m}}
* {{langname|cs}}: {{t+|cs|Japonsko|n}}
* {{langname|zh}}:
*: {{langname|nan-hbl}}: {{t+|nan-hbl|日本|tr=Ji̍t-pún, Li̍t-pún}}
*: {{langname|cmn}}: {{t+|cmn|日本}}
*: {{langname|cjy}}: {{t+|cjy|日本}}
* {{langname|zu}}: {{t+|zu|IJapani}}
{{trans-bottom}}
==={{section|adj}}===
{{vi-adj|head=[[Nhật]] [[bản|Bản]]}}
# {{lb|vi|uncommon}} Thuộc về Nhật Bản.
==={{section|syn}}===
* {{vi-l|Nhật}}
* {{vi-l|Phù Tang}}
==={{section|see}}===
{{danh sách:quốc gia châu Á/vi}}
aj9hr9w8s9hc7uulejjl48kv9g766pr
Thể loại:Emoji
14
248471
2513818
2364384
2026-08-13T13:00:47Z
TheHighFighter2
42988
Đã lùi lại sửa đổi [[Special:Diff/2364384|2364384]] của [[Special:Contributions/Kwamikagami|Kwamikagami]] ([[User talk:Kwamikagami|thảo luận]])
2513818
wikitext
text/x-wiki
[[Thể loại:Mục từ đa ngữ]]
[[Thể loại:Ký hiệu]]
6picgcu8u0naxokw9dtd80pdc1z8es6
gelaten
0
251651
2513830
2001302
2026-08-13T13:24:36Z
Kelly zhrm
58416
2513830
wikitext
text/x-wiki
=={{langname|nl}}==
==={{ĐM|pron}}===
* {{audio|nl|Nl-gelaten.ogg}}
* {{rhymes|nl|aːtən|s=3}}
==={{ĐM|n}}===
{{head|nl|Biến thể hình thái danh từ}}
# {{plural of|nl|gelaat}}
==={{ĐM|participle}}===
{{nl-past-ptc}}
# {{past participle of|nl|laten}}
===={{ĐM|decl}}====
{{nl-decl-past-ptc}}
==={{ĐM|ana}}===
* {{l|nl|elegant}}
c3sa41rtxi2dy3bqbf8oo7468cabbhi
gedane
0
252969
2513832
2001594
2026-08-13T13:26:28Z
Kelly zhrm
58416
2513832
wikitext
text/x-wiki
=={{langname|nl}}==
==={{ĐM|pron}}===
* {{audio|nl|Nl-gedane.ogg}}
==={{ĐM|participle}}===
{{head|nl|Biến thể hình thái phân từ}}
# {{infl of|nl|gedaan||infl}}
==={{ĐM|ana}}===
* {{l|nl|genade}}
pmg7eerzh1rf8ac9tj0fdmiyiwquq19
gekeerd
0
253063
2513829
2001651
2026-08-13T13:22:47Z
Kelly zhrm
58416
2513829
wikitext
text/x-wiki
=={{langname|nl}}==
==={{ĐM|pron}}===
* {{audio|nl|Nl-gekeerd.ogg}}
* {{rhymes|nl|eːrt|s=2}}
==={{ĐM|participle}}===
{{nl-past-ptc}}
# {{past participle of|nl|keren}}
===={{ĐM|decl}}====
{{nl-decl-past-ptc}}
nyl497cxgsvc53vu7w9azwenrjip8ql
Thể loại:Ký hiệu
14
260791
2513821
2361800
2026-08-13T13:02:55Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự]] thành [[Thể loại:Ký hiệu]] (đã tắt đổi hướng)
2361800
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Mô đun:labels/data/lang/vi
828
280313
2513840
2355058
2026-08-13T14:55:22Z
Hiyuune
50834
2513840
Scribunto
text/plain
local labels = {}
labels["xứ Nẫu"] = {
aliases = {"xứ Nẩu", "Nẫu", "Nẩu", "Nau", "Nẫu dialects", "Nẫu dialect", "Nẩu dialects", "Nẩu dialect"},
plain_categories = "Nẫu",
}
labels["Bình Định"] = {
aliases = {"Binh Dinh", "Bình Định dialect", "Binh Dinh dialect"},
Wikipedia = "Bình Định province",
plain_categories = "Nẫu",
}
labels["Trung Bộ"] = {
aliases = {"central Vietnam", "Central Vietnamese", "central Vietnamese"},
Wikipedia = true,
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Fangcheng"] = {
aliases = {"Fangcheng dialect"},
Wikipedia = "Gin people",
plain_categories = true,
}
labels["Hải Phòng"] = {
aliases = {"Hai Phong", "Haiphong", "Hải Phòng dialect", "Hai Phong dialect", "Haiphong dialect"},
Wikipedia = true,
plain_categories = "Northern",
}
labels["Hà Nội"] = {
aliases = {"Hanoi"},
Wikipedia = {"Hanoi"},
plain_categories = "Northern",
}
labels["Hà Tĩnh"] = {
aliases = {"Ha Tinh", "Hà Tĩnh dialect", "Ha Tinh dialect"},
Wikipedia = "Hà Tĩnh province",
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Huế"] = {
aliases = {"Hue", "Huế dialect", "Hue dialect"},
Wikipedia = true,
plain_categories = "Tiếng Việt Huế",
}
labels["Louisiana"] = {
aliases = {"New Orleans"},
Wikipedia = "Vietnamese in New Orleans",
plain_categories = true,
}
labels["Mekong Delta"] = {
aliases = {"Mekong Delta dialect", "Southwestern Vietnamese", "South Western Vietnamese", "Southwest Vietnamese", "southwestern Vietnamese", "south western Vietnamese", "southwest Vietnamese"},
Wikipedia = true,
plain_categories = true,
}
labels["Nghệ An"] = {
aliases = {"Nghe An", "Nghệ An dialect", "Nghe An dialect"},
Wikipedia = "Nghệ An province",
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Nghệ Tĩnh"] = {
aliases = {"Nghe Tinh", "Nghệ Tinh dialect", "Nghe Tinh dialect"},
Wikipedia = true,
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["North Central Vietnam"] = {
aliases = {"North-Central Vietnam", "Northern Central Vietnam", "north central Vietnam", "north-central Vietnam", "northern central Vietnam", "North Central Vietnamese", "North-Central Vietnamese", "Northern Central Vietnamese", "north central Vietnamese", "north-central Vietnamese", "northern central Vietnamese"},
Wikipedia = "North Central Coast",
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Northern Vietnam"] = {
aliases = {"North Vietnam", "northern Vietnam", "north Vietnam", "Northern Vietnamese", "North Vietnamese", "northern Vietnamese", "north Vietnamese"},
Wikipedia = true,
plain_categories = "Northern",
}
labels["South Central Vietnam"] = {
aliases = {"Southcentral Vietnam", "South central Vietnam", "South-Central Vietnam", "South Central Coast", "South central coast"},
Wikipedia = "South Central Coast",
plain_categories = "South Central",
}
labels["Northeastern Vietnam"] = {
aliases = {"Northeast Vietnam", "northeastern Vietnam", "northeast Vietnam", "Northeastern Vietnamese", "Northeast Vietnamese", "northeastern Vietnamese", "northeast Vietnamese"},
Wikipedia = true,
plain_categories = "Northern",
}
labels["Overseas Vietnamese"] = {
aliases = {"overseas Vietnamese"},
Wikipedia = true,
plain_categories = true,
}
labels["Phú Yên"] = {
aliases = {"Phu Yen", "Phú Yên dialect", "Phu Yen dialect"},
Wikipedia = "Phú Yên province",
plain_categories = "Nẫu",
}
labels["Quảng Bình"] = {
aliases = {"Quang Binh", "Quảng Bình dialect", "Quang Binh dialect"},
Wikipedia = "Quảng Bình province",
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Đà Nẵng"] = {
aliases = {"Danang", "Da Nang", "Đà Nẵng dialect", "Danang dialect"},
Wikipedia = true,
plain_categories = "South Central",
}
labels["Quảng Nam"] = {
aliases = {"Quang Nam", "Quảng Nam dialect", "Quang Nam dialect"},
Wikipedia = "Quảng Nam province",
plain_categories = "South Central",
}
labels["Quảng Ngãi"] = {
aliases = {"Quang Ngai", "Quảng Ngãi dialect", "Quang Ngai dialect"},
Wikipedia = "Quảng Ngãi province",
plain_categories = "South Central",
}
labels["Quảng Trị"] = {
aliases = {"Quang Tri", "Quảng Trị dialect", "Quang Tri dialect"},
Wikipedia = "Quảng Trị province",
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Saigon"] = {
aliases = {"Saigonese", "Ho Chi Minh City", "Hồ Chí Minh City"},
Wikipedia = true,
plain_categories = "Southern",
}
labels["Southern Vietnam"] = {
aliases = {"South Vietnam", "southern Vietnam", "south Vietnam", "Southern Vietnamese", "South Vietnamese", "southern Vietnamese", "south Vietnamese"},
Wikipedia = true,
plain_categories = "Southern",
}
labels["Thanh Hoá"] = {
aliases = {"Thanh Hoa", "Thanh Hoá dialect", "Thanh Hoa dialect"},
Wikipedia = "Thanh Hóa province",
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Việt Nam"] = {
aliases = {"Vietnamese", "Vietnam"},
Wikipedia = true,
plain_categories = "Tiếng Việt Việt Nam",
}
---------------- non-regional ----------------
labels["tulay"] = {
display = "[[từ láy]]",
}
return require("Module:labels").finalize_data(labels)
fbxdaftipbfww34c6gzqmsgviegwxmu
2513845
2513840
2026-08-13T15:02:43Z
Hiyuune
50834
2513845
Scribunto
text/plain
local labels = {}
labels["xứ Nẫu"] = {
aliases = {"xứ Nẩu", "Nẫu", "Nẩu", "Nau", "Nẫu dialects", "Nẫu dialect", "Nẩu dialects", "Nẩu dialect"},
plain_categories = "Nẫu",
}
labels["Bình Định"] = {
aliases = {"Binh Dinh", "Bình Định dialect", "Binh Dinh dialect"},
Wikipedia = "Bình Định province",
plain_categories = "Nẫu",
}
labels["Trung Bộ"] = {
aliases = {"central Vietnam", "Central Vietnamese", "central Vietnamese"},
Wikipedia = true,
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Fangcheng"] = {
aliases = {"Fangcheng dialect"},
Wikipedia = "Gin people",
plain_categories = true,
}
labels["Hải Phòng"] = {
aliases = {"Hai Phong", "Haiphong", "Hải Phòng dialect", "Hai Phong dialect", "Haiphong dialect"},
Wikipedia = true,
plain_categories = "Northern",
}
labels["Hà Nội"] = {
aliases = {"Hanoi"},
Wikipedia = {"Hanoi"},
plain_categories = "Northern",
}
labels["Hà Tĩnh"] = {
aliases = {"Ha Tinh", "Hà Tĩnh dialect", "Ha Tinh dialect"},
Wikipedia = "Hà Tĩnh province",
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Huế"] = {
aliases = {"Hue", "Huế dialect", "Hue dialect"},
Wikipedia = true,
plain_categories = "Tiếng Việt Huế",
}
labels["Louisiana"] = {
aliases = {"New Orleans"},
Wikipedia = "Vietnamese in New Orleans",
plain_categories = true,
}
labels["Đồng bằng Sông Cửu Long"] = {
aliases = {"Mekong Delta dialect", "Southwestern Vietnamese", "South Western Vietnamese", "Southwest Vietnamese", "southwestern Vietnamese", "south western Vietnamese", "southwest Vietnamese"},
Wikipedia = true,
plain_categories = "Tiếng Việt Nam Việt Nam",
}
labels["Nghệ An"] = {
aliases = {"Nghe An", "Nghệ An dialect", "Nghe An dialect"},
Wikipedia = "Nghệ An province",
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Nghệ Tĩnh"] = {
aliases = {"Nghe Tinh", "Nghệ Tinh dialect", "Nghe Tinh dialect"},
Wikipedia = true,
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["North Central Vietnam"] = {
aliases = {"North-Central Vietnam", "Northern Central Vietnam", "north central Vietnam", "north-central Vietnam", "northern central Vietnam", "North Central Vietnamese", "North-Central Vietnamese", "Northern Central Vietnamese", "north central Vietnamese", "north-central Vietnamese", "northern central Vietnamese"},
Wikipedia = "North Central Coast",
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Northern Vietnam"] = {
aliases = {"North Vietnam", "northern Vietnam", "north Vietnam", "Northern Vietnamese", "North Vietnamese", "northern Vietnamese", "north Vietnamese"},
Wikipedia = true,
plain_categories = "Northern",
}
labels["South Central Vietnam"] = {
aliases = {"Southcentral Vietnam", "South central Vietnam", "South-Central Vietnam", "South Central Coast", "South central coast"},
Wikipedia = "South Central Coast",
plain_categories = "South Central",
}
labels["Northeastern Vietnam"] = {
aliases = {"Northeast Vietnam", "northeastern Vietnam", "northeast Vietnam", "Northeastern Vietnamese", "Northeast Vietnamese", "northeastern Vietnamese", "northeast Vietnamese"},
Wikipedia = true,
plain_categories = "Northern",
}
labels["Overseas Vietnamese"] = {
aliases = {"overseas Vietnamese"},
Wikipedia = true,
plain_categories = true,
}
labels["Phú Yên"] = {
aliases = {"Phu Yen", "Phú Yên dialect", "Phu Yen dialect"},
Wikipedia = "Phú Yên province",
plain_categories = "Nẫu",
}
labels["Quảng Bình"] = {
aliases = {"Quang Binh", "Quảng Bình dialect", "Quang Binh dialect"},
Wikipedia = "Quảng Bình province",
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Đà Nẵng"] = {
aliases = {"Danang", "Da Nang", "Đà Nẵng dialect", "Danang dialect"},
Wikipedia = true,
plain_categories = "South Central",
}
labels["Quảng Nam"] = {
aliases = {"Quang Nam", "Quảng Nam dialect", "Quang Nam dialect"},
Wikipedia = "Quảng Nam province",
plain_categories = "South Central",
}
labels["Quảng Ngãi"] = {
aliases = {"Quang Ngai", "Quảng Ngãi dialect", "Quang Ngai dialect"},
Wikipedia = "Quảng Ngãi province",
plain_categories = "South Central",
}
labels["Quảng Trị"] = {
aliases = {"Quang Tri", "Quảng Trị dialect", "Quang Tri dialect"},
Wikipedia = "Quảng Trị province",
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Sài Gòn"] = {
aliases = {"Saigon", "Saigonese", "Ho Chi Minh City", "Hồ Chí Minh City"},
Wikipedia = true,
plain_categories = "Tiếng Việt Nam Việt Nam",
}
labels["Nam Việt Nam"] = {
aliases = {"South Vietnam", "southern Vietnam", "south Vietnam", "Southern Vietnamese", "South Vietnamese", "southern Vietnamese", "south Vietnamese"},
Wikipedia = "Nam Bộ",
plain_categories = "Tiếng Việt Nam Việt Nam",
}
labels["Thanh Hoá"] = {
aliases = {"Thanh Hoa", "Thanh Hoá dialect", "Thanh Hoa dialect"},
Wikipedia = "Thanh Hóa province",
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Việt Nam"] = {
aliases = {"Vietnamese", "Vietnam"},
Wikipedia = true,
plain_categories = "Tiếng Việt Việt Nam",
}
---------------- non-regional ----------------
labels["tulay"] = {
display = "[[từ láy]]",
}
return require("Module:labels").finalize_data(labels)
3beguy4khq7lgfeivbah45mqqur926k
2513846
2513845
2026-08-13T15:04:09Z
Hiyuune
50834
2513846
Scribunto
text/plain
local labels = {}
labels["xứ Nẫu"] = {
aliases = {"xứ Nẩu", "Nẫu", "Nẩu", "Nau", "Nẫu dialects", "Nẫu dialect", "Nẩu dialects", "Nẩu dialect"},
plain_categories = "Nẫu",
}
labels["Bình Định"] = {
aliases = {"Binh Dinh", "Bình Định dialect", "Binh Dinh dialect"},
Wikipedia = "Bình Định province",
plain_categories = "Nẫu",
}
labels["Trung Bộ"] = {
aliases = {"central Vietnam", "Central Vietnamese", "central Vietnamese"},
Wikipedia = true,
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Fangcheng"] = {
aliases = {"Fangcheng dialect"},
Wikipedia = "Gin people",
plain_categories = true,
}
labels["Hải Phòng"] = {
aliases = {"Hai Phong", "Haiphong", "Hải Phòng dialect", "Hai Phong dialect", "Haiphong dialect"},
Wikipedia = true,
plain_categories = "Northern",
}
labels["Hà Nội"] = {
aliases = {"Hanoi"},
Wikipedia = {"Hanoi"},
plain_categories = "Northern",
}
labels["Hà Tĩnh"] = {
aliases = {"Ha Tinh", "Hà Tĩnh dialect", "Ha Tinh dialect"},
Wikipedia = "Hà Tĩnh province",
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Huế"] = {
aliases = {"Hue", "Huế dialect", "Hue dialect"},
Wikipedia = true,
plain_categories = "Tiếng Việt Huế",
}
labels["Louisiana"] = {
aliases = {"New Orleans"},
Wikipedia = "Vietnamese in New Orleans",
plain_categories = true,
}
labels["Đồng bằng Sông Cửu Long"] = {
aliases = {"Mekong Delta dialect", "Southwestern Vietnamese", "South Western Vietnamese", "Southwest Vietnamese", "southwestern Vietnamese", "south western Vietnamese", "southwest Vietnamese"},
Wikipedia = true,
plain_categories = "Tiếng Việt Đồng bằng Sông Cửu Long",
}
labels["Nghệ An"] = {
aliases = {"Nghe An", "Nghệ An dialect", "Nghe An dialect"},
Wikipedia = "Nghệ An province",
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Nghệ Tĩnh"] = {
aliases = {"Nghe Tinh", "Nghệ Tinh dialect", "Nghe Tinh dialect"},
Wikipedia = true,
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["North Central Vietnam"] = {
aliases = {"North-Central Vietnam", "Northern Central Vietnam", "north central Vietnam", "north-central Vietnam", "northern central Vietnam", "North Central Vietnamese", "North-Central Vietnamese", "Northern Central Vietnamese", "north central Vietnamese", "north-central Vietnamese", "northern central Vietnamese"},
Wikipedia = "North Central Coast",
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Northern Vietnam"] = {
aliases = {"North Vietnam", "northern Vietnam", "north Vietnam", "Northern Vietnamese", "North Vietnamese", "northern Vietnamese", "north Vietnamese"},
Wikipedia = true,
plain_categories = "Northern",
}
labels["South Central Vietnam"] = {
aliases = {"Southcentral Vietnam", "South central Vietnam", "South-Central Vietnam", "South Central Coast", "South central coast"},
Wikipedia = "South Central Coast",
plain_categories = "South Central",
}
labels["Northeastern Vietnam"] = {
aliases = {"Northeast Vietnam", "northeastern Vietnam", "northeast Vietnam", "Northeastern Vietnamese", "Northeast Vietnamese", "northeastern Vietnamese", "northeast Vietnamese"},
Wikipedia = true,
plain_categories = "Northern",
}
labels["Overseas Vietnamese"] = {
aliases = {"overseas Vietnamese"},
Wikipedia = true,
plain_categories = true,
}
labels["Phú Yên"] = {
aliases = {"Phu Yen", "Phú Yên dialect", "Phu Yen dialect"},
Wikipedia = "Phú Yên province",
plain_categories = "Nẫu",
}
labels["Quảng Bình"] = {
aliases = {"Quang Binh", "Quảng Bình dialect", "Quang Binh dialect"},
Wikipedia = "Quảng Bình province",
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Đà Nẵng"] = {
aliases = {"Danang", "Da Nang", "Đà Nẵng dialect", "Danang dialect"},
Wikipedia = true,
plain_categories = "South Central",
}
labels["Quảng Nam"] = {
aliases = {"Quang Nam", "Quảng Nam dialect", "Quang Nam dialect"},
Wikipedia = "Quảng Nam province",
plain_categories = "South Central",
}
labels["Quảng Ngãi"] = {
aliases = {"Quang Ngai", "Quảng Ngãi dialect", "Quang Ngai dialect"},
Wikipedia = "Quảng Ngãi province",
plain_categories = "South Central",
}
labels["Quảng Trị"] = {
aliases = {"Quang Tri", "Quảng Trị dialect", "Quang Tri dialect"},
Wikipedia = "Quảng Trị province",
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Sài Gòn"] = {
aliases = {"Saigon", "Saigonese", "Ho Chi Minh City", "Hồ Chí Minh City"},
Wikipedia = true,
plain_categories = "Tiếng Việt Nam Việt Nam",
}
labels["Nam Việt Nam"] = {
aliases = {"South Vietnam", "southern Vietnam", "south Vietnam", "Southern Vietnamese", "South Vietnamese", "southern Vietnamese", "south Vietnamese"},
Wikipedia = "Nam Bộ",
plain_categories = "Tiếng Việt Nam Việt Nam",
}
labels["Thanh Hoá"] = {
aliases = {"Thanh Hoa", "Thanh Hoá dialect", "Thanh Hoa dialect"},
Wikipedia = "Thanh Hóa province",
plain_categories = "Tiếng Việt Trung Bộ",
}
labels["Việt Nam"] = {
aliases = {"Vietnamese", "Vietnam"},
Wikipedia = true,
plain_categories = "Tiếng Việt Việt Nam",
}
---------------- non-regional ----------------
labels["tulay"] = {
display = "[[từ láy]]",
}
return require("Module:labels").finalize_data(labels)
mpoyuep1232mvtfpwcdf237dch3jsk3
áo khươi
0
282889
2513875
2104057
2026-08-13T16:09:45Z
Hiyuune
50834
2513875
wikitext
text/x-wiki
=={{langname|tyz}}==
==={{ĐM|pron}}===
{{tyz-IPA}}
==={{ĐM|n}}===
{{tyz-noun}}
# [[chú]] (chồng của cô).
==={{ĐM|ref}}===
* {{R:Lương Bèn}}
nr7z4me80d5rqng0i304obdsdbtzuoi
lu̱a
0
282909
2514012
2104111
2026-08-14T09:39:37Z
Hiyuune
50834
2514012
wikitext
text/x-wiki
=={{langname|tyz}}==
==={{section|pron}}===
{{tyz-IPA}}
==={{section|n}}===
{{tyz-noun}}
# Cái [[máng]] bằng [[gỗ]].
==={{section|ref}}===
* {{R:Lương Bèn}}
9ao8x7vk4cz1kgxuvhdkh8nwmukmm68
thản
0
282910
2514010
2104112
2026-08-14T09:36:58Z
Hiyuune
50834
2514010
wikitext
text/x-wiki
=={{langname|tyz}}==
==={{ĐM|pron}}===
{{tyz-IPA}}
==={{ĐM|n}}===
{{tyz-noun}}
# Cái [[giá]] đựng [[đồ]].
# [[ban thờ|Bàn thờ]].
==={{ĐM|ref}}===
* {{R:Lương Bèn}}
lep8qrbwceaild09h2raak4iysaufyq
nào po̱
0
283039
2514007
2104429
2026-08-14T09:33:51Z
Hiyuune
50834
2514007
wikitext
text/x-wiki
=={{langname|tyz}}==
==={{ĐM|pron}}===
{{tyz-IPA}}
==={{ĐM|adj}}===
{{tyz-adj}}
# [[não bạt]] [[to]] ([[nhỏ]] [[núm]]).
6b30tdvklccjv5ungn4zyzlqthxsrqs
2514008
2514007
2026-08-14T09:35:33Z
Hiyuune
50834
/* {{langname|tyz}} */
2514008
wikitext
text/x-wiki
=={{langname|tyz}}==
==={{ĐM|pron}}===
{{tyz-IPA}}
==={{ĐM|adj}}===
{{tyz-adj}}
# [[não bạt]] [[to]] ([[nhỏ]] [[núm]]).
==={{ĐM|ref}}===
*{{R:tyz:Hoàng Văn Ma, Lục Văn Pảo, Hoàng Chí}}
*{{R:tyz:Lương Bèn}}
*{{R:tyz:tdcnt}}
mf0xajnu9fbetrrkld5l9umomxdhblf
2514009
2514008
2026-08-14T09:36:16Z
Hiyuune
50834
/* Tiếng Tày */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2514009
wikitext
text/x-wiki
=={{langname|tyz}}==
==={{ĐM|pron}}===
{{tyz-IPA}}
==={{ĐM|n}}===
{{tyz-noun}}
# [[não bạt]] [[to]] ([[nhỏ]] [[núm]]).
==={{ĐM|ref}}===
*{{R:tyz:Hoàng Văn Ma, Lục Văn Pảo, Hoàng Chí}}
*{{R:tyz:Lương Bèn}}
*{{R:tyz:tdcnt}}
36jj9m21kfnt26ygfggl7kywr8xavdv
nào me̱
0
283040
2514011
2104430
2026-08-14T09:38:21Z
Hiyuune
50834
2514011
wikitext
text/x-wiki
=={{langname|tyz}}==
==={{section|pron}}===
{{tyz-IPA}}
==={{section|n}}===
{{tyz-noun|[[鐃]][[⿰女米]]}}
# [[não bạt]] [[nhỏ]] ([[to]] [[núm]]).
==={{section|ref}}===
*{{R:tyz:Hoàng Văn Ma, Lục Văn Pảo, Hoàng Chí}}
*{{R:tyz:Lương Bèn}}
*{{R:tyz:tdcnt}}
gm6akfji2mtfph9zt8vgwq9d29qefrz
cột kẹo
0
283115
2514005
2104564
2026-08-14T09:31:59Z
Hiyuune
50834
2514005
wikitext
text/x-wiki
=={{langname|tyz}}==
==={{ĐM|pron}}===
{{tyz-IPA}}
==={{ĐM|adj}}===
{{tyz-adj}}
# [[cong queo|Cong queo]].
# [[giả dối|Giả dối]].
==={{ĐM|ref}}===
* {{R:Lương Bèn}}
t8mv6uh235ccaelw3unm1bquff36whr
kí cáo
0
283127
2514006
2104614
2026-08-14T09:32:26Z
Hiyuune
50834
2514006
wikitext
text/x-wiki
=={{langname|tyz}}==
==={{ĐM|pron}}===
{{tyz-IPA}}
==={{ĐM|adj}}===
{{tyz-adj}}
# [[mưu kế|Mưu kế]], [[thủ đoạn]].
==={{ĐM|ref}}===
* {{R:Lương Bèn}}
tc1nhrwav57t8pj4qx2bab88bb2gf11
toòng cháo
0
283137
2513872
2104650
2026-08-13T16:00:20Z
Hiyuune
50834
2513872
wikitext
text/x-wiki
=={{langname|tyz}}==
==={{ĐM|pron}}===
{{tyz-IPA}}
==={{ĐM|adj}}===
{{tyz-adj}}
# [[thao láo|Thao láo]].
==={{ĐM|ref}}===
* {{R:Lương Bèn}}
mlezybf8eafcasajiyiie4s99nf10v1
bạch ngạch
0
283138
2513873
2104651
2026-08-13T16:00:46Z
Hiyuune
50834
2513873
wikitext
text/x-wiki
=={{langname|tyz}}==
==={{ĐM|pron}}===
{{tyz-IPA}}
==={{ĐM|adj}}===
{{tyz-adj}}
# [[lếu láo|Lếu láo]].
==={{ĐM|ref}}===
* {{R:Lương Bèn}}
pune7xn5en0o06fbkea4tj2uousoft1
quai quẳn
0
283142
2513874
2104658
2026-08-13T16:01:12Z
Hiyuune
50834
2513874
wikitext
text/x-wiki
=={{langname|tyz}}==
==={{ĐM|pron}}===
{{tyz-IPA}}
==={{ĐM|adj}}===
{{tyz-adj}}
# [[khôn ngoan|Khôn ngoan]], [[khôn khéo]].
==={{ĐM|ref}}===
* {{R:Lương Bèn}}
4uqex2hrq829c50snemheafzc6xgzb4
mjàng
0
283143
2513849
2104660
2026-08-13T15:29:25Z
Hiyuune
50834
2513849
wikitext
text/x-wiki
=={{langname|tyz}}==
==={{section|pron}}===
{{tyz-IPA}}
==={{section|adj}}===
{{tyz-adj}}
# [[thành thạo|Thành thạo]].
# [[nhanh|Nhanh]].
# [[khéo léo|Khéo léo]].
==={{section|ref}}===
* {{R:Lương Bèn}}
b1pt3ha9r2a4sql7l0zurlq1tmewzdy
pác nẩư
0
283149
2513850
2104667
2026-08-13T15:30:33Z
Hiyuune
50834
2513850
wikitext
text/x-wiki
=={{langname|tyz}}==
==={{section|pron}}===
{{tyz-IPA}}
==={{section|adj}}===
{{tyz-adj}}
# [[nhanh miệng|Nhanh miệng]].
==={{section|ref}}===
* {{R:Lương Bèn}}
1by7alsbr2zowgilscslcnkmi7r6fiw
híu chảy
0
283152
2513852
2104670
2026-08-13T15:31:31Z
Hiyuune
50834
2513852
wikitext
text/x-wiki
=={{langname|tyz}}==
==={{section|pron}}===
{{tyz-IPA}}
==={{section|adj}}===
{{tyz-adj}}
# [[tinh nhanh|Tinh nhanh]], [[cừ khôi]].
==={{section|ref}}===
* {{R:Lương Bèn}}
d9y6ji5b1ghzjqdr4k6nu9rlx0vilio
pja coòng
0
283172
2513851
2104705
2026-08-13T15:31:04Z
Hiyuune
50834
2513851
wikitext
text/x-wiki
=={{langname|tyz}}==
==={{section|pron}}===
{{tyz-IPA}}
==={{section|n}}===
{{tyz-noun}}
# [[cá|Cá]] [[ngạnh]].
==={{section|ref}}===
* {{R:Lương Bèn}}
tdtsa62uvvihnvq06fsg6nstcjzoq94
Bản mẫu:kk-regional
10
288310
2513795
2314851
2026-08-13T12:09:50Z
Hiyuune
50834
2513795
wikitext
text/x-wiki
{{#invoke:checkparams|error}}<!-- Validate template parameters
-->{{kk-alt
|Cyrl={{{1|{{PAGENAME}}}}}
|Arab={{#if:{{{2|}}}|{{{2|}}}|}}
|Latn={{#if:{{{3|}}}|{{{3|}}}|}}}}<noinclude>[[Thể loại:Bản mẫu tiếng Kazakh]]</noinclude>
ebxof5hmfah2dhk4wu1kvmq6yb0b457
ᠨᠢᠭᠡᠨ
0
291852
2513986
2285583
2026-08-14T08:26:10Z
WhoAlone
40420
2513986
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{section|num}}===
{{head|cmg|Số từ}}
# [[một|Một]].
===={{section|desc}}====
* {{desc|mn|нэг|ᠨᠢᠭᠡ}}
9a22rd64zlp4e6ns58t7zmjrhnd9ck1
斗比
0
293309
2513975
2133410
2026-08-14T05:09:52Z
WhoAlone
40420
2513975
wikitext
text/x-wiki
{{-tuw-kkl-}}
{{-noun-}}
{{head|tuw-kkl|Danh từ|tr=do(u)bi}}
# [[cáo]].
{{C|tuw-kkl|Họ Chó}}
knzinqqajsqc4aw2r8r5vnl33v0fibi
依彻库伦
0
293313
2513967
2133416
2026-08-14T05:07:25Z
WhoAlone
40420
2513967
wikitext
text/x-wiki
{{-tuw-kkl-}}
{{-noun-}}
{{head|tuw-kkl|Danh từ|tr=(y)iche kulun}}
# [[cô dâu]].
{{c|tuw-kkl|Mọi người|Hôn nhân}}
ijnh6szn6vm4x7lxl126fz6htg90a75
伊彻额驸
0
293314
2513974
2133417
2026-08-14T05:09:11Z
WhoAlone
40420
2513974
wikitext
text/x-wiki
{{-tuw-kkl-}}
{{-noun-}}
{{head|tuw-kkl|Danh từ|tr=(y)iche efu}}
# [[chú rể]].
{{c|tuw-kkl|Mọi người|Hôn nhân}}
aw5f5ihk55joq8p2j150ccsio1m6r6b
朱瓦俐
0
293318
2513956
2133422
2026-08-14T05:03:12Z
WhoAlone
40420
2513956
wikitext
text/x-wiki
{{-tuw-kkl-}}
{{-noun-}}
{{head|tuw-kkl|Danh từ|tr=zhuwali}}
# [[cóc]].
{{c|tuw-kkl|Lớp Lưỡng cư}}
f71ug0yf9s6awoj4ugj17hvxoypdnk0
p̪
0
293515
2513836
2133789
2026-08-13T14:47:36Z
TheHighFighter2
42988
/* */
2513836
wikitext
text/x-wiki
{{character image|IPA Unicode 0x0070+0x032A.svg}}
=={{langname|mul}}==
{{wikipedia|Âm bật môi răng vô thanh}}
==={{ĐM|symbol}}===
{{head|mul|Ký hiệu}}
# {{label|mul|[[w:Bảng mẫu tự ngữ âm quốc tế|IPA]]}} [[âm bật|Âm bật]] [[môi]] [[răng]] [[vô thanh]].
[[Thể loại:Ký hiệu IPA|P]]
7gwxbel8gydhzjf7wv50ulgkhljfobt
Thể loại:Ký hiệu IPA
14
293516
2513837
2133790
2026-08-13T14:48:55Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự IPA]] thành [[Thể loại:Ký hiệu IPA]] (đã tắt đổi hướng)
2133790
wikitext
text/x-wiki
[[Thể loại:Ký tự đa ngữ|IPA]]
ejey756p3beuzwvt8wmctl7eqr43ya2
2513839
2513837
2026-08-13T14:54:26Z
TheHighFighter2
42988
/* */
2513839
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Bản mẫu:kk-alt
10
293548
2513797
2314855
2026-08-13T12:12:10Z
Hiyuune
50834
2513797
wikitext
text/x-wiki
{{inflection-table-top|title=Các dạng chữ viết thay thế|class=floatright|palette=blue}}
! {{w|Bảng chữ cái Kazakh#Chữ Ả Rập|Ả Rập}}
| {{#if:{{{ar|}}}{{{Arab|}}}|{{l-self|kk|{{{ar|}}}{{{Arab|}}}|tr=-}}|{{l-self|kk|{{#invoke:kk-Cyrl-Arab-translit|tr|{{{Cyrl|{{PAGENAME}}}}}}}|tr=-}}}}{{#if:{{{ar2|}}}{{{Arab2|}}}|, {{l-self|kk|{{{ar2|}}}{{{Arab2|}}}|tr=-}}|}}
|- {{#if: {{{old|}}}{{{ar-old|}}}{{{old2|}}}{{{ar-old2|}}} |
{{!-}}
! {{w|İske imlâ alphabet|İske imlâ|lang=en}}
{{!}} {{#if: {{{old|}}}{{{ar-old|}}} | {{l-self|kk|{{{old|}}}{{{ar-old|}}}|tr=-}}}}|
}}{{#if:{{{old2|}}}{{{ar-old2|}}}|, {{l-self|kk|{{{old2|}}}{{{ar-old2|}}}|tr=-}}|}}
|-
! style="min-width:4em;max-width:7em"| {{w|Bảng chữ cái Kazakh#Chữ Kirin|Kirin}}
| {{#if:{{{cy|}}}{{{Cyrl|}}}|{{l-self|kk|{{{cy|}}}{{{Cyrl|}}}|tr=-}}|{{l-self|kk|{{{Cyrl|{{PAGENAME}}}}}|tr=-}}}}{{#if:{{{cy2|}}}{{{Cyrl2|}}}|, {{l-self|kk|{{{cy2|}}}{{{Cyrl2|}}}|tr=-}}|}}
|-
! {{w|Bảng chữ cái Kazakh#Chữ Latinh|Latinh}}
| {{#if:{{{la|}}}{{{Latn|}}}|{{l-self|kk|{{{la|}}}{{{Latn|}}}|tr=-}}|{{l-self|kk|{{xlit|kk|module=kk-convert|{{{Cyrl|{{PAGENAME}}}}}}}|tr=-}}}}{{#if:{{{la2|}}}{{{Latn2|}}}|, {{l-self|kk|{{{la2|}}}{{{Latn2|}}}|tr=-}}|}} <!--
yañalif bị vô hiệu hóa, cần được xử lý trước khi bật lại
|-
{{#ifeq:{{xlit|kk|module=kk-translit|{{{Cyrl|{{PAGENAME}}}}}}}|{{#invoke:kk-translit|yanalif|{{{Cyrl|{{PAGENAME}}}}}}}||! {{w|Yañalif}}
{{!}} {{#if: {{{yan|}}}{{{ya-Latn|}}} {{!}} {{l-self|kk|{{{yan|}}}{{{ya-Latn|}}}|tr=-}}|{{l-self|kk|{{#invoke:kk-translit|yanalif|{{{Cyrl|{{PAGENAME}}}}}|tr=-}}}}}}}} -->
{{inflection-table-bottom}}<noinclude>{{tài liệu}}</noinclude>
kp3y6ffdyy3pjz6kb2eekmjhtjsxegp
Thành viên:Renamed user 884fe7926b62f9d3ae6cf3bcc87670a6
2
303715
2513876
2169746
2026-08-13T20:56:00Z
Deepfriedokra
43439
Deepfriedokra đã đổi [[Thành viên:Trong Dang]] thành [[Thành viên:Renamed user 884fe7926b62f9d3ae6cf3bcc87670a6]] (đã tắt đổi hướng): Tự động di chuyển trang khi đổi tên thành viên “[[Special:CentralAuth/Trong Dang|Trong Dang]]” thành “[[Special:CentralAuth/Renamed user 884fe7926b62f9d3ae6cf3bcc87670a6|Renamed user 884fe7926b62f9d3ae6cf3bcc87670a6]]”
2169746
wikitext
text/x-wiki
{{User vi}}
6ncsg265a2svz1rafiqtm8cqdcyvzoj
Thảo luận Thành viên:Renamed user 884fe7926b62f9d3ae6cf3bcc87670a6
3
303936
2513877
2384132
2026-08-13T20:56:00Z
Deepfriedokra
43439
Deepfriedokra đã đổi [[Thảo luận Thành viên:Trong Dang]] thành [[Thảo luận Thành viên:Renamed user 884fe7926b62f9d3ae6cf3bcc87670a6]] (đã tắt đổi hướng): Tự động di chuyển trang khi đổi tên thành viên “[[Special:CentralAuth/Trong Dang|Trong Dang]]” thành “[[Special:CentralAuth/Renamed user 884fe7926b62f9d3ae6cf3bcc87670a6|Renamed user 884fe7926b62f9d3ae6cf3bcc87670a6]]”
2384132
wikitext
text/x-wiki
<div style="border:1px solid #FDA; vertical-align:top; padding: 0.5em; background: #FFFFE5;">
<div style="margin-left:0.5em; margin-right: 0.5em; margin-bottom:1em; margin-top: 0.1em;">
<span style="font-size: 1.3rem;">'''Xin chào Trong Dang! Chào mừng bạn đến với Wiktionary tiếng Việt!'''</span>
<div class="plainlinks"><p style="margin-top:0.1em; margin-bottom: 1em;">Wiktionary là từ điển mở mà ai cũng đều có thể chỉnh sửa. Cách sử dụng và quy định có thể gây khó khăn cho bạn, nhưng hãy dần học hỏi và '''[[Wiktionary:Táo bạo|mạnh dạn đóng góp]]'''. Các thông tin dưới đây sẽ có thể giúp bạn sử dụng và chỉnh sửa Wiktionary.</p></div>
</div>
<!-- Quan trọng -->
<div style="clear: both; margin-left: 1.5em; margin-right 1.5em; font-size: 100%;">
{| class="plainlinks" width="100%" style="margin:auto;"
| style="text-align:center; padding:4px" | [[File:OOjs UI icon search-ltr.svg|30px|link=]]
| style="width:50%; padding:4px;" | '''[[Trợ giúp:Tìm kiếm|Tìm kiếm Wiktionary]]''' <br />Tìm hiểu cách tìm và tra cứu mục từ trên Wiktionary.
| style="text-align:center; padding:4px" | [[File:OOjs UI icon lightbulb.svg|30px|link=]]
| style="width:50%; padding:4px;" | '''[[Wiktionary:Yêu cầu mục từ|Yêu cầu mục từ]]'''<br />Nơi để bạn thêm những từ cần viết nhưng chưa có tại Wiktionary.
|-
| style="text-align:center; padding:4px" | [[File:OOjs UI icon userAvatar.svg|30px|link=|]]
| style="width:50%; padding:4px;" | '''[[Wiktionary:Cộng đồng|Cổng Cộng đồng Wiktionary tiếng Việt]]'''<br />Nơi tìm hiểu mọi thông tin về của Wiktionary.
| style="text-align:center; padding:4px" | [[File:OOjs UI icon articleCheck-ltr.svg|30px|link=]]
| style="width:50%; padding:4px;" | '''[[Wiktionary:Quy định và hướng dẫn|Quy định và hướng dẫn]]''' <br />Chính sách của Wiktionary là gì? Xin vui lòng đọc nó trước khi thực hiện chỉnh sửa!
|-
| style="text-align:center; padding:4px" | [[File:OOjs UI icon edit-ltr.svg|30px|link=]]
| style="width:50%; padding:4px;" | '''[[Trợ giúp:Viết trang mới|Viết trang mới]]'''<br />Tham khảo cách viết trang hay mục từ mới tại Wiktionary.
| style="text-align:center; padding:4px" | [[File:OOjs UI icon tray.svg|30px|link=]]
| style="width:50%; padding:4px;" | '''[[Wiktionary:Chỗ thử|Chỗ thử]]'''<br />Nơi để bạn viết nháp và thử nghiệm.
|-
| style="text-align:center; padding:4px" | [[File:OOjs UI icon wikiText.svg|30px|link=]]
| style="width:50%; padding:4px;" | '''[[Trợ giúp:Sửa đổi|Thực hiện sửa đổi]]'''<br />Tham khảo hướng dẫn sửa đổi và cùng bắt đầu sửa đổi Wiktionary.
| style="text-align:center; padding:4px" | [[File:OOjs UI icon helpNotice-ltr.svg|30px|link=]]
| style="width:50%; padding:4px;" | '''[[Wiktionary:Bàn giúp đỡ|Bàn giúp đỡ]]'''<br />Nơi hỏi đáp khi gặp khó khăn trên Wiktionary.
|-
| style="text-align:center; padding:4px" | [[File:OOjs UI icon book-ltr.svg|30px|link=]]
| style="width:50%; padding:4px;" | '''[[Trợ giúp:Mục lục|Mục lục trợ giúp]]''' <br />Mục lục giúp bạn thông tin và hướng dẫn khác khi gặp khó khăn trong những lúc đóng góp!
| style="text-align:center; padding:4px" | [[File:OOjs UI icon speechBubbles-ltr.svg|30px|link=]]
| style="width:50%; padding:4px;" | '''[[Wiktionary:Thảo luận|Thảo luận chung]]'''<br />Nơi trao đổi ý kiến giữa bạn và các thành viên khác!
|}</div>
<div style="padding: 0.5em; background: #FEC; border: 1px solid #FDA; margin-top: 1em; font-size: 1em">
[[File:Vector sig vi.png|250px|right]]
'''Hãy luôn nhớ rằng, sau khi viết thảo luận xong thì đừng quên bước quan trọng đó là ký tên ở phía sau thảo luận'''. Phương thức ký tên được sử dụng trong Wiktionary không phải là viết tên trực tiếp, nhưng xin hãy ký tên bằng cách sử dụng 4 dấu ngã (<code><nowiki>--~~~~</nowiki></code>) hoặc bằng cách nhấp vào nút chữ ký ([[File:OOUI JS signature icon LTR.svg|20px|border]]) trong cửa sổ chỉnh sửa của phiên bản trình soạn thảo mã nguồn thông thường.<br/>
[[Wiktionary:Guestbook_for_non-Vietnamese_speakers|Welcome! If you are not good at Vietnamese or do not speak it, click here.]]
</div>
</div> – [[Thành viên:Nguyên Hưng Trần|<span style="color:darkgreen;font-family:Georgia, serif;">'''The Emerald of Harmony'''</span>]][[Thảo luận Thành viên:Nguyên Hưng Trần|<sup style="font-family:Georgia;">awaits you in a fantasy world of silence...</sup>]] 01:57, ngày 11 tháng 5 năm 2024 (UTC)
== Thể loại ==
Chào bạn, rất cảm ơn bạn tạo các mục từ cho bách khoa thư. Tuy nhiên, mời bạn thêm thể loại cho các bài viết vừa mới tạo. Tôi nhận thấy có rất nhiều bài viết được bạn tạo nhưng chưa thêm thể loại. Một số trang, tôi có giúp việc này, tuy vậy, hiện tại số lượng mục từ "thiếu" thể loại được bạn tạo thêm liên tục tăng nhanh chóng. Mời bạn vào cải thiện. Trân trọng! [[Thành viên:P. ĐĂNG|Phong Đăng]] ([[Thảo luận Thành viên:P. ĐĂNG|thảo luận]]) 11:45, ngày 11 tháng 2 năm 2025 (UTC)
== Về việc xóa thông tin chữ nôm trong mục từ ==
Đề nghị bạn giải thích tại sao lại xóa hết thông tin chữ nôm, phiên âm hán việt, từ tương tự khỏi mục từ, tôi thấy cần có lý do hợp lý mới xóa các nội dung này.--[[Thành_viên:Kateru Zakuro|<span style="color:#EE1D25;">'''Kateru'''</span> <span style="color:#EE1D25;">'''Zakuro'''</span>]] ([[Thảo luận Thành viên:Kateru Zakuro|thảo luận]]) 22:21, ngày 19 tháng 6 năm 2026 (UTC)
:Xin lỗi vì chưa tham khảo ý kiến trước.
:Do hiện tại các thông tin xuất hiện thẳng lên trang gây khó đọc, dài dòng (có thể thấy ở mục từ [[tư]]) và tôi nghĩ nên chuyển dần định dạng theo enwikt như vài thành viên khác đã thực hiện. – [[Thành viên:Trong Dang|Trong Dang]] ([[Thảo luận Thành viên:Trong Dang|thảo luận]]) 00:36, ngày 20 tháng 6 năm 2026 (UTC)
::@[[Thành viên:Trong Dang|Trong Dang]]: Cái này thật ra có thể thu gọn bằng cách thay {{tl|đầu}} và {{tl|cuối}} bằng {{tl|col-top}} và {{tl|col-bottom}} là được, em sẽ quay lùi tất cả trang và dùng bot thay thế tự động. – [[Thành_viên:Kateru Zakuro|<span style="color:#EE1D25;">'''Kateru'''</span> <span style="color:#EE1D25;">'''Zakuro'''</span>]] ([[Thảo luận Thành viên:Kateru Zakuro|thảo luận]]) 01:31, ngày 20 tháng 6 năm 2026 (UTC)
::Lưu ý, nên thảo luận tham khảo ý kiến trước khi thực hiện các sửa đổi mang tính hàng loạt. Hy vọng bạn rút kinh nghiệm. [[Thành viên:P. ĐĂNG|Phong Đăng]] ([[Thảo luận Thành viên:P. ĐĂNG|thảo luận]]) 14:27, ngày 20 tháng 6 năm 2026 (UTC)
0ueibkgiiwi6q29x8lk64xfy6vi2zlg
Thể loại:Chữ cái tiếng Palaung Shwe
14
304507
2513843
2158564
2026-08-13T14:57:39Z
TheHighFighter2
42988
/* */
2513843
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Từ tái tạo:Tiếng Mông Cổ nguyên thuỷ/naran
0
306090
2513952
2163665
2026-08-14T04:50:41Z
WhoAlone
40420
2513952
wikitext
text/x-wiki
{{reconstructed}}
{{-xgn-pro-}}
{{-etym-}}
So sánh {{cognate|zkt|𘲺}}.
{{-noun-}}
'''*naran'''
# [[mặt trời]].
{{-desc-}}
{{top3}}
* Trung Mông Cổ:
** {{desc|xng|ᠨᠠᠷᠠᠨ}}
*** {{desc|cmg|ᠨᠠᠷᠠᠨ}}
**** {{desc|mn|нар}}
**** {{desc|bua|наран}}
**** {{desc|ykh|нара|нару}}
*** {{desc|xwo|}}
**** {{desc|xal|нарн}}
* Nam Mông Cổ:
** {{desc|yuy|naran}}
** Shirongol:
*** Bảo An:
**** {{desc|peh|narang|narong}}
**** {{desc|sce|naran}}
**** {{desc|kxs|naro}}
*** Monguor:
**** {{desc|xgn-mgr|nara}}
**** {{desc|xgn-mgl|nara}}
* {{desc|dta|nar}}
* {{desc|mhj|ناران|tr=naran}}
{{bottom}}
[[Thể loại:Mục từ tiếng Mông Cổ nguyên thuỷ|N]]
[[Thể loại:Danh từ tiếng Mông Cổ nguyên thuỷ|N]]
rmvt6xf91z2ajq4ye48hu81js82jiri
muvaffakiyetsizleştiricileştiriveremeyebileceklerimizdenmişsinizcesine
0
308350
2513903
2171638
2026-08-14T02:50:14Z
WhoAlone
40420
2513903
wikitext
text/x-wiki
{{-tur-}}
{{-etym-}}
{{inh+|tr|ota|موفقیتسزلشدریجیلشدریویرهمیهبیلهجكلریمزدنمشسڭزجهسنه}}. Nghĩa đen: "như thể bạn nằm trong số những người mà chúng tôi không thể dễ dàng biến thành người tạo ra những kẻ không thành công". Với 70 chữ cái, nó được Köksal Karakuş đề xuất là từ dài nhất trong tiếng Thổ Nhĩ Kỳ.
{{-phrase-}}
{{head|tr|Cụm từ}}
# [[như thể|Như thể]] [[bạn]] [[nằm]] [[trong]] [[số]] [[những]] [[người]] [[mà]] [[chúng tôi]] [[không thể]] [[dễ dàng]] [[biến]] [[thành]] [[người]] [[tạo]] [[ra]] [[những]] [[kẻ]] [[không]] [[thành công]].
#:{{ux|tr|Söz konusu öğretmeni makamına çağırıp ona diyor ki: "'''Muvaffakiyetsizleştiricileştiriveremeyebileceklerimizdenmişsinizcesine''' laflar ediyormuşsunuz ha?"|Ông ta mời giáo viên vào phòng và nói "Thầy đang nói '''như thể thầy nằm trong số những người mà chúng tôi không thể dễ dàng biến thành người tạo ra những kẻ không thành công''', phải không?"}}
{{-usage-}}
Trong tiếng Thổ Nhĩ Kỳ thông thường, cụm từ này sẽ là "Hemencecik başarısızlaştırıcı hâline getiremeyebileceğimiz kişilerden biriymişsiniz gibi".
{{-references-}}
* "[https://web.archive.org/web/20110718172642/http://www.yenimesaj.com.tr/index.php?haberno=8001752&tarih=2008-03-08 Yeni Mesaj, Turkish newspaper]". Lưu trữ [http://www.yenimesaj.com.tr/index.php?haberno=8001752&tarih=2008-03-08 bản gốc] ngày 18 tháng 7 năm 2011.
[[Thể loại:Từ dài tiếng Thổ Nhĩ Kỳ]]
ry5smliq5cj2ik6rwv4z88300jf4o0o
abortoria
0
311833
2513826
2266182
2026-08-13T13:19:56Z
Kelly zhrm
58416
2513826
wikitext
text/x-wiki
=={{langname|en}}==
==={{ĐM|pron}}===
* {{IPA4|en|/æ.bɔɹˈtɔɹ.i.ə/|a=US}}
==={{ĐM|noun}}===
{{head|en|Biến thể hình thái danh từ}}
# {{plural of|en|abortorium}}
=={{langname|nl}}==
==={{ĐM|noun}}===
{{head|nl|Biến thể hình thái danh từ}}
# {{plural of|nl|abortorium}}
ddd7hpcbp7jh10j11wdxm8g9tyc1klx
Japonsko
0
313600
2513869
2334423
2026-08-13T15:57:43Z
Hiyuune
50834
/* Xem thêm */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2513869
wikitext
text/x-wiki
{{also|Japóńskô}}
=={{langname|cs}}==
{{wp|cs:}}
==={{section|pron}}===
* {{cs-IPA}}
* {{audio|cs|Cs-Japonsko.ogg}}
* {{hyph|cs|Ja|pon|sko}}
==={{section|pr-noun}}===
{{cs-proper noun|n|adj=japonský|dem=Japonec|fdem=Japonka}}
# {{tcl|cs|Nhật Bản|id=Q17}}
===={{section|decl}}====
{{cs-ndecl|n.sg}}
===={{section|rel}}====
{{col|cs|japonština<g:f>}}
==={{section|further}}===
* {{R:cs:PSJC}}
* {{R:cs:SSJC}}
* {{R:cs:ASSC}}
* {{R:cs:IJP}}
{{cln|cs|Từ có hậu tố -sko}}
=={{langname|sk}}==
{{wp|sk:}}
==={{section|pron}}===
* {{sk-IPA}}
==={{section|pr-noun}}===
{{sk-proper noun|n|adj=japonský|dem=Japonec|fdem=Japonka}}
# {{tcl|sk|Nhật Bản|id=Q17}}
===={{section|decl}}====
{{sk-ndecl|n|n=sg}}
===={{section|rel}}====
{{col|sk|japončina<g:f>}}
==={{section|see}}===
{{list:quốc gia của châu Á/sk}}
==={{section|further}}===
* {{R:sk:SDK}}
{{cln|sk|Từ có hậu tố -sko}}
sr68sezghxhtvhx5brctryxd8uifobx
ăn ong
0
314876
2513860
2182837
2026-08-13T15:46:21Z
Hiyuune
50834
2513860
wikitext
text/x-wiki
{{also|Anong|anong}}
=={{langname|vi}}==
==={{section|pron}}===
{{vi-pron}}
==={{section|v}}===
{{vi-verb}}
# {{lb|vi|Mekong Delta}} Đi lấy [[mật ong]] từ việc gác [[kèo]] trước đó.
{{C|vi|Ong}}
9dhrndv2nxil1xb75tuk82bij1busqr
2513861
2513860
2026-08-13T15:46:49Z
Hiyuune
50834
/* {{section|v}} */
2513861
wikitext
text/x-wiki
{{also|Anong|anong}}
=={{langname|vi}}==
==={{section|pron}}===
{{vi-pron}}
==={{section|v}}===
{{vi-verb}}
# {{lb|vi|Đồng bằng Sông Cửu Long}} Đi lấy [[mật ong]] từ việc gác [[kèo]] trước đó.
{{C|vi|Ong}}
ldhd1esevkfw5c7j77snrzdvgtp9xuh
Thể loại:Tiếng Việt Huế
14
315016
2513835
2293519
2026-08-13T13:46:16Z
Hiyuune
50834
/* */
2513835
wikitext
text/x-wiki
{{auto cat|lect=1|lang=vi|cat=Tiếng Việt Trung Bộ|[[Huế]], [[Việt Nam]]}}
k3odhaxwxngrpgv7or4tgufzk39mton
bibliotheken
0
323594
2513827
2198763
2026-08-13T13:21:28Z
Kelly zhrm
58416
2513827
wikitext
text/x-wiki
{{also|Bibliotheken}}
=={{langname|nl}}==
==={{ĐM|pron}}===
* {{audio|nl|Nl-bibliotheken.ogg}}
==={{ĐM|n}}===
{{head|nl|Biến thể hình thái danh từ}}
# {{plural of|nl|bibliotheek}}
nesw80s876nbqr4tecu36dix0xc6n4j
Mô đun:category tree/topic/Communication
828
324839
2513817
2512776
2026-08-13T12:56:14Z
TheHighFighter2
42988
2513817
Scribunto
text/plain
local labels = {}
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
-- FIXME: Lookup langs in the language list.
for _, lang_etc in ipairs {
"Ngôn ngữ Ả Rập", {"Ngôn ngữ Trung Quốc"}, "Ngôn ngữ Anh", "Ngôn ngữ Đức", "Ngôn ngữ Nhật", "Ngôn ngữ Latinh", "Ngôn ngữ Triều Tiên ", "Ngôn ngữ Okinawa",
"Ngôn ngữ Bồ Đào Nha", "Ngôn ngữ Phạn", "Ngôn ngữ Tây Ban Nha", "Ngôn ngữ Việt"
} do
if type(lang_etc) ~= "table" then
lang_etc = {lang_etc}
end
local lang, desc = unpack(lang_etc)
desc = desc or ("%s"):format(lang, lang)
labels[lang] = {
type = "chủ đề thuộc nhóm liên quan",
description = "=" .. desc,
parents = {"Tên ngôn ngữ"},
}
end
labels["Giao tiếp"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"Tất cả chủ đề"},
wpcat = true,
}
labels["Thuật ngữ theo lĩnh vực"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"Thuật ngữ"},
wpcat = true,
}
labels["Thuật ngữ"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"Ngôn ngữ"},
wpcat = true,
}
labels["Bảng chữ cái"] = {
type = "chủ đề thuộc nhóm tên gọi",
description = "default",
parents = {"Hệ chữ viết"},
}
labels["Ngôn ngữ được xây dựng"] = { -- distinguish from "cat:constructed languages" family category
type = "chủ đề thuộc nhóm tên gọi",
description = "={{w|Ngôn ngữ được xây dựng}}",
parents = {"Tên ngôn ngữ"},
}
labels["Ngôn ngữ cơ thể"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"Ngôn ngữ", "Giao tiếp phi ngôn ngữ"},
}
labels["Phát sóng"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"Truyền thông đại chúng", "Viễn thông"},
}
labels["thành phần chữ Hán"] = {
type = "chủ đề thuộc nhóm tập hợp",
description = "=[[Chinese character]] [[component]]s",
parents = {"chữ cái, ký hiệu và dấu câu"},
}
labels["diacritical marks"] = {
type = "chủ đề thuộc nhóm tập hợp",
description = "default",
parents = {"chữ cái, ký hiệu và dấu câu"},
}
labels["Phương ngữ"] = {
type = "chủ đề thuộc nhóm tập hợp",
description = "default",
parents = {"Ngôn ngữ"},
}
labels["Đọc viết chính tả"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"Giao tiếp"},
}
labels["extinct languages"] = {
type = "chủ đề thuộc nhóm tên gọi",
description = "default",
parents = {"languages"},
}
labels["sign languages"] = {
type = "chủ đề thuộc nhóm tên gọi",
description = "default",
parents = {"languages"},
}
labels["facial expressions"] = {
type = "chủ đề thuộc nhóm tập hợp",
description = "default",
parents = {"nonverbal communication", "face"},
}
labels["figures of speech"] = {
type = "chủ đề thuộc nhóm tập hợp",
description = "=[[figure of speech|figures of speech]]",
parents = {"rhetoric"},
}
labels["flags"] = {
type = "chủ đề thuộc nhóm liên quan,chủ đề thuộc nhóm tên gọi,chủ đề thuộc nhóm loại hình",
description = "default",
parents = {"communication"},
}
labels["biệt ngữ"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"ngôn ngữ"},
}
labels["Han characters"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"Hệ thống viết"},
}
labels["Ngôn ngữ"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"Giao tiếp"},
}
labels["language families"] = {
type = "chủ đề thuộc nhóm tên gọi",
description = "=[[language family|language families]], both accepted and controversial",
parents = {"Ngôn ngữ", "Tên"},
}
labels["Tên ngôn ngữ"] = {
type = "chủ đề thuộc nhóm tên gọi",
description = "default",
parents = {"Ngôn ngữ"},
}
labels["chữ cái, ký hiệu và dấu câu"] = {
type = "chủ đề thuộc nhóm tập hợp",
description = "=[[letter]]s, [[symbol]]s, and [[punctuation]]",
parents = {"orthography"},
}
labels["logical fallacies"] = {
type = "chủ đề thuộc nhóm tập hợp",
description = "=[[logical fallacy|logical fallacies]], clearly defined errors in reasoning used to support or refute an argument",
additional = "{{also|Category:{{{langcode}}}:biases}}",
parents = {"rhetoric", "logic"},
}
labels["Phương tiện truyền thông"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"Giao tiếp"},
}
labels["mobile phones"] = {
type = "chủ đề thuộc nhóm liên quan,chủ đề thuộc nhóm tập hợp",
description = "default",
parents = {"telephony"},
}
labels["Giao tiếp phi ngôn ngữ"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"Giao tiếp"},
}
labels["orthography"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"viết"},
}
labels["cổ văn tự học"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"viết"},
}
labels["bưu chính"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "=[[bưu chính]] hoặc [[thư điện tử]]",
parents = {"Giao tiếp"},
}
labels["quan hệ công chúng"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"Giao tiếp"},
}
labels["punctuation marks"] = {
type = "chủ đề thuộc nhóm tập hợp",
description = "default",
parents = {"chữ cái, ký hiệu và dấu câu"},
}
labels["Truyền thanh"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"Viễn thông"},
}
labels["rhetoric"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"language"},
}
labels["signs"] = {
type = "chủ đề thuộc nhóm liên quan,chủ đề thuộc nhóm tên gọi,chủ đề thuộc nhóm loại hình",
description = "default",
parents = {"communication"},
}
labels["sociolects"] = {
type = "chủ đề thuộc nhóm tên gọi",
description = "default",
parents = {"language"},
}
labels["ký hiệu"] = {
type = "chủ đề thuộc nhóm tập hợp",
description = "=[[symbol]]s, especially [[mathematical]] and [[scientific]] symbols",
additional = "Most symbols have equivalent meanings in many languages and can therefore be found in [[:Category:Translingual symbols]].",
parents = {"chữ cái, ký hiệu và dấu câu"},
}
labels["Trò chuyện"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"Ngôn ngữ", "Hành vi con người"},
}
labels["Viễn thông"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default no singularize",
parents = {"Giao tiếp", "Công nghệ"},
}
labels["telegraphy"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"telecommunications", "electronics"},
wpcat = true,
commonscat = true,
}
labels["telephony"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"telecommunications", "electronics"},
}
labels["texting"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"telecommunications"},
}
labels["textual division"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"viết"},
}
labels["typography"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"writing", "printing"},
}
labels["viết"] = {
type = "chủ đề thuộc nhóm liên quan",
description = "default",
parents = {"Ngôn ngữ", "Hành vi con người"},
}
labels["Hệ thống viết"] = {
type = "chủ đề thuộc nhóm tập hợp",
description = "default",
parents = {"viết"},
}
return labels
2ec6ewn3w3axf7unwafor6iax8d6wfp
Thành viên:Hiyuune/common.js
2
329942
2513936
2313010
2026-08-14T04:33:21Z
Hiyuune
50834
2513936
javascript
text/javascript
importScript("en:User:Erutuon/scripts/addAutoCat.js");
mw.loader.load("//vi.wiktionary.org/w/index.php?title=Th%C3%A0nh_vi%C3%AAn:Veritas_Sapientiae/twinkle.js&action=raw&ctype=text/javascript");
importScript("Thành viên:Hiyuune/Gadget-PagePreviews.js");
gxp1otpuju4ns4ojxuxo0o8e0mhms9r
2513938
2513936
2026-08-14T04:34:33Z
Hiyuune
50834
2513938
javascript
text/javascript
importScript("en:User:Erutuon/scripts/addAutoCat.js");
mw.loader.load("//vi.wiktionary.org/w/index.php?title=Th%C3%A0nh_vi%C3%AAn:Veritas_Sapientiae/twinkle.js&action=raw&ctype=text/javascript");
mw.loader.load("//vi.wiktionary.org/w/index.php?title=Th%C3%A0nh_vi%C3%AAn:Hiyuune/Gadget-PagePreviews.js&action=raw&ctype=text/javascript");
4z6w1teoqcjem8koi0ymuj4p8fapdnm
2513946
2513938
2026-08-14T04:43:23Z
Hiyuune
50834
2513946
javascript
text/javascript
importScript("en:User:Erutuon/scripts/addAutoCat.js");
mw.loader.load("//vi.wiktionary.org/w/index.php?title=Th%C3%A0nh_vi%C3%AAn:Veritas_Sapientiae/twinkle.js&action=raw&ctype=text/javascript");
mw.loader.load("//vi.wiktionary.org/w/index.php?title=Th%C3%A0nh_vi%C3%AAn:Hiyuune/Gadget-popups.js&action=raw&ctype=text/javascript");
e03ub5wozf7dcbaqs6rjfodegwdwkd6
Thể loại:Ký hiệu tiếng Palaung Shwe
14
342916
2513841
2361789
2026-08-13T14:56:47Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Palaung Shwe]] thành [[Thể loại:Ký hiệu tiếng Palaung Shwe]] (đã tắt đổi hướng)
2361789
wikitext
text/x-wiki
[[Thể loại:Ký tự đa ngữ|P]]
[[Thể loại:Mục từ tiếng Palaung Shwe]]
7rz05egyxm1od0zpnzs209xbc3cn81g
2513842
2513841
2026-08-13T14:57:09Z
TheHighFighter2
42988
/* */
2513842
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Mô đun:category tree/symbols
828
357831
2513838
2513192
2026-08-13T14:54:00Z
TheHighFighter2
42988
2513838
Scribunto
text/plain
local labels = {}
local raw_categories = {}
-----------------------------------------------------------------------------
-- --
-- LABELS --
-- --
-----------------------------------------------------------------------------
labels["Ký hiệu"] = {
description = "Các ký hiệu được viết bằng {{{langname}}}.",
umbrella_parents = "Tất cả",
parents = {{name = "{{{langcat}}}", raw = true}},
}
labels["Dấu câu"] = {
description = "{{{langname}}} glyphs that are added to characters to modify their sounds or meanings.",
parents = {"Ký hiệu"},
}
labels["Han characters"] = {
description = "{{{langname}}} symbols of the Han logographic script, which can represent sounds or convey meanings directly.",
topright = "{{wp|Chinese characters}}",
parents = {"logograms"},
}
labels["ideograms"] = {
description = "{{{langname}}} symbols that represent objects, ideas or concepts directly.",
umbrella_parents = "Fundamental",
parents = {"symbols"},
}
labels["iteration marks"] = {
description = "{{{langname}}} glyphs that act as a shorthand for repeated text.",
parents = {"abbreviations", "symbols"},
}
labels["Chữ cái"] = {
description = "{{{langname}}} symbols that represent single sounds. Variations of letters, such as letters with diacritics, should also be categorized here.",
parents = {"Ký hiệu"},
}
labels["ligatures"] = {
description = "{{{langname}}} letters that have been combined into a single graphical unit.",
parents = {"symbols"},
}
labels["Văn tự ngữ tố"] = {
description = "{{{langname}}} symbols that represent single words or morphemes.",
parents = {"Ký hiệu"},
}
labels["matched pairs"] = {
description = "{{{langname}}} pairs of separate characters that are used together. Example: [[( )]].",
parents = {"symbols"},
}
labels["numeral symbols"] = {
description = "{{{langname}}} symbols that represent numerals.",
parents = {"symbols"},
}
labels["punctuation marks"] = {
description = "{{{langname}}} symbols that indicate structure and organization of writing.",
parents = {"symbols"},
}
labels["syllables"] = {
description = "{{{langname}}} symbols that represent groups of sounds. Variations of syllables, such as syllables with diacritics, should also be categorized here.",
parents = {"symbols"},
}
labels["multigraphs"] = {
description = "{{{langname}}} symbols consisting of multiple letters.",
parents = {"symbols"},
}
local function multigraph(name, n)
labels[name] = {
description = "{{{langname}}} symbols consisting of " .. n .. " letters.",
parents = {"multigraphs"},
}
end
multigraph("digraphs", "two")
multigraph("trigraphs", "three")
multigraph("tetragraphs", "four")
multigraph("pentagraphs", "five")
multigraph("hexagraphs", "six")
multigraph("heptagraphs", "seven")
multigraph("octagraphs", "eight")
-- Add 'umbrella_parents' key if not already present.
for key, data in pairs(labels) do
if not data.umbrella_parents then
data.umbrella_parents = "Thể loại con ký hiệu theo ngôn ngữ"
end
end
-----------------------------------------------------------------------------
-- --
-- RAW CATEGORIES --
-- --
-----------------------------------------------------------------------------
raw_categories["Thể loại con ký hiệu theo ngôn ngữ"] = {
description = "Umbrella categories covering topics related to symbols.",
additional = "{{{umbrella_meta_msg}}}",
parents = {
"Siêu thể loại chung",
{name = "Ký hiệu", is_label = true, sort = " "},
},
}
raw_categories["Ký hiệu IPA"] = {
description = "Các ký hiệu trong {{w|bảng phiên âm quốc tế}} (IPA).",
additional = "Để biết thêm thông tin, hãy đọc [[Wiktionary:IPA]].",
catfix = "mul",
parents = {
{name = "Ký hiệu", is_label = true, lang = "mul"},
},
}
raw_categories["NAPA symbols"] = {
description = "Symbols which are part of the {{w|Americanist phonetic notation|North American Phonetic Alphabet}}.",
catfix = "mul",
parents = {
{name = "symbols", is_label = true, lang = "mul"},
},
}
raw_categories["UPA symbols"] = {
description = "Symbols which are part of the {{w|Uralic Phonetic Alphabet}}.",
catfix = "mul",
parents = {
{name = "symbols", is_label = true, lang = "mul"},
},
}
raw_categories["Ký hiệu đa ngữ"] = {
description = "Thể loại này liệt kê các ký hiệu đa ngữ (các ký hiệu được sử dụng chung trên toàn thế giới).",
catfix = "mul",
parents = {
"Đa ngữ",
"Thể loại:Ký hiệu theo ngôn ngữ",
},
}
raw_categories["Mã ISO"] = {
description = "Thể loại này liệt kê các mã ISO.",
catfix = "mul",
parents = {
"Ký hiệu đa ngữ",
},
}
return {LABELS = labels, RAW_CATEGORIES = raw_categories}
t9e475073zg6u6aceqgudcjir2d9tmu
Thể loại:Ký hiệu tiếng Punjab
14
363223
2513816
2282756
2026-08-13T12:43:49Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Punjab]] thành [[Thể loại:Ký hiệu tiếng Punjab]] (đã tắt đổi hướng)
2282756
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Ký hiệu tiếng Phai
14
365562
2513815
2290075
2026-08-13T12:43:04Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Phai]] thành [[Thể loại:Ký hiệu tiếng Phai]] (đã tắt đổi hướng)
2290075
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Ký hiệu tiếng Khuen
14
365669
2513802
2290217
2026-08-13T12:35:15Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Khuen]] thành [[Thể loại:Ký hiệu tiếng Khuen]] (đã tắt đổi hướng)
2290217
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Ký hiệu tiếng Kensiu
14
365770
2513801
2290444
2026-08-13T12:34:44Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Kensiu]] thành [[Thể loại:Ký hiệu tiếng Kensiu]] (đã tắt đổi hướng)
2290444
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Ký hiệu tiếng Ormur
14
366246
2513814
2291611
2026-08-13T12:42:33Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Ormur]] thành [[Thể loại:Ký hiệu tiếng Ormur]] (đã tắt đổi hướng)
2291611
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Ký hiệu tiếng Koli Parkar
14
366253
2513807
2291619
2026-08-13T12:38:01Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Koli Parkar]] thành [[Thể loại:Ký hiệu tiếng Koli Parkar]] (đã tắt đổi hướng)
2291619
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Ký hiệu tiếng Kanuri
14
366255
2513800
2291621
2026-08-13T12:34:14Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Kanuri]] thành [[Thể loại:Ký hiệu tiếng Kanuri]] (đã tắt đổi hướng)
2291621
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Ký hiệu tiếng Oriya
14
368229
2513812
2296046
2026-08-13T12:41:41Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Oriya]] thành [[Thể loại:Ký hiệu tiếng Oriya]] (đã tắt đổi hướng)
2296046
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Ký hiệu tiếng Konso
14
369724
2513809
2299190
2026-08-13T12:40:04Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Konso]] thành [[Thể loại:Ký hiệu tiếng Konso]] (đã tắt đổi hướng)
2299190
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Ký hiệu tiếng Kistane
14
370883
2513803
2302161
2026-08-13T12:35:46Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Kistane]] thành [[Thể loại:Ký hiệu tiếng Kistane]] (đã tắt đổi hướng)
2302161
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Ký hiệu tiếng Konkan
14
371398
2513808
2303111
2026-08-13T12:38:36Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Konkan]] thành [[Thể loại:Ký hiệu tiếng Konkan]] (đã tắt đổi hướng)
2303111
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Ký hiệu tiếng Korku
14
374326
2513810
2309594
2026-08-13T12:40:28Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Korku]] thành [[Thể loại:Ký hiệu tiếng Korku]] (đã tắt đổi hướng)
2309594
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Ký hiệu tiếng Kolhe
14
374339
2513806
2309619
2026-08-13T12:37:27Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Kolhe]] thành [[Thể loại:Ký hiệu tiếng Kolhe]] (đã tắt đổi hướng)
2309619
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Ký hiệu tiếng Oriya Adiwasi
14
374364
2513813
2309658
2026-08-13T12:42:08Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Oriya Adiwasi]] thành [[Thể loại:Ký hiệu tiếng Oriya Adiwasi]] (đã tắt đổi hướng)
2309658
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Ký hiệu tiếng Koda
14
375198
2513804
2311176
2026-08-13T12:36:19Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Koda]] thành [[Thể loại:Ký hiệu tiếng Koda]] (đã tắt đổi hướng)
2311176
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Ký hiệu tiếng Kannada
14
375946
2513799
2312606
2026-08-13T12:33:42Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Kannada]] thành [[Thể loại:Ký hiệu tiếng Kannada]] (đã tắt đổi hướng)
2312606
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Ký hiệu tiếng Kodava
14
375950
2513805
2312610
2026-08-13T12:36:56Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Kodava]] thành [[Thể loại:Ký hiệu tiếng Kodava]] (đã tắt đổi hướng)
2312610
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Ký hiệu tiếng Kukna
14
375982
2513811
2312642
2026-08-13T12:40:49Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Kukna]] thành [[Thể loại:Ký hiệu tiếng Kukna]] (đã tắt đổi hướng)
2312642
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
ᠬᠣᠶᠠᠷ
0
379212
2513987
2319520
2026-08-14T08:26:24Z
WhoAlone
40420
2513987
wikitext
text/x-wiki
=={{langname|mn}}==
{{mn-variant|ᠬᠣᠶᠠᠷ|хоёр}}
==={{ĐM|pron}}===
{{mn-IPA|хоёр}}
==={{ĐM|num}}===
{{head|mn|Số từ}}
# [[hai]].
=={{langname|cmg}}==
==={{section|num}}===
{{head|cmg|Số từ}}
# [[hai]].
===={{section|desc}}====
* {{desc|mn|хоёр|ᠬᠣᠶᠠᠷ}}
pcjk8zq3rl3zbeu1v7i62hv3x4mbh1e
Mô đun:mul-letter
828
400562
2513794
2363904
2026-08-13T12:06:54Z
Hiyuune
50834
2513794
Scribunto
text/plain
local export = {}
local PAGENAME = mw.loadData("Module:headword/data").pagename
local function getCasing(char, variant)
if not variant then
variant = "common"
end
local casing = require("Module:mul-letter/data")[variant][char]
if not casing then
if mw.ustring.len(char) > 1 then
casing = {mw.ustring.lower(char), mw.ustring.upper(mw.ustring.sub(char, 1, 1)) .. mw.ustring.lower(mw.ustring.sub(char, 2, -1)), mw.ustring.upper(char)}
else
casing = {mw.ustring.lower(char), mw.ustring.upper(char), mw.ustring.upper(char)}
end
end
return casing
end
function export.show(frame)
local args = frame:getParent().args
local head = args["head"] or PAGENAME
local lang = require("Module:languages").getByCode("mul")
local scriptCode = args["sc"] or require("Module:scripts").findBestScriptWithoutLang(head):getCode()
local script = scriptCode and require("Module:scripts").getByCode(scriptCode) or lang:findBestScript(head)
local variant = args["var"]
local data = {
lang = lang,
sc = script,
pos_category = "Chữ cái",
categories = {},
heads = {head},
no_redundant_head_cat = not args.head,
inflections = {}
}
local casing = getCasing(head, variant)
if casing[1] ~= head then
table.insert(data.inflections, {label = "chữ thường", casing[1]})
end
if casing[2] ~= head and casing[2] ~= casing[3] then
table.insert(data.inflections, {label = "chữ hỗn hợp", casing[2]})
end
if casing[3] ~= head then
table.insert(data.inflections, {label = "chữ hoa", casing[3]})
end
return require("Module:headword").full_headword(data) ..
require("Module:utilities").format_categories("Ký tự " .. script:getCategoryName(), lang)
end
return export
nloxburs5t47t6mc2ew513481r3trdn
Thể loại:Ký tự Chữ symbolic
14
400901
2513819
2364385
2026-08-13T13:01:27Z
TheHighFighter2
42988
Đã lùi lại sửa đổi [[Special:Diff/2364385|2364385]] của [[Special:Contributions/Kwamikagami|Kwamikagami]] ([[User talk:Kwamikagami|thảo luận]])
2513819
wikitext
text/x-wiki
[[Thể loại:Ký hiệu]]
1jxsjd6237srnlzgdwqjzrd6if31jm3
Thể loại:Ký tự Chữ unspecified
14
400902
2513820
2364386
2026-08-13T13:02:00Z
TheHighFighter2
42988
đã dời [[Thể loại:Ký tự]]; đã thêm [[Thể loại:Ký hiệu]] dùng [[w:WP:HOTCAT|HotCat]]
2513820
wikitext
text/x-wiki
[[Thể loại:Ký hiệu]]
1jxsjd6237srnlzgdwqjzrd6if31jm3
Thể loại:Ký hiệu tiếng Kafa
14
405539
2513798
2405812
2026-08-13T12:33:09Z
TheHighFighter2
42988
TheHighFighter2 đã đổi [[Thể loại:Ký tự tiếng Kafa]] thành [[Thể loại:Ký hiệu tiếng Kafa]] (đã tắt đổi hướng)
2405812
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
6월
0
410962
2513980
2502615
2026-08-14T05:30:02Z
TheHighFighter2
42988
2513980
wikitext
text/x-wiki
=={{langname|ko}}==
==={{ĐM|etym}}===
{{ko-etym-Sino|六|sáu|月|tháng}}.
==={{ĐM|pron}} 1===
{{ko-IPA|유월}}
===={{ĐM|noun}}====
{{ko-noun|hangeul=유월|hanja=[[六月]]/[[6月]]}}
# [[tháng Sáu|Tháng Sáu]] {{gl|Hàn Quốc}}.
==={{ĐM|pron}} 2===
{{ko-IPA|류월}}
===={{ĐM|noun}}====
{{ko-noun|hangeul=류월|hanja=[[六月]]/[[6月]]}}
# [[tháng Sáu|Tháng Sáu]] {{gl|Bắc Triều Tiên}}.
{{C|ko|Tháng}}
8ns1gw3pzqrxlpswxnjfcgxmtv99mc2
2513981
2513980
2026-08-14T05:33:43Z
TheHighFighter2
42988
/* Tiếng Triều Tiên */
2513981
wikitext
text/x-wiki
=={{langname|ko}}==
==={{ĐM|etym}}===
{{ko-etym-Sino|六|sáu|月|tháng}}.
==={{ĐM|pron}} 1===
{{ko-IPA|유월}}
===={{ĐM|noun}}====
{{ko-noun|hangeul=유월|hanja=[[六月]]/[[6月]]}}
# {{lb|ko|Hàn Quốc}} [[tháng Sáu|Tháng Sáu]].
==={{ĐM|pron}} 2===
{{ko-IPA|류월}}
===={{ĐM|noun}}====
{{ko-noun|hangeul=류월|hanja=[[六月]]/[[6月]]}}
# {{lb|ko|Bắc Triều Tiên}} [[tháng Sáu|Tháng Sáu]].
{{C|ko|Tháng}}
bo1zmkjadctbn6oz9viownhceicwo0r
Ɛ̃
0
411302
2513793
2512129
2026-08-13T12:03:58Z
Hiyuune
50834
2513793
wikitext
text/x-wiki
{{minitoc}}
=={{langname|mul}}==
==={{ĐM|desction}}===
{{mul-letter}}
#[[chữ|Chữ]] '''[[Ɛ]] viết hoa''' với dấu ngã ([[tilde]]).
==={{ĐM|see}}===
{{Latn-script}}
=={{langname|ada}}==
==={{ĐM|pron}}===
*{{IPA4|ada|[ɛ̃]}}
==={{ĐM|symbol}}===
{{head|ada|Ký tự|chữ thường|ɛ̃}}
#Chữ ''{{l|ada|Ɛ}}'' viết hoa với dấu mũi hóa.
#:{{quote|ada|HYƐ MAWU NINE NYA NÍ TSUMI! NƆ́ NƐ MAWU HA NƐ E KP'''Ɛ̃''' Ɔ, MƐNƆ MA NYƐ MAA PEE LƐ NƐ E DA?|HÃY XEM-XÉT CÔNG-VIỆC CỦA ĐỨC CHÚA TRỜI; VÌ VẬT GÌ NGÀI ĐÃ ĐÁNH CONG, AI CÓ THỂ LÀM NGAY LẠI ĐƯỢC? ([[s:Truyền đạo/7#7:13|Truyền đạo 7:13]])}}
===={{ĐM|see}}====
*{{letters|lang=ada|sc=Latn|qual=Bảng chữ cái Latinh tiếng Adangme|Aa|Bb|Dd|Ee|Ɛɛ|Ff|Gg|Hh|Ii|Jj|Kk|Ll|Mm|Nn|Oo|Ɔɔ|Pp|Ss|Tt|Uu|Vv|Ww|Yy|Zz}}
==={{ĐM|ref}}===
*{{Chú thích web|ada|entryurl=https://www.bible.com/bible/2265/ECC.7.DANGME|entry=Fiɛlɔ 7|author=Bible Society of Ghana|work=BAIBLO ALOO NGMAMI KLƆUKLƆU Ɔ|year=2025}}
=={{langname|bba}}==
==={{ĐM|pron}}===
*{{IPA4|bba|/ɛ̃/}}
==={{ĐM|symbol}}===
{{head|bba|Ký tự|chữ thường|ɛ̃}}
#Chữ ''{{l|bsq|Ɛ}}'' viết hoa với dấu mũi hóa.
#:{{ux|bba|GB'''Ɛ̃'''GB'''Ɛ̃'''NƐ|[[âm thanh|ÂM THANH]] [[vang|VANG]]|inline=1}}
==={{ĐM|see}}===
*{{letters|lang=bba|sc=Latn|qual=Bảng chữ cái Latinh tiếng Baatonum|Aa|Bb|Dd|Ee|Ɛɛ|Ff|Gg|Gb/gb|Ii|Kk|Kp/kp|Ll|Mm|Nn|Oo|Ɔɔ|Pp|Rr|Ss|Tt|Uu|Ww|Yy}}
==={{ĐM|ref}}===
*{{Chú thích sách|en|title=[https://www.webonary.org/baatonum/files/Dictionnaire-Baatonum-Fran%C3%A7ais_corrig%C3%A9-_version-du-01-09-2017.pdf Baatonum – French Dictionary]|editor=Léonard Goragui|year=2021|publisher=SIL International|page=57}}
=={{langname|bsq}}==
==={{ĐM|pron}}===
*{{IPA4|bsq|/ɛ̃/}}
==={{ĐM|symbol}}===
{{head|bsq|Ký tự|chữ thường|ɛ̃}}
#Chữ ''{{l|bsq|Ɛ}}'' viết hoa với dấu mũi hóa.
#:{{quote|bsq|'''Ɛ̃̀'''hɛ̃́ɛ̀, kpá nìà kɛ mɔ̀ ní kpá ɓěɔ̀ ɖò, ké fɔ́ nìà kɛ mɔ̀ ní fɔ́ɔ̀ ɖò.|Người nầy là xương bởi xương tôi, thịt bởi thịt tôi mà ra. ([[s:Sáng thế Ký/2#2:23|Sáng thế Ký 2:23]])}}
==={{ĐM|see}}===
*{{letters|lang=bsq|sc=Latn|qual=Bảng chữ cái Latinh tiếng Bassa|Aa|(Áá)|(Àà)|(Ǎǎ)|(Ââ)|(Ãã)|Bb|Ɓɓ|Cc|Dd|Ɖɖ|Dy/dy|Ɛɛ|(Ɛ́/ɛ́)|(Ɛ̀/ɛ̀)|(Ɛ̌/ɛ̌)|(Ɛ̂/ɛ̂)|(Ɛ̃/ɛ̃)|Ee|(Éé)|(Èè)|(Ěě)|(Êê)|Ff|Gg|Gb/gb|Gm/gm|Hh|Hw/hw|Ii|(Íí)|(Ìì)|(Ǐǐ)|(Îî)|(Ĩĩ)|Jj|Kk|Kp/kp|Mm|Nn|Ny/ny|Ɔɔ|(Ɔ́/ɔ́)|(Ɔ̀/ɔ̀)|(Ɔ̌/ɔ̌)|(Ɔ̂/ɔ̂)|(Ɔ̃/ɔ̃)|Oo|(Óó)|(Òò)|(Ǒǒ)|(Ôô)|Pp|Ss|Tt|Uu|(Úú)|(Ùù)|(Ǔǔ)|(Ûû)|(Ũũ)|Vv|Ww|Xw/xw|Zz}}
==={{ĐM|ref}}===
*{{Chú thích web|en|work=Bassa-English Dictionary|author=CEFL|year=2025|entryurl=https://cefliberia.org/bassa/bassa-dictionary/o2/|entry=Ɔ}}
*{{Chú thích web|bsq|work=Ɓǎsɔ́ɔ̀ Báɓòɔ̀|author=Bible Society in Liberia|year=2025|entryurl=https://www.bible.com/bible/2826/GEN.2.%25C6%2581B02|entry=Jɛ́nɛ́sì 2}}
=={{langname|bqc}}==
==={{ĐM|pron}}===
*{{IPA4|bqc|/ɛ̃/}}
==={{ĐM|symbol}}===
{{head|bqc|Ký tự|chữ thường|ɛ̃}}
#Chữ ''{{l|bqc|Ɛ}}'' viết hoa với dấu mũi hóa.
#:{{quote|bqc|ASA GB'''Ɛ̃'''PII DAZIƐ.|VÌ LÀ MẸ CỦA CẢ LOÀI NGƯỜI. ([[s:Sáng thế Ký/3#3:20|Sáng thế Ký 3:20]])}}
==={{ĐM|see}}===
*{{letters|lang=bqc|sc=Latn|qual=Bảng chữ cái Latinh tiếng Boko|Aa|Bb|Dd|Ee|Ɛɛ|Ff|Gg|Gb/gb|Ii|Kk|Kp/kp|Ll|Mm|Nn|Oo|Ɔɔ|Pp|Rr|Ss|Tt|Uu|Vv|Ww|Yy|Zz}}
==={{ĐM|ref}}===
*{{Chú thích web|bqc|work=Boko Bible|author=SIM International|year=2024|entryurl=https://www.bible.com/bible/1155/GEN.3.BOO2024|entry=Daa 3}}
=={{langname|bus}}==
==={{ĐM|pron}}===
*{{IPA4|bus|[ɛ̃]}}
==={{ĐM|letter}}===
{{letter|bus}}
#Chữ cái thứ 13 viết hoa trong bảng chữ cái tiếng Bokobaru.
#:{{quote|bus|À EBERU NƐ́GↃ̃GB'''Ɛ̃''' KƐ̀ WÀ Ń ÍNↃ KÁ NILI ÍN Ń PÍNGI, MↃDE À NƐ́NↃGB'''Ɛ̃'''NↃ TÓ.|PHÀM CON TRAI CỦA DÂN HÊ-BƠ-RƠ MỚI SANH, HÃY LIỆNG XUỐNG SÔNG; CÒN CON GÁI, THÌ ĐỂ CHO SỐNG. ([[s:Xuất Ê-díp-tô Ký/1#1:22|Xuất Ê-díp-tô Ký 1:22]])}}
==={{ĐM|see}}===
*{{letters|lang=bus|sc=Latn|qual=Bảng chữ cái Latinh tiếng Bokobaru|Aa|Aa/aa|Ãã|Ãa/ãa|Bb|Dd|Ee|Ee/ee|Ẽẽ|Ẽe/ẽe|Ɛɛ|Ɛɛ/ɛɛ|Ɛ̃/ɛ̃|Ɛ̃ɛ/ɛ̃ɛ|Ff|Gg|Gb/gb|Hh|Ii|Ii/ii|Ĩĩ|Ĩi/ĩi|Kk|Kp/kp|Ll|Mm|Nn|Oo|Oo/oo|Õõ|Õo/õo|Ɔɔ|Ɔɔ/ɔɔ|Ɔ̃/ɔ̃|Ɔ̃ɔ/ɔ̃ɔ|Pp|Rr|Ss|Tt|Uu|Uu/uu|Ũũ|Ũu/ũu|Vv|Ww|Yy|Zz}}
==={{ĐM|ref}}===
*{{Chú thích web|bus|author=SIM International|entryurl=https://www.bible.com/bible/3085/EXO.1.BBB|entry=BƆNA 1|year=2024|work=Bokobaru Bible}}
=={{langname|bqp}}==
==={{ĐM|pron}}===
*{{IPA4|bqp|/ɛ̃/}}
==={{ĐM|letter}}===
{{letter|bqp}}
#Chữ cái thứ 13 viết hoa trong bảng chữ cái Latinh tiếng Busa.
#:{{quote|bqp|MATƐNI ABIRE OÁRƐ, DE GB'''Ɛ̃'''KE SÚN Á SÃTƐ KŨ LƐ́NNA LƐ́KPAKƆ̃ANAAORO YÃI.|TÔI NÓI NHƯ VẬY, HẦU CHO CHẲNG AI LẤY LỜI DỖ-DÀNH MÀ LỪA-DỐI ANH EM. ([[s:Cô-lô-se/2#2:4|Cô-lô-se 2:4]])}}
==={{ĐM|see}}===
*{{letters|lang=bqp|sc=Latn|qual=Bảng chữ cái Latinh tiếng Busa|Aa|Aa/aa|Ãã|Ãa/ãa|Bb|Dd|Ee|Ee/ee|Ẽẽ|Ẽe/ẽe|Ɛɛ|Ɛɛ/ɛɛ|Ɛ̃/ɛ̃|Ɛ̃ɛ/ɛ̃ɛ|Ff|Gg|Gb/gb|Hh|Ii|Ii/ii|Ĩĩ|Ĩi/ĩi|Kk|Kp/kp|Ll|Mm|Nn|Oo|Oo/oo|Õõ|Õo/õo|Ɔɔ|Ɔɔ/ɔɔ|Ɔ̃/ɔ̃|Ɔ̃ɔ/ɔ̃ɔ|Pp|Rr|Ss|Tt|Uu|Uu/uu|Ũũ|Ũu/ũu|Vv|Ww|Yy|Zz}}
==={{ĐM|ref}}===
*{{Chú thích web|bqp|author=SIM International|entryurl=https://www.bible.com/bible/3084/COL.2.BB2005|entry=KƆLƆSEDENƆ 2|year=2005|work=Bisã Bible}}
=={{langname|dgi}}==
==={{ĐM|pron}}===
*{{IPA|dgi|/ɛ̃/}}
==={{ĐM|symbol}}===
{{head|dgi|Ký tự|chữ thường|ɛ̃}}
#Chữ ''{{l|dgi|Ɛ}}'' viết hoa với dấu mũi hóa.
#:{{ux|dgi|KOR'''Ɛ̃'''TƖ|[[s:I Cô-rinh-tô|CÔ-RINH-TÔ]]|inline=1}}
==={{ĐM|see}}===
*{{letters|lang=dgi|sc=Latn|qual=Bảng chữ cái Latinh tiếng Dagara Bắc|Aa|Bb|Ɓɓ|Cc|Dd|Ee|Ɛɛ|Ff|Gg|Gb/gb|Hh|’H/’h|Ii|Ɩɩ|Jj|Kk|Kp/kp|Ll|’L/’l|Mm|Nn|Ny/ny|Ŋŋ|Ŋm/ŋm|Oo|Ɔɔ|Pp|Rr|Ss|Tt|Uu|Ʋʋ|Vv|Ww|’W/’w|Yy|Ƴƴ|Zz|’}}
==={{ĐM|ref}}===
*{{Chú thích web|dgi|author=Bible Society of Burkina Faso|work=A SƐB-SÕW|year=2013|entry=1 Korɛ̃tɩ 1|entryurl=https://www.bible.com/bible/3053/1CO.1.DBDC}}
=={{langname|dnj}}==
==={{ĐM|pron}}===
*{{IPA4|dnj|[ɛ̃]}}
==={{ĐM|symbol}}===
{{head|dnj|Ký tự|chữ thường|ɛ̃}}
#Chữ cái '''{{l|dnj|Ɛ}} viết hoa''' với dấu mũi hóa.
#:{{quote|dnj|GÓ YÀWƐ È GRÚSƎ̀ KƎ KAÁ NƐ̀Z'''Ɛ̃'''Ã KÁ GRÚSƎ̀ KƎ̀A MƐ WÀ GAƐ WO KÁ, É KÁ KƎ̀A N KÁ PƟ́Ɛ LƟ́.|CẦU ĐỨC GIÊ-HÔ-VA LẤY ƠN ĐÃI HAI CON NHƯ HAI CON ĐÃ ĐÃI CÁC NGƯỜI THÁC CỦA CHÚNG TA, VÀ ĐÃ ĐÃI CHÍNH MÌNH TA! ([[s:Ru-tơ/1#1:8|Ru-tơ 1:8]])}}
==={{ĐM|see}}===
*{{letters|lang=dnj|sc=Latn|qual=Bảng chữ cái Latinh tiếng Dan ở Liberia|Aa|Bb|Ɓɓ|Dd|Ɗɗ|Ee|Ɛɛ|Ff|Gg|GB/gb|Hh|Ii|Kk|KP/kp|KW/kw|Ll|Mm|Nn|NW/nw|NY/ny|Ŋŋ|Oo|Ɔɔ|Əə|Ɵɵ|Pp|Rr|Ss|Tt|Uu|Ɥɥ|Vv|Ww|Xx|Yy|Zz}}
*{{letters|lang=dnj|sc=Latn|qual=Bảng chữ cái Latinh tiếng Dan Tây ở Bờ Biển Ngà|Aa|AƆ/aɔ|Bb|BH/bh|Dd|DH/dh|Ee|Ɛɛ|Ëë|ƐA/ɛa|Ff|Gg|GB/gb|GW/gw|Ii|Kk|KP/kp|KW/kw|Ll|Mm|Nn|NG/ng|Oo|Ɔɔ|Öö|Pp|Rr|Ss|Tt|Uu|Üü|Vv|Ww|Yy|Zz}}
*{{letters|lang=dnj|sc=Latn|qual=Bảng chữ cái Latinh tiếng Dan Đông ở Bờ Biển Ngà|Aa|Œœ|Bb|BH/bh|Dd|DH/dh|Ee|Ʌʌ|Ɛɛ|Ææ|Ff|Gg|GB/gb|GW/gw|Hh|Ii|Ɩɩ|Kk|KP/kp|KW/kw|Ll|Mm|Nn|Oo|ɤ|Ɔɔ|Pp|Rr|Ss|Tt|Uu|Ɯɯ|Ʋʋ|Vv|Ww|Yy|Zz}}
==={{ĐM|ref}}===
*{{Chú thích web|dnj|author=Bible Society in Liberia|entryurl=https://www.bible.com/bible/2828/RUT.1.DHB|entry=Wrúfè 1|year=2025|work=Aaɓi Kwì}}
*{{Chú thích web|dnj|author=Wycliffe Bible Translators|entryurl=https://www.bible.com/bible/1831/EPH.1.DNJBL|entry=Efɛzö 1|year=1991|work=Dan Blowo (Yacouba) Nouveau Testament}}
=={{langname|gaa}}==
==={{ĐM|pron}}===
*{{IPA4|gaa|/ɛ̃/}}
==={{ĐM|letter}}===
{{letter|gaa}}
#Chữ cái thứ 7 viết hoa trong bảng chữ cái Latinh tiếng Ga.
#:{{quote|gaa|MIBI SOLOMON BAAYE MAŊTSƐ YƐ MISƐƐ, NI LƐ EBAATA MIS'''Ɛ̃'''I LƐ NƆ.|SA-LÔ-MÔN CON TRAI NGƯƠI SẼ TRỊ-VÌ KẾ TA, VÀ NÓ SẼ NGỒI TRÊN NGÔI TA. ([[s:I Các Vua/1#1:17|I Các Vua 1:17]])}}
==={{ĐM|see}}===
*{{letters|lang=gaa|sc=Latn|qual=Bảng chữ cái Latinh tiếng Ga|Aa|(Ãã)|Bb|Dd|Ee|Ẽẽ|Ɛɛ|Ɛ̃/ɛ̃|Ff|Gg|(Gb/gb)|(Gw/gw)|Hh|(Hw/hw)|Ii|(Ĩĩ)|Jj|(Jw/jw)|Kk|(Kp/kp)|(Kw/kw)|Ll|Mm|Nn|(Ny/ny)|Ŋŋ|(Ŋm/ŋm)|(Ŋw/ŋw)|Oo|(Õõ)|Ɔɔ|(Ɔ̃/ɔ̃)|Pp|Rr|Ss|(Sh/sh)|(Shw/shw)|Tt|(Ts/ts)|(Tsw/tsw)|Uu|(Ũũ)|Vv|Ww|Yy|Zz}}
==={{ĐM|ref}}===
#{{Chú thích web|gaa|author=Bible Society of Ghana|entryurl=https://www.bible.com/bible/2708/1KI.1.NEGAB|entry=1 Maŋtsɛmɛi 1|year=2024|work=ŊMALƐ KRƆŊKRƆŊ LƐ}}
=={{langname|gej}}==
==={{ĐM|pron}}===
*{{IPA4|gej|[ɛ̃]}}
==={{ĐM|symbol}}===
{{head|gej|Ký tự|chữ thường|ɛ̃}}
#Chữ cái '''{{l|gej|Ɛ}} viết hoa''' với dấu mũi hóa.
#:{{ux|gej|ÈSEA GBÌGBƆ̀ HL'''Ɛ̃'''|[[s:Phục truyền Luật lệ Ký|PHỤC TRUYỀN LUẬT LỆ KÝ]]|inline=1}}
==={{ĐM|see}}===
*{{letters|lang=gej|sc=Latn|qual=Bảng chữ cái Latinh tiếng Gen|Aa|Bb|Cc|Dd|Ɖɖ|Ee|Ɛɛ|Ff|Gg|GB/gb|Ɣɣ|Hh|Xx|Ii|Jj|Kk|KP/kp|Ll|Mm|Nn|NY/ny|Ŋŋ|Ɔɔ|Pp|Ss|Tt|Uu|Ʋʋ|Vv|Ww|Yy|Zz}}
==={{ĐM|ref}}===
*{{Chú thích web|gej|author=Bible Society of Togo|work=GƐ̃GBE BIBLIA 2014|entry=Èsea gbìgbɔ̀ hlɛ̃ 1|entryurl=https://www.bible.com/bible/2236/DEU.1.GEN|year=2014}}
=={{langname|ife}}==
==={{ĐM|pron}}===
*{{IPA4|ife|/ɛ̃/}}
==={{ĐM|letter}}===
{{letter|ife}}
#Chữ cái thứ 8 viết hoa trong bảng chữ cái Latinh tiếng Ifè.
#:{{ux|kbp|Ɔ̀G'''Ɛ̃'''|[[tính|TÍNH]] [[lười|LƯỜI]]|inline=1}}
==={{ĐM|see}}===
*{{letters|lang=ife|sc=Latn|qual=Bảng chữ cái Latinh tiếng Ifè|Aa|Ãã|Bb|Ɖɖ|Dz/dz|Ee|Ɛɛ|Ɛ̃/ɛ̃|Ff|Gg|Gb/gb|Hh|Ii|Ĩĩ|Kk|Kp/kp|Ll|Mm|Nn|Ny/ny|Ŋŋ|Oo|Ɔɔ|Ɔ̃/ɔ̃|Rr|Ss|Tt|Ts/ts|Uu|Ũũ|Ww|Yy}}
==={{ĐM|ref}}===
*{{Chú thích web|editor=Mary Gardner & Elizabeth Graveling|entryurl=https://www.webonary.org/ife/gc787e220-e01b-4451-ab30-46a76ff3063e/?lang=en|entry=ɔ̀gɛ̃|year=2016|work=Ife–French-English Dictionary|publisher=SIL International}}
=={{langname|gkp}}==
==={{ĐM|pron}}===
*{{IPA4|gkp|[ɛ̃]}}
==={{ĐM|symbol}}===
{{head|gkp|Ký tự}}
#Chữ cái '''{{l|gkp|Ɛ}} viết hoa''' với dấu mũi hóa.
#:{{quote|gkp|Ŋ̀ÓLII-SÃAI KÉLEE KÁ GÙLA MEDIƐŊ KƆ-KULUƂA-ŊAI DA V'''Ɛ̃'''Ɛ-KPÂLAŊ SU-ƂELAI WÔLII KA DÍTƐƐ ḾBƆ.|TA CHỈ XIN CÁC NGƯƠI MỘT ĐỀU NẦY, LÀ MỖI NGƯỜI TRONG CÁC NGƯƠI PHẢI GIAO CHO TA NHỮNG VÒNG MÌNH ĐÃ ĐOẠT LẤY. ([[s:Các Quan Xét/8#8:24|Các Quan Xét 8:24]])}}
==={{ĐM|see}}===
*{{letters|lang=gkp|sc=Latn|qual=Bảng chữ cái năm 1989|a|(á)|(à)|(à)|(â)|(ã)|b|ɓ|d|e|(é)|(è)|(ê)|ə|ɛ|(ɛ́)|(ɛ̀)|(ɛ̂)|(ɛ̃)|f|g|ɠ|gb|gw|h|hw|i|(í)|(ì)|(î)|(ĩ)|j|k|kp|kw|l|m|n|nw|ɲ|ŋ|o|(ó)|(ò)|(ô)|ɔ|(ɔ́)|(ɔ̀)|(ɔ̂)|(ɔ̃)|p|r|s|t|u|(ú)|(ù)|(û)|(ũ)|v|w|y}}
==={{ĐM|ref}}===
*{{Chú thích web|gkp|author=Bible Society in Liberia|work=Bible|year=2012|entry=M̀ɛni-sâa Ƃó-ƃelai 8|entryurl=https://www.bible.com/bible/500/JDG.8.KWB}}
=={{langname|lob}}==
==={{ĐM|pron}}===
*{{IPA4|lob|/ɛ̃/}}
==={{ĐM|symbol}}===
{{head|lob|Ký tự|chữ thường|ɛ̃}}
#Chữ ''{{l|lob|Ɛ}}'' viết hoa với dấu mũi hóa.
#:{{ux|lob|KOR'''Ɛ̃'''TƖDARA|[[s:I Cô-rinh-tô|CÔ-RINH-TÔ]]|inline=1}}
==={{ĐM|see}}===
*{{letters|lang=lob|sc=Latn|qual=Bảng chữ cái Latinh tiếng Lobi|Aa|Bb|Ɓɓ|Cc|Dd|Ee|Ɛɛ|Ǝǝ|Ff|Gg|Gb/gb|Hh|Ii|Ɩɩ|Jj|Kk|Kh/kh|Kp/kp|Ll|’L/’l|Mm|Nn|Ny/ny|Oo|Ɔɔ|Pp|Ph/ph|Rr|Ss|Tt|Th/th|Uu|Ʋʋ|Vv|Ww|’W/’w|(Ⱳⱳ)|Yy|Ƴƴ}}
==={{ĐM|ref}}===
*{{Chú thích web|lob|author=British & Foreign Bible Society|entryurl=https://www.bible.com/bible/3594/1CO.1.LOBIRI|entry=1 Korɛ̃tɩdara 1|year=1985|work=Thãgbaa Ʋlfɛ Pha 1985}}
=={{langname|lom}}==
==={{ĐM|pron}}===
*{{IPA4|lom|[ɛ̃]}}
==={{ĐM|symbol}}===
{{head|lom|Ký tự|chữ thường|ɛ̃}}
#Chữ ''{{l|lom|Ɛ}}'' viết hoa với dấu mũi hóa.
#:{{quote|lom|TƐ DƐ TÙO VELEI W'''Ɛ̃'''ƐNI.|CHẲNG TỪNG BIẾT ĐƯỜNG-LỐI TA. ([[s:Hê-bơ-rơ/3#3:10|Hê-bơ-rơ 3:10]])}}
==={{ĐM|ref}}===
*{{Chú thích web|lom|author=Bible Society in Liberia|work=DEƲE NIINƐI|year=1971|entry=HIIƂULUITIƐ 3|entryurl=https://www.bible.com/bible/1456/HEB.3.DN1971}}
=={{langname|nga}}==
==={{ĐM|pron}}===
*{{IPA4|nga|[ɛ̃]}}
==={{ĐM|symbol}}===
{{head|nga|Ký tự|chữ thường|ɛ̃}}
#Chữ ''{{l|nga|Ɛ}} viết hoa'' với dấu mũi hóa.
#:{{quote|nga|'''Ɛ̃'''fɛ̃ lɛnggɛ kɛ ma usu 'dã hã ni, ma ɔ nɛ fɛ̃a mɔ bolo kɛ wa nãlã ma dɛ 'do gili iko ni.|Môi-miệng sốt-sắng và lòng độc-ác,<br>Khác nào bình-gốm bọc vàng bạc pha. ([[s:Châm ngôn/26#26:23|Châm ngôn 26:23]])}}
===={{ĐM|see}}====
*{{letters|lang=nga|sc=Latn|qual=Bảng chữ cái Latinh tiếng Ngbaka|Aa|Bb|ʼB/ʼb|Dd|ʼD/ʼd|Ee|Ɛɛ|Ff|Gg|Gb/gb|Hh|Ii|Kk|Kp/kp|Ll|Mm|Nn|Ng/ng|Ngg/ngg|Oo|Ɔɔ|Ss|Tt|Uu|Vv|Ww|Zz}}
==={{ĐM|adj}}===
{{head|nga|Tính từ}}
#{{see-entry|ɛ̃}}
==={{ĐM|ref}}===
*{{Chú thích web|fr|editor=Marcel Henrix|work=Ngbaka – French Dictionary|year=2020|entry=ɛ|entryurl=https://www.webonary.org/ngbaka/browse/browse-vernacular-french/?key=nga&letter=%C9%9B&lang=en}}
*{{Chú thích web|nga|author=Bible Society of the Democratic Republic of Congo|work=WE NU GALE|year=1995|entry=GBA WENƆ 26|entryurl=https://www.bible.com/bible/2920/PRO.26.NGB}}
=={{langname|sbd}}==
==={{ĐM|pron}}===
*{{IPA4|sbd|/ɛ̃/}}
==={{ĐM|symbol}}===
{{head|sbd|Ký tự|chữ thường|ɛ̃}}
#Ký tự ''{{l|sbd|Ɛ}}'' viết hoa với dấu mũi hóa.
#:{{ux|sbd|PA'''Ɛ̃'''PA'''Ɛ̃'''|[[trắng tinh|TRẮNG TINH]]|inline=1}}
===={{ĐM|see}}====
*{{letters|lang=sbd|sc=Latn|qual=Bảng chữ cái Latinh tiếng Samo Nam|Aa|Bb|Dd|Ff|Gg|Hh|Ii|Kk|Ll|Mm|Nn|Pp|Rr|Ss|Tt|Ww|Yy|Zz}}
==={{ĐM|ref}}===
*{{Chú thích web|entry=paɛ̃paɛ̃|entryurl=https://www.webonary.org/san-sud/gd83828bc-b5c8-4a68-9cb7-0de3e73bda12/?lang=en|author=Urs Niggli|work=Southern San - French - English Dictionary|year=2021|publisher=SIL International}}
*{{Chú thích web|sbd|entryurl=https://www.bible.com/bible/3059/GEN.1.SBPE2011|entry=SGK 1|work=LAWA A BOO SONBORE|year=2011|author=Bible Society of Burkina Faso}}
=={{langname|sld}}==
==={{ĐM|pron}}===
*{{IPA4|sld|/ɛ̃/}}
==={{ĐM|letter}}===
{{letter|sld}}
#Chữ cái thứ 10 viết hoa trong bảng chữ cái Latinh tiếng Sissala.
#:{{quote|sld|BEE WƖ́Ɛ́ NƖƐ NÁ HÁ WƖ WƖƖSƐ '''Ɛ̃''', ƁA NƖ́Ɩ́ JA ONNI NYÁŊ WUU.|VÌ MỌI ĐỀU ĐÓ, CÁC DÂN NGOẠI VẪN THƯỜNG TÌM, ([[s:Ma-thi-ơ/6#6:32|Ma-thi-ơ 6:32]])}}
==={{ĐM|see}}===
*{{letters|lang=sld|sc=Latn|qual=Bảng chữ cái Latinh tiếng Sissala|Aa|Ãã|Bb|Ɓɓ|Cc|Dd|Ee|Ẽẽ|Ɛɛ|Ɛ̃/ɛ̃|Ff|Gg|Gb/gb|Hh|Ii|Ĩĩ|Ɩɩ|Ɩ̃/ɩ̃|Jj|Kk|Ll|Mm|Nn|Ny/ny|Ŋŋ|Ŋm/ŋm|Oo|Õõ|Ɔɔ|Ɔ̃/ɔ̃|Pp|Rr|Ss|Tt|Uu|Ʋʋ|Ʋ̃/ʋ̃|Vv|Ww|Yy|Zz}}
==={{ĐM|ref}}===
*{{Chú thích web|sld|author=The Bible League|entryurl=https://live.bible.is/bible/sldtbl/mat/6|entry=Matie 6|year=1999|work=Bible.is}}
=={{langname|akp}}==
==={{ĐM|pron}}===
*{{IPA4|akp|/ɛ̃/}}
==={{ĐM|symbol}}===
{{head|akp|Ký tự|chữ thường|ɛ̃}}
#Chữ ''{{l|akp|Ɛ}}'' viết hoa với dấu mũi hóa.
#:{{quote|akp|LOTÃ WŨ ƆWI SƆ ƆFINIKI, ƔƐƐ ƆS'''Ɛ̃''' SƆ Ɔ̃IBANYUA ASƆRƆ̃ IƉE.|TA ĐÃ CHO NÓ THÌ-GIỜ ĐỂ ĂN-NĂN, MÀ NÓ CHẲNG MUỐN ĂN-NĂN ĐỀU TÀ-DÂM NÓ! ([[s:Khải huyền/1#1:21|Khải huyền 1:21]])}}
==={{ĐM|see}}===
*{{letters|lang=akp|sc=Latn|qual=Bảng chữ cái Latinh tiếng Siwu|Aa|Bb|Dd|Dz/dz|Ɖɖ|Ee|Ɛɛ|Ff|Gg|Gb/gb|Ɣɣ|Hh|Ii|Kk|Kp/kp|Ll|Mm|Nn|Ny/ny|Oo|Ɔɔ|Pp|Rr|Ss|Tt|Ts/ts|Uu|Vv|Ww|Yy}}
==={{ĐM|ref}}===
*{{Chú thích web|akp|author=Wycliffe Bible Translators, Inc.|entryurl=https://www.bible.com/bible/1738/REV.1.AKP|entry=KAƉIKATE ƆKO 1|year=2008|work=Siwu}}
=={{langname|tcd}}==
==={{ĐM|pron}}===
*{{IPA4|tcd|[ɛ̃]}}
==={{ĐM|letter}}===
{{letter|tcd}}
#Chữ cái thứ 11 viết hoa trong bảng chữ cái Latinh tiếng Tafi.
#:{{ux|tcd|RASH'''Ɛ̃'''L|[[w:Rachel|RACHEL]]|inline=1}}
==={{ĐM|see}}===
*{{letters|lang=tcd|sc=Latn|qual=Bảng chữ cái Latinh tiếng Tafi|Aa|Ãã|Bb|Bh/bh|Dd|Dz/dz|Dzy/dzy|Ɖɖ|Ee|Ɛɛ|Ɛ̃/ɛ̃|Ff|Fw/fw|Gg|Gb/gb|Hh|Hw/hw|Ii|Ĩĩ|Ɩɩ|Ɩ̃/ɩ̃|Kk|Kp/kp|Ll|Mm|Nn|Ny/ny|Ŋŋ|Ŋw/ŋw|Oo|Ɔɔ|Ɔ̃/ɔ̃|Pp|Rr|Ss|Sh/sh|Tt|Ts/ts|Tsy/tsy|Tsyw/tsyw|Uu|Ũũ|Ʊʊ|Ʊ̃/ʊ̃|Vv|Ww|W̃/w̃|Xx|Xw/xw|Yy|Zz|Zh/zh}}
==={{ĐM|ref}}===
*{{Chú thích web|tcd|work=ONUGBÙ NYƐNYÃ VUVƆ|author=Wycliffe Bible Translators, Inc.|year=2025|entry=Mateo 2|entryurl=https://www.bible.com/bible/3070/MAT.2.TCD}}
=={{langname|tbz}}==
==={{ĐM|pron}}===
*{{IPA4|tbz|/ɛ̃/}}
==={{ĐM|symbol}}===
{{head|tbz|Ký tự|chữ thường|ɛ̃}}
#Chữ ''{{l|tbz|Ɛ}} viết hoa'' với dấu mũi hóa.
#:{{quote|tbz|KƐ̀ KUYIE NDO BO YIE NKƐ́ N Y'''Ɛ̃'''TƐ|CHỚ CHI ĐỨC CHÚA TRỜI ĐẸP LÒNG CÀ-TÁN TÔI, ([[s:Gióp/6#6:9|Gióp 6:9]])}}
==={{ĐM|see}}===
*{{letters|lang=tbz|sc=Latn|qual=Bảng chữ cái Latinh tiếng Tammari|Aa|(Àà)|(Áá)|(Ãã)|(Ã̀/ã̀)|(Ã́/ã́)|Bb|Cc|Dd|Ee|(Èè)|(Éé)|Ɛɛ|(Ɛ̀/ɛ̀)|(Ɛ́/ɛ́)|(Ɛ̃/ɛ̃)|(Ɛ̃̀/ɛ̃̀)|(Ɛ̃́/ɛ̃́)|Ff|Hh|Ii|(Ìì)|(Íí)|(Ĩĩ)|(Ĩ̀/ĩ̀)|(Ĩ́/ĩ́)|Kk|Kp/kp|Mm|(Ḿḿ)|(M̀/m̀)|Nn|(Ńń)|(Ǹǹ)|Oo|(Òò)|(Óó)|Ɔɔ|(Ɔ̀/ɔ̀)|(Ɔ́/ɔ́)|(Ɔ̃/ɔ̃)|(Ɔ̃̀/ɔ̃̀)|(Ɔ̃́/ɔ̃́)|Pp|Rr|Ss|Tt|Uu|(Ùù)|(Úú)|(Ũũ)|(Ũ̀/ũ̀)|(Ṹ/ṹ)|Ww|Yy}}
==={{ĐM|ref}}===
*{{Chú thích sách|fr|author=Annelore Zürcher|title=[https://www.sil.org/system/files/reapdata/18/46/48/18464836488843238420469350815761768632/2725_Zurcher_Phonemes_ditammari_1983.pdf Les phonèmes segmentaux du ditammari: Rapport de recherches]|year=1983|publisher=Société Internationale de Linguistique|location=Lomé}}
*{{Chú thích web|tbz|author= L’Alliance Biblique du Bénin|entryurl=https://live.bible.is/bible/TBZBSB/JOB/6|entry=ISƆBU 6|year=2001|work=Bibiri - Kuyie Nnaanti Patiri}}
=={{langname|bov}}==
==={{ĐM|pron}}===
*{{IPA4|bov|[ɛ̃]}}
==={{ĐM|symbol}}===
{{head|bov|Ký tự|chữ thường|ɛ̃}}
#Chữ ''{{l|bov|Ɛ}} viết hoa'' với dấu mũi hóa.
#:{{quote|bov|NWA'''Ɛ̃''' AMƐ|HỠI ANH EM TA|inline=1}}
==={{ĐM|see}}===
*{{letters|lang=bov|sc=Latn|qual=Bảng chữ cái Latinh tiếng Tuwuli|Aa|Bb|Dd|Ee|Ɛɛ|Ff|Gg|Hh|Ii|Kk|Ll|Mm|Nn|Oo|Ɔɔ|Pp|Rr|Ss|Tt|Uu|Vv|Ww|Yy|Zz|Dz/dz|Gb/gb|Kp/kp|Ny/ny|Ts/ts}}
==={{ĐM|ref}}===
*{{Chú thích web|bov|work=Tuwuli|author=Wycliffe Bible Translators, Inc.|year=2008|entry=ADZUMA 1|entryurl=https://www.bible.com/bible/1752/ACT.1.BOV}}
=={{langname|wwa}}==
==={{ĐM|pron}}===
*{{IPA4|wwa|/ɛ̃/}}
==={{ĐM|symbol}}===
{{head|wwa|Ký tự|chữ thường|ɛ̃}}
#Chữ ''{{l|wwa|Ɛ}}'' viết hoa với dấu mũi hóa.
#:{{ux|wwa|'''Ɛ̃'''ɛ|[[dạ|Dạ]]|inline=1}}
===={{ĐM|see}}====
*{{letters|lang=wwa|sc=Latn|qual=Bảng chữ cái Latinh tiếng Wama|Aa|Bb|Cc|Dd|Ee|Ɛɛ|Ff|Ii|Kk|Kp/kp|Ll|Mm|Nn|Ŋŋ|Oo|Ɔɔ|Pp|Rr|Ss|Tt|Uu|Ww|Yy}}
==={{ĐM|ref}}===
*{{Chú thích web|entry=ɛ|entryurl=https://www.webonary.org/waama/browse/browse-vernacular-english/?key=wwa&letter=%C9%9B&lang=en|editor=Katharina Pope|work=Waama-French Dictionary|year=2014|publisher=SIL International|edition=Preliminary Edition}}
*{{Chú thích web|wwa|entryurl=https://live.bible.is/bible/WWAWYI/MAT/1|entry=Yeesu Krisi yaadarima sa maa Matie ta wari 1|work=Bible.is|year=1994|author=Wycliffe Bible Translators, Inc.}}
4p45nqbp587uxp370zftnmrx4o8yn9j
ɛ̍
0
411904
2513792
2513495
2026-08-13T12:03:35Z
Hiyuune
50834
2513792
wikitext
text/x-wiki
=={{langname|mul}}==
==={{ĐM|desction}}===
{{mul-letter}}
#[[chữ|Chữ]] '''[[ɛ]] viết thường''' với dấu thẳng đứng bên trên.
==={{ĐM|see}}===
{{Latn-script}}
=={{langname|kss}}==
==={{ĐM|letter}}===
{{letter|kss}}
#Chữ cái '''{{l|kss|ɛ}} viết thường''' với dấu thanh trung.
#:{{ux|kss|Chɛ̄n'''ɛ̍'''se̍ē|[[s:Sáng thế Ký|Sáng thế Ký]]|inline=1}}
==={{ĐM|see}}===
*{{letters|lang=kss|sc=Latn|qual=Bảng chữ cái Latinh tiếng Kissi Nam|a|b|c|d|e|ɛ|f|gb|h|i|j|k|kp|l|m|mb|n|nd|ŋ|ŋg|ɲ|o|ɔ|p|s|t|u|w|y}}
==={{ĐM|ref}}===
*{{Chú thích web|kss|work=Yāu̍ Mɛ̄lɛ̄ka̍a̍|author=Bible Society in Liberia|year=2015|entryurl=https://www.bible.com/bible/2241/GEN.1.YM|entry=Chɛ̄nɛ̍se̍ē 1}}
=={{langname|nga}}==
==={{ĐM|letter}}===
{{letter|nga}}
#Chữ '''{{l|nga|ɛ}} viết thường''' với dấu thanh trung.
#:{{ux|nga|bàg'''ɛ̍'''l'''ɛ̍'''|[[bẫy]]|inline=1}}
===={{ĐM|see}}====
*{{letters|lang=nga|sc=Latn|qual=Bảng chữ cái Latinh tiếng Ngbaka|Aa|Bb|ʼB/ʼb|Dd|ʼD/ʼd|Ee|Ɛɛ|Ff|Gg|Gb/gb|Hh|Ii|Kk|Kp/kp|Ll|Mm|Nn|Ng/ng|Ngg/ngg|Oo|Ɔɔ|Ss|Tt|Uu|Vv|Ww|Zz}}
==={{ĐM|pronoun}}===
{{head|nga|Đại từ}}
#[[tôi|Tôi]], [[tao]],...
==={{ĐM|ref}}===
*{{Chú thích web|fr|editor=Marcel Henrix|work=Ngbaka – French Dictionary|year=2020|entry=ɛ|entryurl=https://www.webonary.org/ngbaka/browse/browse-vernacular-french/?key=nga&letter=%C9%9B&lang=en}}
*{{Chú thích web|nga|author=Bible Society of the Democratic Republic of Congo|work=WE NU GALE|year=1995|entry=GBA WENƆ 26|entryurl=https://www.bible.com/bible/2920/PRO.26.NGB}}
=={{langname|yba}}==
==={{ĐM|letter}}===
{{letter|yba}}
#Chữ cái '''{{l|yba|ɛ}} viết thường''' với dấu thanh cao.
#:{{quote|yba|Iju̍da ma̍ Ip'''ɛ̍'''rɛ̄ɛsi ba̍la Ij'''ɛ̍'''ra; '''ɛ̍'''n'''ɛ̍''' yī wā re̍ka Ita̍ma.|Giu-đa bởi Tha-ma sanh Pha-rê và Xa-ra. ([[s:Ma-thi-ơ/1#1:3|Ma-thi-ơ 1:3]])}}
==={{ĐM|see}}===
*{{letters|lang=yba|sc=Latn|qual=Bảng chữ cái Latinh tiếng Yala|a|b|ch|d|e|ɛ|f|g|gb|h|i|j|k|kp|kw|l|m|n|ng|ngm|ny|o|ɔ|p|r|sh|t|u|w|y}}
==={{ĐM|ref}}===
*{{Chú thích web|yba|work=Ye̳ka Onīpīpē̳ Yī O̳wo̳|author=Bible Society of Nigeria|year=2020|entryurl=https://www.bible.com/bible/3079/MAT.1.YBASR|entry=Ima̍tiyu 1}}
gn58axefdsv96slza6ah48lisyje0cj
Mô đun:kk-convert
828
412041
2513796
2026-08-13T12:12:02Z
Hiyuune
50834
Trang mới: “local export = {} local vowels = "үҮиИеЕәӘөӨӯӮАаоОЫыІіЯяЮюЁё" local rsubn = mw.ustring.gsub local tt = { ["ү"]="ü",["Ү"]="Ü",["т"]="t",["Т"]="T",["һ"]="h",["Һ"]="H",["р"]="r",["Р"]="R" ,["ф"]="f",["Ф"]="F",["ш"]="ş",["Ш"]="Ş",["н"]="n",["Н"]="N",["п"]="p",["П"]="P" ,["й"]="i",["Й"]="İ",["л"]="l",["Л"]="L",["з"]="z",["З"]="Z",["е"]="e",["Е"]="E" ,["г"]="g",["Г"]="G",["б"]="b",["Б"]="B",["у"]="u",["У"]="U",["с"…”
2513796
Scribunto
text/plain
local export = {}
local vowels = "үҮиИеЕәӘөӨӯӮАаоОЫыІіЯяЮюЁё"
local rsubn = mw.ustring.gsub
local tt = {
["ү"]="ü",["Ү"]="Ü",["т"]="t",["Т"]="T",["һ"]="h",["Һ"]="H",["р"]="r",["Р"]="R"
,["ф"]="f",["Ф"]="F",["ш"]="ş",["Ш"]="Ş",["н"]="n",["Н"]="N",["п"]="p",["П"]="P"
,["й"]="i",["Й"]="İ",["л"]="l",["Л"]="L",["з"]="z",["З"]="Z",["е"]="e",["Е"]="E"
,["г"]="g",["Г"]="G",["б"]="b",["Б"]="B",["у"]="u",["У"]="U",["с"]="s",["С"]="S"
,["х"]="x",["Х"]="X",["ы"]="y",["Ы"]="Y",["м"]="m",["М"]="M",["о"]="o",["О"]="O"
,["и"]="i",["И"]="I",["ж"]="j",["Ж"]="J",["к"]="k",["К"]="K",["д"]="d",["Д"]="D"
,["в"]="v",["В"]="V",["а"]="a",["А"]="A",["ң"]="ñ",["Ң"]="Ñ",["ә"]="ä",["Ә"]="Ä"
,["э"]="e",["Э"]="E",["ұ"]="ū",["Ұ"]="Ū",["қ"]="q",["Қ"]="Q",["ғ"]="ğ",["Ғ"]="Ğ"
,["і"]="ı",["І"]="ı",["ө"]="ö",["Ө"]="Ö"
--mostly in loanwords from Russian
,["ё"]="io",["Ё"]="İo",["ц"]="ts",["Ц"]="TS",["ч"]="tş",["Ч"]="Tş",["щ"]="ştş",["Щ"]="Ştş"
,["ъ"]="",["Ъ"]="",["э"]="e",["Э"]="E",["ю"]="iu",["Ю"]="İu"
,["я"]="ia",["Я"]="İa",["ӯ"]="u",["Ӯ"]="U"
};
-- Main translit
function export.tr(text, lang, sc)
if type(text) == "table" then
options = {}
text, script = text.args[1], text.args[2]
end
if not sc then
sc = require("Module:languages").getByCode("kk"):findBestScript(text):getCode()
end
if sc ~= "Cyrl" then
return nil
end
text = rsubn(text, "нг([^" .. vowels .. "])", "ң%1")
text = rsubn(text, "Нг([^" .. vowels .. "])", "Ң%1")
text = rsubn(text, "нг$", "ң")
text = rsubn(text, "([^" .. vowels .. "])я", "%1ә")
text = rsubn(text, "([^" .. vowels .. "])Я", "%1Ә")
text = rsubn(text, "[иИ]([яЯ])", "%1")
--currently it is not clear how these two are romanized
text = rsubn(text, "щ", "ш")
text = rsubn(text, "Щ", "Ш")
text = rsubn(text,"[ьЬ]" , "" )
--remove them if their romanization changes
text = rsubn(text, '.', tt)
return text
end
-- NOT MAIN TRANSLIT
-- Yañalif 1938 version
local yanalif = {
["ү"]="y",["Ү"]="Y",["ш"]="c",["Ш"]="C",["й"]="i",["Й"]="I",["б"]="ʙ"
,["ы"]="ь",["Ы"]="Ь",["и"]="i",["И"]="I",["ж"]="ç",["Ж"]="Ç"
,["ң"]="ꞑ",["Ң"]="Ꞑ",["ә"]="ə",["Ә"]="Ə",["ғ"]="ƣ",["Ғ"]="Ƣ"
,["і"]="j",["І"]="J",["ө"]="ɵ",["Ө"]="Ɵ"
--mostly in loanwords from Russian
,["ё"]="io",["Ё"]="Io",["ч"]="c",["Ч"]="Tc",["щ"]="cc",["Щ"]="Cc"
,["ъ"]="ʼ",["Ъ"]="ʼ",["ю"]="iu",["Ю"]="Iu"
,["я"]="ia",["Я"]="Ia"}
--Yañalif 1927 version
local oldyan = {
["ү"]="y",["Ү"]="Y",["ш"]="c",["Ш"]="C",["й"]="i",["Й"]="I",["б"]="ʙ"
,["ы"]="ь",["Ы"]="Ь",["и"]="j",["И"]="J",["ж"]="ç",["Ж"]="Ç"
,["ң"]="ꞑ",["Ң"]="Ꞑ",["ә"]="ə",["Ә"]="Ə",["ғ"]="ƣ",["Ғ"]="Ƣ"
,["і"]="i",["І"]="I",["ө"]="ɵ",["Ө"]="Ɵ",["в"]="ʙ",["В"]="B"
,["ф"]="p",["Ф"]="P",["х"]="h",["Х"]="H",["у"]="v",["У"]="V"
,["ұ"]="u",["Ұ"]="U"
--mostly in loanwords from Russian
,["ё"]="io",["Ё"]="Io",["ч"]="c",["Ч"]="Tc",["щ"]="cc",["Щ"]="Cc"
,["ъ"]="ʼ",["Ъ"]="ʼ",["ю"]="iu",["Ю"]="Iu"
,["я"]="ia",["Я"]="Ia"}
--Pinyin
local pinyin = {
["ү"]="ü",["Ү"]="Ü",["ш"]="x",["Ш"]="X",["й"]="y",["Й"]="Y"
,["ы"]="e",["Ы"]="E",["и"]="y",["И"]="Y",["у"]="w",["У"]="W"
,["ң"]="ng",["Ң"]="ng",["ә"]="ə",["Ә"]="Ə",["ғ"]="ƣ",["Ғ"]="Ƣ"
,["і"]="i",["І"]="I",["ө"]="ɵ",["Ө"]="Ɵ",["һ"]="ⱨ",["Һ"]="Ⱨ"
,["х"]="h",["Х"]="H",["қ"]="ⱪ",["Қ"]="Ⱪ",["е"]="ê",["Е"]="ê"
,["ұ"]="u",["Ұ"]="U"
--mostly in loanwords from Russian
,["ё"]="io",["Ё"]="Io",["ч"]="q",["Ч"]="Q",["щ"]="x",["Щ"]="X"
,["ъ"]="ʼ",["Ъ"]="ʼ",["ю"]="iu",["Ю"]="Iu",["ь"]="",["Ь"]=""
,["я"]="ia",["Я"]="Ia"}
-- Yanalif 1938 version
function export.yanalif(text, lang, sc)
if type(text) == "table" then
options = {}
text, script = text.args[1], text.args[2]
end
if not sc then
sc = require("Module:languages").getByCode("kk"):findBestScript(text):getCode()
end
if sc ~= "Cyrl" then
return nil
end
text = rsubn(text, "нг([^" .. vowels .. "])", "ң%1")
text = rsubn(text, "Нг([^" .. vowels .. "])", "Ң%1")
text = rsubn(text, "нг$", "ң")
text = rsubn(text, "([^" .. vowels .. "])я", "%1ә")
text = rsubn(text, "([^" .. vowels .. "])Я", "%1Ә")
text = rsubn(text, "[иИ]([яЯ])", "%1")
text = rsubn(text, '.', yanalif)
text = rsubn(text, '.', tt)
return text
end
local frontur = "әӘіІ"
local backur = "АаЫы"
local frontround = "үҮөӨ"
local backround = "ӯӮоОҰұ"
local consonant = "[^" .. vowels .. ". -]"
-- Yanalif pre 1927 version
function export.oldyan(text, lang, sc)
if type(text) == "table" then
options = {}
text, script = text.args[1], text.args[2]
end
if not sc then
sc = require("Module:languages").getByCode("kk"):findBestScript(text):getCode()
end
if sc ~= "Cyrl" then
return nil
end
text = rsubn(text, "нг([^" .. vowels .. "])", "ң%1")
text = rsubn(text, "Нг([^" .. vowels .. "])", "Ң%1")
text = rsubn(text, "нг$", "ң")
text = rsubn(text, "([^" .. vowels .. "])я", "%1ә")
text = rsubn(text, "([^" .. vowels .. "])Я", "%1Ә")
-- check preceeding vowel
-- for w
text = rsubn(text, "([" .. backur .. "])(" .. consonant .. ")у", "%1%2ыу")
text = rsubn(text, "([" .. backround .. "])(" .. consonant .. ")у", "%1%2ұу")
text = rsubn(text, "([" .. frontur .. "])(" .. consonant .. ")у", "%1%2іу")
text = rsubn(text, "([" .. frontround .. "])(" .. consonant .. ")у", "%1%2үу")
-- for j
text = rsubn(text, "([" .. backur .. backround .. "])(" .. consonant .. "?)и", "%1%2ыи")
text = rsubn(text, "([" .. frontur .. frontround .. "])(" .. consonant .. "?)и", "%1%2іи")
-- if none, check following vowel
-- for w
text = rsubn(text, "([^" .. vowels .. "])У(" .. consonant .. "?)([" .. backur .. "])", "%1ЫУ%2%3")
text = rsubn(text, "([^" .. vowels .. "])у(" .. consonant .. "?)([" .. backur .. "])", "%1ыу%2%3")
text = rsubn(text, "([^" .. vowels .. "])У(" .. consonant .. "?)([" .. backround .. "])", "%1ҰУ%2%3")
text = rsubn(text, "([^" .. vowels .. "])у(" .. consonant .. "?)([" .. backround .. "])", "%1ұу%2%3")
text = rsubn(text, "([^" .. vowels .. "])У(" .. consonant .. "?)([" .. frontur .. "])", "%1ІУ%2%3")
text = rsubn(text, "([^" .. vowels .. "])у(" .. consonant .. "?)([" .. frontur .. "])", "%1іу%2%3")
text = rsubn(text, "([^" .. vowels .. "])У(" .. consonant .. "?)([" .. frontround .. "])", "%1ҮУ%2%3")
text = rsubn(text, "([^" .. vowels .. "])у(" .. consonant .. "?)([" .. frontround .. "])", "%1үу%2%3")
-- for j
text = rsubn(text, "([^" .. vowels .. "])и(" .. consonant .. "?)([" .. backur .. backround .. "])", "%1ыи%2%3")
text = rsubn(text, "([^" .. vowels .. "])и(" .. consonant .. "?)([" .. frontur .. frontround .. "])", "%1іи%2%3")
text = rsubn(text, "([^" .. vowels .. "])И(" .. consonant .. "?)([" .. backur .. backround .. "])", "%1ЫИ%2%3")
text = rsubn(text, "([^" .. vowels .. "])И(" .. consonant .. "?)([" .. frontur .. frontround .. "])", "%1ІИ%2%3")
text = rsubn(text, "^И(" .. consonant .. "?)([" .. backur .. backround .. "])", "ЫИ%1%2")
text = rsubn(text, "^И(" .. consonant .. "?)([" .. frontur .. frontround .. "])", "ІИ%1%2")
-- final touches
text = rsubn(text, "[иИ]([яЯ])", "%1")
text = rsubn(text, '.', oldyan)
text = rsubn(text, '.', tt)
return text
end
-- Pinyin
function export.pinyin(text, lang, sc)
if type(text) == "table" then
options = {}
text, script = text.args[1], text.args[2]
end
if not sc then
sc = require("Module:languages").getByCode("kk"):findBestScript(text):getCode()
end
if sc ~= "Cyrl" then
return nil
end
text = rsubn(text, "([^" .. vowels .. "])я", "%1ә")
text = rsubn(text, "([^" .. vowels .. "])Я", "%1Ә")
text = rsubn(text, "[иИ]([яЯ])", "%1")
text = rsubn(text, "([^" .. vowels .. "])[у]", "%1ұу")
text = rsubn(text, "([^" .. vowels .. "])[У]", "%1ұУ")
text = rsubn(text, "^[у]([^" .. vowels .. "])", "ұу%1")
text = rsubn(text, "^[У]([^" .. vowels .. "])", "ұУ%1")
text = rsubn(text, '.', pinyin)
text = rsubn(text, '.', tt)
return text
end
return export
j8c91uxbs7hb9rpdqvnc1g97kj38gl5
شيدې
0
412042
2513822
2026-08-13T13:04:34Z
Kelly zhrm
58416
Trang mới: “=={{langname|ps}}== ==={{ĐM|etym}}=== {{inh+|ps|ira-pro|*xšwiptah}}. ==={{ĐM|pron}}=== * {{IPA4|ps|/ʃide/}} ==={{ĐM|n}}=== {{ps-noun|g=f|tr=shide}} # [[sữa|Sữa]].”
2513822
wikitext
text/x-wiki
=={{langname|ps}}==
==={{ĐM|etym}}===
{{inh+|ps|ira-pro|*xšwiptah}}.
==={{ĐM|pron}}===
* {{IPA4|ps|/ʃide/}}
==={{ĐM|n}}===
{{ps-noun|g=f|tr=shide}}
# [[sữa|Sữa]].
nnfzo1vxa44owg33jfspcmu2tuevo6y
阿爾法粒子
0
412043
2513824
2026-08-13T13:10:05Z
Kelly zhrm
58416
Trang mới: “=={{langname|zh}}== {{zh-forms|s=阿尔法粒子|type=32}} ==={{ĐM|pron}}=== {{zh-pron |m=ā'ěrfǎ lìzǐ |c=aa3 ji5 faat3 lap1 zi2 |cat=n }} ==={{ĐM|n}}=== {{head|zh|Danh từ}} # [[hạt|Hạt]] [[alpha]]. ===={{ĐM|syn}}==== * {{zh-l|α粒子}} * {{zh-l|阿爾發粒子}} * {{zh-l|阿耳法粒子}} * {{zh-l|阿伐粒子}} * {{zh-l|阿爾伐粒子}} * {{zh-l|阿爾發質點}} * {{zh-l|阿爾伐質點}} * {{zh-l|甲種粒子}}”
2513824
wikitext
text/x-wiki
=={{langname|zh}}==
{{zh-forms|s=阿尔法粒子|type=32}}
==={{ĐM|pron}}===
{{zh-pron
|m=ā'ěrfǎ lìzǐ
|c=aa3 ji5 faat3 lap1 zi2
|cat=n
}}
==={{ĐM|n}}===
{{head|zh|Danh từ}}
# [[hạt|Hạt]] [[alpha]].
===={{ĐM|syn}}====
* {{zh-l|α粒子}}
* {{zh-l|阿爾發粒子}}
* {{zh-l|阿耳法粒子}}
* {{zh-l|阿伐粒子}}
* {{zh-l|阿爾伐粒子}}
* {{zh-l|阿爾發質點}}
* {{zh-l|阿爾伐質點}}
* {{zh-l|甲種粒子}}
m6os3xq31dzysz4h3lfy89ab4kzgfty
Thể loại:Từ đánh vần với 法 tiếng Trung Quốc
14
412044
2513825
2026-08-13T13:11:30Z
Kelly zhrm
58416
Trang mới: “{{auto cat}}”
2513825
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Tiếng Việt Việt Nam
14
412045
2513833
2026-08-13T13:33:06Z
Hiyuune
50834
/* */
2513833
wikitext
text/x-wiki
{{auto cat|lect=1|lang=vi|Việt Nam}}
sot9mlry15qlord4psx02694mah7ik6
Thể loại:Tiếng Việt Trung Bộ
14
412046
2513834
2026-08-13T13:39:11Z
Hiyuune
50834
/* */
2513834
wikitext
text/x-wiki
{{auto cat|lect=1|lang=vi|[[Trung Bộ]], [[Việt Nam]]|wp=Trung Bộ,Nghệ Tĩnh,Thanh Hóa,Nghệ An,Hà Tĩnh,Quảng Bình,Quảng Trị,Huế,Bắc Trung Bộ|cat=Tiếng Việt Việt Nam}}
1xlkbtlosnlivgg424h4uytfbvnnz05
Thể loại:Tiếng Việt Đồng bằng Sông Cửu Long
14
412047
2513847
2026-08-13T15:04:31Z
Hiyuune
50834
Trang mới: “{{auto cat|lect=1|lang=vi|Đồng bằng Sông Cửu Long|cat=Tiếng Việt Nam Việt Nam}}”
2513847
wikitext
text/x-wiki
{{auto cat|lect=1|lang=vi|Đồng bằng Sông Cửu Long|cat=Tiếng Việt Nam Việt Nam}}
033wcaqjcftvh1rv3bwam6zx9vxrd3m
karamihan
0
412048
2513848
2026-08-13T15:28:29Z
Hiyuune
50834
Trang mới: “=={{langname|tl}}== ==={{section|etym}}=== {{ety|tl|:af|dami|ka- -han|text=+|tree=1}} ==={{section|pron}}=== {{tl-pr}} ==={{section|n}}=== {{tl-noun|b=+}} # [[đa số|Đa số]], [[số đông]]. #: {{syn|tl|nakararami|mayorya}} #: {{ant|tl|minorya}} ===={{section|drv}}==== {{col|tl|sangkaramihan}} ==={{section|further}}=== * {{R:Pambansang Diksiyonaryo}}”
2513848
wikitext
text/x-wiki
=={{langname|tl}}==
==={{section|etym}}===
{{ety|tl|:af|dami|ka- -han|text=+|tree=1}}
==={{section|pron}}===
{{tl-pr}}
==={{section|n}}===
{{tl-noun|b=+}}
# [[đa số|Đa số]], [[số đông]].
#: {{syn|tl|nakararami|mayorya}}
#: {{ant|tl|minorya}}
===={{section|drv}}====
{{col|tl|sangkaramihan}}
==={{section|further}}===
* {{R:Pambansang Diksiyonaryo}}
g7rby2uuaw4izp7s2b4odpb1bz1t3bw
กระเบื้อง
0
412049
2513857
2026-08-13T15:38:39Z
Hiyuune
50834
Trang mới: “=={{langname|th}}== ==={{section|etym}}=== Cùng gốc với {{cog|sou|เบื้อง}}, {{cog|lo|ກະເບຶ້ອງ}}, {{cog|shn|မိူင်ႈ}} hoặc {{m|shn|ဝိူင်ႈ}}. ==={{section|pron}}=== {{th-pron|กฺระ-เบื้อง}} ==={{section|n}}=== {{th-noun|แผ่น}} # [[gạch|Gạch]]; [[ngói]].”
2513857
wikitext
text/x-wiki
=={{langname|th}}==
==={{section|etym}}===
Cùng gốc với {{cog|sou|เบื้อง}}, {{cog|lo|ກະເບຶ້ອງ}}, {{cog|shn|မိူင်ႈ}} hoặc {{m|shn|ဝိူင်ႈ}}.
==={{section|pron}}===
{{th-pron|กฺระ-เบื้อง}}
==={{section|n}}===
{{th-noun|แผ่น}}
# [[gạch|Gạch]]; [[ngói]].
du0b0kp9aarcx41mcc9ipesxfj3tp5z
မိူင်ႈ
0
412050
2513858
2026-08-13T15:39:56Z
Hiyuune
50834
Trang mới: “=={{langname|shn}}== ==={{section|pron}}=== {{shn-pron|မိူင်ႈ}} ==={{section|n}}=== {{shn-noun}} # [[gạch|Gạch]]; [[ngói]].”
2513858
wikitext
text/x-wiki
=={{langname|shn}}==
==={{section|pron}}===
{{shn-pron|မိူင်ႈ}}
==={{section|n}}===
{{shn-noun}}
# [[gạch|Gạch]]; [[ngói]].
1g7l3mwfoyhcjge1p6epme743nsdb58
Vần:Tiếng Shan/ɤŋ
0
412051
2513859
2026-08-13T15:43:15Z
Hiyuune
50834
Trang mới: “{{rhymes nav|shn|ɤŋ}} =={{section|pron}}== * {{IPA|shn|/ɤŋ/}} =={{section|rhymes}}== ===1 âm tiết=== {{col|shn|ၵႃႇလႃႉႁႃႈလႅင်မိူင်း|ၵူၼ်းမိူင်း|ၵၢၼ်မိူင်း|ၶိူင်ႈ|ၶိူင်ႈပိူင်|ငိူင်း|ၸဝ်ႈၵႅမ်မိူင်း|ၸဝ်ႈၼေႃႇမိူင်း|ၸိုင်ႈမိူင်း|ၸိူင်း|ၸိူင်ႉ|…”
2513859
wikitext
text/x-wiki
{{rhymes nav|shn|ɤŋ}}
=={{section|pron}}==
* {{IPA|shn|/ɤŋ/}}
=={{section|rhymes}}==
===1 âm tiết===
{{col|shn|ၵႃႇလႃႉႁႃႈလႅင်မိူင်း|ၵူၼ်းမိူင်း|ၵၢၼ်မိူင်း|ၶိူင်ႈ|ၶိူင်ႈပိူင်|ငိူင်း|ၸဝ်ႈၵႅမ်မိူင်း|ၸဝ်ႈၼေႃႇမိူင်း|ၸိုင်ႈမိူင်း|ၸိူင်း|ၸိူင်ႉ|တမ်ႇတိူင်း|ထိူင်း|ၼွၵ်ႈမိူင်း|ပၵ်းပိူင်|ပိူင်|ပိူင်ႇ|ပိူင်ႈ|ပႅၵ်ႇပိူင်ႈ|ပၢၵ်ႇပီမိူင်း|ၽွင်းမိူင်း|ၽိတ်းပိူင်ႈ|ၽိူင်း|ၾိူင်း|မိူင်|မိူင်း|မၢၵ်ႇၽိူင်း|မၢၼ်ႈမိူင်း|ယိူင်း|ယိူင်ႈ|လၵ်းမိူင်း|လိၼ်မိူင်း|လိူင်|လိူင်း|လိူင်ႇ|လူတ်ႉၶိူင်ႈ|ဝႂ်ၶၢမ်ႈမိူင်း|ဝိူင်း|ဝၢၼ်ႈမိူင်း|သိူင်ႇ|ႁိူင်း|ႁူဝ်ပၢၵ်ႇပီမိူင်း|ဢိူင်ႇ|ဢုပ်ႉပိူင်ႇ}}
[[mnw:ကာရန်:သေံဇၞော်/ɤŋ]]
[[shn:တူၼ်းၸၢပ်ႈလႅပ်ႈ:တႆး/ɤŋ]]
[[th:สัมผัส:ภาษาไทใหญ่/ɤŋ]]
75gn0z4wm31nba554itglf7giasje2b
固體
0
412052
2513863
2026-08-13T15:50:08Z
Hiyuune
50834
Trang mới: “{{also|固体}} =={{langname|zh}}== {{zh-forms|s=固体}} ==={{section|pron}}=== {{zh-pron |m=gùtǐ |ma=Zh-gùti.ogg |c=gu3 tai2 |ca=LL-Q9186 (yue)-Justinrleung-固體.wav |h=pfs=ku-thí |mn=kò͘-thé |mn-t=gu3 ti2 |cat=n }} ==={{section|n}}=== {{head|zh|Danh từ}} # Thể [[rắn]], [[đặc]]. #: {{zh-x|固體 狀態|Trạng thái thể '''rắn'''.}} ===={{section|see}}==== * {{zh-l|流體}} * {{zh-l|氣體}} * {{zh-l|液體}}”
2513863
wikitext
text/x-wiki
{{also|固体}}
=={{langname|zh}}==
{{zh-forms|s=固体}}
==={{section|pron}}===
{{zh-pron
|m=gùtǐ
|ma=Zh-gùti.ogg
|c=gu3 tai2
|ca=LL-Q9186 (yue)-Justinrleung-固體.wav
|h=pfs=ku-thí
|mn=kò͘-thé
|mn-t=gu3 ti2
|cat=n
}}
==={{section|n}}===
{{head|zh|Danh từ}}
# Thể [[rắn]], [[đặc]].
#: {{zh-x|固體 狀態|Trạng thái thể '''rắn'''.}}
===={{section|see}}====
* {{zh-l|流體}}
* {{zh-l|氣體}}
* {{zh-l|液體}}
2db7eo13n87whnipss81c3aq2xt5wt7
2513865
2513863
2026-08-13T15:52:10Z
Hiyuune
50834
/* Tiếng Trung Quốc */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2513865
wikitext
text/x-wiki
{{also|固体}}
=={{langname|ja}}==
{{ja-kanjitab|こ|たい|yomi=on}}
{{ja-see|固体}}
{{-}}
=={{langname|zh}}==
{{zh-forms|s=固体}}
==={{section|pron}}===
{{zh-pron
|m=gùtǐ
|ma=Zh-gùti.ogg
|c=gu3 tai2
|ca=LL-Q9186 (yue)-Justinrleung-固體.wav
|h=pfs=ku-thí
|mn=kò͘-thé
|mn-t=gu3 ti2
|cat=n
}}
==={{section|n}}===
{{head|zh|Danh từ}}
# Thể [[rắn]], [[đặc]].
#: {{zh-x|固體 狀態|Trạng thái thể '''rắn'''.}}
===={{section|see}}====
* {{zh-l|流體}}
* {{zh-l|氣體}}
* {{zh-l|液體}}
nildjsjvkowjimrsetfscikespkanow
2513866
2513865
2026-08-13T15:52:44Z
Hiyuune
50834
/* Tiếng Trung Quốc */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2513866
wikitext
text/x-wiki
{{also|固体}}
=={{langname|ja}}==
{{ja-kanjitab|こ|たい|yomi=on}}
{{ja-see|固体}}
{{-}}
=={{langname|ko}}==
{{ko-hanjatab}}
==={{section|n}}===
{{ko-noun|hj|rv=|hangeul=고체}}
# {{hanja form of|고체|thể [[rắn]]}}
=={{langname|zh}}==
{{zh-forms|s=固体}}
==={{section|pron}}===
{{zh-pron
|m=gùtǐ
|ma=Zh-gùti.ogg
|c=gu3 tai2
|ca=LL-Q9186 (yue)-Justinrleung-固體.wav
|h=pfs=ku-thí
|mn=kò͘-thé
|mn-t=gu3 ti2
|cat=n
}}
==={{section|n}}===
{{head|zh|Danh từ}}
# Thể [[rắn]], [[đặc]].
#: {{zh-x|固體 狀態|Trạng thái thể '''rắn'''.}}
===={{section|see}}====
* {{zh-l|流體}}
* {{zh-l|氣體}}
* {{zh-l|液體}}
1occmhcq2x5k5fp6p02h3q0r2yply27
2513867
2513866
2026-08-13T15:53:07Z
Hiyuune
50834
/* Tiếng Nhật */ (sử dụng [[MediaWiki:Gadget-AjaxEdit.js|AjaxEdit]])
2513867
wikitext
text/x-wiki
{{also|固体}}
=={{langname|vi}}==
{{vi-hantutab}}
==={{section|adv}}===
{{vi-adv|sc=Hani}}
# {{vi-Nom form of|có thể}}
=={{langname|ja}}==
{{ja-kanjitab|こ|たい|yomi=on}}
{{ja-see|固体}}
{{-}}
=={{langname|ko}}==
{{ko-hanjatab}}
==={{section|n}}===
{{ko-noun|hj|rv=|hangeul=고체}}
# {{hanja form of|고체|thể [[rắn]]}}
=={{langname|zh}}==
{{zh-forms|s=固体}}
==={{section|pron}}===
{{zh-pron
|m=gùtǐ
|ma=Zh-gùti.ogg
|c=gu3 tai2
|ca=LL-Q9186 (yue)-Justinrleung-固體.wav
|h=pfs=ku-thí
|mn=kò͘-thé
|mn-t=gu3 ti2
|cat=n
}}
==={{section|n}}===
{{head|zh|Danh từ}}
# Thể [[rắn]], [[đặc]].
#: {{zh-x|固體 狀態|Trạng thái thể '''rắn'''.}}
===={{section|see}}====
* {{zh-l|流體}}
* {{zh-l|氣體}}
* {{zh-l|液體}}
n1dxoj8d19ve60fpp2em70r5ig8z7qx
こたい
0
412053
2513879
2026-08-14T02:23:36Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-see|個体|固体}}”
2513879
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-see|個体|固体}}
h5f7fs3s567cucxs3inla4wjyi55jsm
kotai
0
412054
2513880
2026-08-14T02:24:01Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== ==={{ĐM|rom}}=== {{ja-romaji}} # {{ja-romanization of|こたい}}”
2513880
wikitext
text/x-wiki
=={{langname|ja}}==
==={{ĐM|rom}}===
{{ja-romaji}}
# {{ja-romanization of|こたい}}
mu14nbvlqmtekfq8g6e23vyboloe4fg
gùtǐ
0
412055
2513881
2026-08-14T02:25:16Z
WhoAlone
40420
Trang mới: “{{also|guti}} =={{langname|cmn}}== ==={{ĐM|rom}}=== {{cmn-pinyin}} # {{cmn-pinyin of|固體}}”
2513881
wikitext
text/x-wiki
{{also|guti}}
=={{langname|cmn}}==
==={{ĐM|rom}}===
{{cmn-pinyin}}
# {{cmn-pinyin of|固體}}
4oj7wlsaows538xl6tdon91t4o55gc8
고체
0
412056
2513882
2026-08-14T02:28:27Z
WhoAlone
40420
Trang mới: “=={{langname|ko}}== ==={{ĐM|etym}}=== {{ko-etym-Sino|固體}}. ==={{ĐM|pron}}=== {{ko-IPA}} ==={{ĐM|noun}}=== {{ko-noun|hanja=固體}} # Thể [[rắn]]. #: {{cot|ko|기체(氣體)|액체(液體)}}”
2513882
wikitext
text/x-wiki
=={{langname|ko}}==
==={{ĐM|etym}}===
{{ko-etym-Sino|固體}}.
==={{ĐM|pron}}===
{{ko-IPA}}
==={{ĐM|noun}}===
{{ko-noun|hanja=固體}}
# Thể [[rắn]].
#: {{cot|ko|기체(氣體)|액체(液體)}}
bpcs0f359qmib11r291gohk1gswcjbd
亜美利
0
412057
2513883
2026-08-14T02:32:19Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-kanjitab|あ|み|り}} ==={{ĐM|pr-noun}}=== {{ja-pos|proper|あみり}} # {{given name|ja|nữ|sort=あみり}}”
2513883
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-kanjitab|あ|み|り}}
==={{ĐM|pr-noun}}===
{{ja-pos|proper|あみり}}
# {{given name|ja|nữ|sort=あみり}}
s8uorizb207aw0r4p9a9uh0qd9s6kfj
亜美李
0
412058
2513884
2026-08-14T02:32:22Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-kanjitab|あ|み|り}} ==={{ĐM|pr-noun}}=== {{ja-pos|proper|あみり}} # {{given name|ja|nữ|sort=あみり}}”
2513884
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-kanjitab|あ|み|り}}
==={{ĐM|pr-noun}}===
{{ja-pos|proper|あみり}}
# {{given name|ja|nữ|sort=あみり}}
s8uorizb207aw0r4p9a9uh0qd9s6kfj
亜美梨
0
412059
2513885
2026-08-14T02:32:24Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-kanjitab|あ|み|り}} ==={{ĐM|pr-noun}}=== {{ja-pos|proper|あみり}} # {{given name|ja|nữ|sort=あみり}}”
2513885
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-kanjitab|あ|み|り}}
==={{ĐM|pr-noun}}===
{{ja-pos|proper|あみり}}
# {{given name|ja|nữ|sort=あみり}}
s8uorizb207aw0r4p9a9uh0qd9s6kfj
亜美理
0
412060
2513886
2026-08-14T02:32:27Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-kanjitab|あ|み|り}} ==={{ĐM|pr-noun}}=== {{ja-pos|proper|あみり}} # {{given name|ja|nữ|sort=あみり}}”
2513886
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-kanjitab|あ|み|り}}
==={{ĐM|pr-noun}}===
{{ja-pos|proper|あみり}}
# {{given name|ja|nữ|sort=あみり}}
s8uorizb207aw0r4p9a9uh0qd9s6kfj
亜美里
0
412061
2513887
2026-08-14T02:32:29Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-kanjitab|あ|み|り}} ==={{ĐM|pr-noun}}=== {{ja-pos|proper|あみり}} # {{given name|ja|nữ|sort=あみり}}”
2513887
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-kanjitab|あ|み|り}}
==={{ĐM|pr-noun}}===
{{ja-pos|proper|あみり}}
# {{given name|ja|nữ|sort=あみり}}
s8uorizb207aw0r4p9a9uh0qd9s6kfj
亜美麗
0
412062
2513888
2026-08-14T02:32:32Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-kanjitab|あ|み|り}} ==={{ĐM|pr-noun}}=== {{ja-pos|proper|あみり}} # {{given name|ja|nữ|sort=あみり}}”
2513888
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-kanjitab|あ|み|り}}
==={{ĐM|pr-noun}}===
{{ja-pos|proper|あみり}}
# {{given name|ja|nữ|sort=あみり}}
s8uorizb207aw0r4p9a9uh0qd9s6kfj
亜美莉
0
412063
2513889
2026-08-14T02:32:35Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-kanjitab|あ|み|り}} ==={{ĐM|pr-noun}}=== {{ja-pos|proper|あみり}} # {{given name|ja|nữ|sort=あみり}}”
2513889
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-kanjitab|あ|み|り}}
==={{ĐM|pr-noun}}===
{{ja-pos|proper|あみり}}
# {{given name|ja|nữ|sort=あみり}}
s8uorizb207aw0r4p9a9uh0qd9s6kfj
亜未梨
0
412064
2513890
2026-08-14T02:32:37Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-kanjitab|あ|み|り}} ==={{ĐM|pr-noun}}=== {{ja-pos|proper|あみり}} # {{given name|ja|nữ|sort=あみり}}”
2513890
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-kanjitab|あ|み|り}}
==={{ĐM|pr-noun}}===
{{ja-pos|proper|あみり}}
# {{given name|ja|nữ|sort=あみり}}
s8uorizb207aw0r4p9a9uh0qd9s6kfj
亜未莉
0
412065
2513891
2026-08-14T02:32:43Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-kanjitab|あ|み|り}} ==={{ĐM|pr-noun}}=== {{ja-pos|proper|あみり}} # {{given name|ja|nữ|sort=あみり}}”
2513891
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-kanjitab|あ|み|り}}
==={{ĐM|pr-noun}}===
{{ja-pos|proper|あみり}}
# {{given name|ja|nữ|sort=あみり}}
s8uorizb207aw0r4p9a9uh0qd9s6kfj
亜巳利
0
412066
2513892
2026-08-14T02:32:45Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-kanjitab|あ|み|り}} ==={{ĐM|pr-noun}}=== {{ja-pos|proper|あみり}} # {{given name|ja|nữ|sort=あみり}}”
2513892
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-kanjitab|あ|み|り}}
==={{ĐM|pr-noun}}===
{{ja-pos|proper|あみり}}
# {{given name|ja|nữ|sort=あみり}}
s8uorizb207aw0r4p9a9uh0qd9s6kfj
亞美利
0
412067
2513893
2026-08-14T02:33:44Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-see|亜美利}}”
2513893
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-see|亜美利}}
4v2wnci3uds1rm09un9w143jorqgvb0
亞美李
0
412068
2513894
2026-08-14T02:34:48Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-see|亜美李}}”
2513894
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-see|亜美李}}
c8a0c1owmekbgd24hqr9tq9r4vcdalv
亞美梨
0
412069
2513895
2026-08-14T02:35:55Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-see|亜美梨}}”
2513895
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-see|亜美梨}}
q1la5bk6tob2kex31p03njjzwl0yd30
亞美理
0
412070
2513896
2026-08-14T02:36:20Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-see|亜美理}}”
2513896
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-see|亜美理}}
8ea1pvr5s3l2j9hxxywnjvsxt5506ra
亞美里
0
412071
2513897
2026-08-14T02:36:39Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-see|亜美里}}”
2513897
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-see|亜美里}}
s8u9922hsvqhcraqb8jp5stu1on1pkb
亞美麗
0
412072
2513898
2026-08-14T02:38:19Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-see|亜美麗}}”
2513898
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-see|亜美麗}}
oj5vtubo8whz83nx8qte7ftos6skhn3
亞美莉
0
412073
2513899
2026-08-14T02:38:33Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-see|亜美莉}}”
2513899
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-see|亜美莉}}
2jwvxd6kh6yllln2345kihlnmls6y4y
亞未梨
0
412074
2513900
2026-08-14T02:38:48Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-see|亜未梨}}”
2513900
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-see|亜未梨}}
es5i8qey5bp23ptzf432bff8yx07zs3
亞未莉
0
412075
2513901
2026-08-14T02:39:07Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-see|亜未莉}}”
2513901
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-see|亜未莉}}
hsj364g3q0bww3azcl6mn8ldmilizvs
亞巳利
0
412076
2513902
2026-08-14T02:39:23Z
WhoAlone
40420
Trang mới: “=={{langname|ja}}== {{ja-see|亜巳利}}”
2513902
wikitext
text/x-wiki
=={{langname|ja}}==
{{ja-see|亜巳利}}
bqfzrcu8xvq3tvsp52ujuvrwhm20cjv
ᠬᠣᠱᠣᠢ ᠬᠠᠲᠤᠨ
0
412077
2513904
2026-08-14T02:53:45Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|etym}}=== {{der+|cmg|mnc|ᡥᠣᡧᠣᡳ ᡶᡠᠵᡳᠨ}}. ==={{ĐM|pron}}=== {{rfp|cmg}} ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # {{lb|cmg|historical}} {{rfdef|cmg}} ===={{ĐM|decl}}==== {{rfinfl|cmg|noun}} ===={{ĐM|desc}}==== * {{desc|mn|ᠬᠣᠱᠣᠢ ᠬᠠᠲᠤᠨ}} *: {{desc|mn|хошой хатан|sclang=1}}”
2513904
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|etym}}===
{{der+|cmg|mnc|ᡥᠣᡧᠣᡳ ᡶᡠᠵᡳᠨ}}.
==={{ĐM|pron}}===
{{rfp|cmg}}
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# {{lb|cmg|historical}} {{rfdef|cmg}}
===={{ĐM|decl}}====
{{rfinfl|cmg|noun}}
===={{ĐM|desc}}====
* {{desc|mn|ᠬᠣᠱᠣᠢ ᠬᠠᠲᠤᠨ}}
*: {{desc|mn|хошой хатан|sclang=1}}
g2hy349qk1khxik9ybb2569wkegg0k4
Thể loại:Yêu cầu định nghĩa mục từ tiếng Mông Cổ cổ điển
14
412078
2513905
2026-08-14T02:57:03Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513905
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:Yêu cầu liên quan đến tiếng Mông Cổ cổ điển
14
412079
2513906
2026-08-14T02:57:14Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513906
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
amarrásseis
0
412080
2513907
2026-08-14T03:00:15Z
Hiyuune
50834
+ pt, gl
2513907
wikitext
text/x-wiki
=={{langname|pt}}==
==={{section|v}}===
{{head|pt|Biến thể hình thái động từ}}
# {{pt-verb form of|amarrar}}
=={{langname|gl}}==
==={{section|v}}===
{{head|gl|Biến thể hình thái động từ}}
# {{gl-reinteg-verb form of|amarrar}}
k201vmzpx562y1unolpk9hle9qxsf8a
mõtūks
0
412081
2513908
2026-08-14T03:03:38Z
Hiyuune
50834
Trang mới: “=={{langname|liv}}== ==={{section|etym}}=== Từ {{inh|liv|urj-fin-pro|*mët'os}}. ==={{section|pron}}=== {{liv-p}} ==={{section|n}}=== {{head|liv|Danh từ}} # {{taxfmt|Tetrao urogallus|loài}} ===={{section|decl}}==== {{liv-decl|205}} ==={{section|ref}}=== * {{R:liv:LEL|mõtūks}}”
2513908
wikitext
text/x-wiki
=={{langname|liv}}==
==={{section|etym}}===
Từ {{inh|liv|urj-fin-pro|*mët'os}}.
==={{section|pron}}===
{{liv-p}}
==={{section|n}}===
{{head|liv|Danh từ}}
# {{taxfmt|Tetrao urogallus|loài}}
===={{section|decl}}====
{{liv-decl|205}}
==={{section|ref}}===
* {{R:liv:LEL|mõtūks}}
k0iq8y1mk3nhf4mynw4m3icda6daq8a
ᠮᠠᠾᠠᠠ
0
412082
2513909
2026-08-14T03:04:53Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|etym}}=== {{bor+|cmg|sa|महा}}, {{mention|sa|महत्}}, {{mention|sa|महत्}}, từ {{der|cmg|inc-pro|*maźʰā́}}, từ {{der|cmg|iir-pro|*maȷ́ʰā́}}, từ {{der|cmg|ine-pro|*méǵh₂s}}. ==={{ĐM|pron}}=== * {{IPA4|cmg|/ma.ɦɑː/}} * {{hyph|cmg|ᠮᠠ|ᠾᠠᠠ}} ==={{ĐM|prefix}}=== {{head|cmg|Tiền tố}} # [[vĩ đại]]. # [[to]], [[lớn]]. # [[đại]]-, [[siêu]]-”
2513909
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|etym}}===
{{bor+|cmg|sa|महा}}, {{mention|sa|महत्}}, {{mention|sa|महत्}}, từ {{der|cmg|inc-pro|*maźʰā́}}, từ {{der|cmg|iir-pro|*maȷ́ʰā́}}, từ {{der|cmg|ine-pro|*méǵh₂s}}.
==={{ĐM|pron}}===
* {{IPA4|cmg|/ma.ɦɑː/}}
* {{hyph|cmg|ᠮᠠ|ᠾᠠᠠ}}
==={{ĐM|prefix}}===
{{head|cmg|Tiền tố}}
# [[vĩ đại]].
# [[to]], [[lớn]].
# [[đại]]-, [[siêu]]-
2my9bkrb5cdw2h31m2ea56wqdjwstk2
Thể loại:Tiền tố tiếng Mông Cổ cổ điển
14
412083
2513910
2026-08-14T03:05:07Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513910
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
nominatīv
0
412084
2513911
2026-08-14T03:05:14Z
Hiyuune
50834
Trang mới: “{{also|nominativ|Nominativ}} =={{langname|lv}}== ==={{section|n}}=== {{head|lv|Biến thể hình thái danh từ}} # {{inflection of|lv|nominatīvs||voc|s}} =={{langname|liv}}== ==={{section|etym}}=== Cuối cùng từ {{der|liv|la|nōminātīvus}}. {{etystub|liv}} ==={{section|pron}}=== {{liv-p}} ==={{section|n}}=== {{head|liv|Danh từ}} # {{lb|liv|grammatical case}} [[danh cách|Danh cách]]. ===={{section|decl}}==== {{liv-decl|141|n=sg}}”
2513911
wikitext
text/x-wiki
{{also|nominativ|Nominativ}}
=={{langname|lv}}==
==={{section|n}}===
{{head|lv|Biến thể hình thái danh từ}}
# {{inflection of|lv|nominatīvs||voc|s}}
=={{langname|liv}}==
==={{section|etym}}===
Cuối cùng từ {{der|liv|la|nōminātīvus}}. {{etystub|liv}}
==={{section|pron}}===
{{liv-p}}
==={{section|n}}===
{{head|liv|Danh từ}}
# {{lb|liv|grammatical case}} [[danh cách|Danh cách]].
===={{section|decl}}====
{{liv-decl|141|n=sg}}
6hhl5xrtffxhng2cazg654wvu3obawg
jegā
0
412085
2513912
2026-08-14T03:07:47Z
Hiyuune
50834
Trang mới: “=={{langname|liv}}== ==={{section|etym}}=== Từ {{inh|liv|urj-fin-pro|*joka|*ekä}}. ==={{section|pron}}=== {{liv-p}} ==={{section|det}}=== {{head|liv|Từ hạn định}} # [[mỗi|Mỗi]], [[mọi]]. ===={{section|decl}}==== {{liv-decl|2}} ==={{section|ref}}=== * {{R:liv:LEL|jegā}}”
2513912
wikitext
text/x-wiki
=={{langname|liv}}==
==={{section|etym}}===
Từ {{inh|liv|urj-fin-pro|*joka|*ekä}}.
==={{section|pron}}===
{{liv-p}}
==={{section|det}}===
{{head|liv|Từ hạn định}}
# [[mỗi|Mỗi]], [[mọi]].
===={{section|decl}}====
{{liv-decl|2}}
==={{section|ref}}===
* {{R:liv:LEL|jegā}}
53k0m3s0r458nic06korwtcw05tp5ub
jõka
0
412086
2513913
2026-08-14T03:11:07Z
Hiyuune
50834
Trang mới: “=={{langname|vot}}== ==={{section|etym}}=== Từ {{inh|vot|urj-fin-pro|*joka}}. ==={{section|pron}}=== {{vot-p|J=+|C=+|E=+}} ==={{section|det}}=== {{head|vot|Từ hạn định}} # {{lb|vot|không biến tố}} [[mỗi|Mỗi]]. ==={{section|ref}}=== * {{R:vot:VKS|jõka}}”
2513913
wikitext
text/x-wiki
=={{langname|vot}}==
==={{section|etym}}===
Từ {{inh|vot|urj-fin-pro|*joka}}.
==={{section|pron}}===
{{vot-p|J=+|C=+|E=+}}
==={{section|det}}===
{{head|vot|Từ hạn định}}
# {{lb|vot|không biến tố}} [[mỗi|Mỗi]].
==={{section|ref}}===
* {{R:vot:VKS|jõka}}
hyj0cdnmfamfevoz9s6gvjoizdj5j00
Thể loại:Hình vị tiếng Mông Cổ cổ điển
14
412087
2513914
2026-08-14T03:20:26Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513914
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
ᠹᠦᠷᠪᠦ
0
412088
2513915
2026-08-14T03:22:17Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|etym}}=== {{bor+|cmg|xct|ཕུར་བུ|t=Sao Mộc; Thứ Năm}}. ==={{ĐM|pron}}=== {{rfp|cmg}} ==={{ĐM|noun}}=== {{head|cmg|Danh từ|tr=phürbü}} # [[thứ Năm]]. ===={{ĐM|decl}}==== {{rfinfl|cmg|noun}} ===={{ĐM|desc}}==== * {{desc|mn|ᠫᠦᠷᠪᠦ}} *: {{desc|mn|пүрэв|sclang=1}} * {{desc|bua|пүрбэ|bor=1}} ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ|tr=phürbü}} # {{given name|cmg|male|from=tiếng Tây Tạng…”
2513915
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|etym}}===
{{bor+|cmg|xct|ཕུར་བུ|t=Sao Mộc; Thứ Năm}}.
==={{ĐM|pron}}===
{{rfp|cmg}}
==={{ĐM|noun}}===
{{head|cmg|Danh từ|tr=phürbü}}
# [[thứ Năm]].
===={{ĐM|decl}}====
{{rfinfl|cmg|noun}}
===={{ĐM|desc}}====
* {{desc|mn|ᠫᠦᠷᠪᠦ}}
*: {{desc|mn|пүрэв|sclang=1}}
* {{desc|bua|пүрбэ|bor=1}}
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ|tr=phürbü}}
# {{given name|cmg|male|from=tiếng Tây Tạng cổ điển|meaning=thứ Năm}}
===={{ĐM|decl}}====
{{rfinfl|cmg|noun}}
===={{ĐM|desc}}====
* {{desc|mn|ᠫᠦᠷᠪᠦ}}
*: {{desc|mn|Пүрэв|sclang=1}}
* {{desc|bua|Пүрбэ|bor=1}}
* {{desc|ru|Пурбо́|bor=1}}
{{C|cmg|Ngày trong tuần}}
ewiuraxkfobta10gp0t5alkc51gpti9
2513916
2513915
2026-08-14T03:23:13Z
WhoAlone
40420
/* {{ĐM|pr-noun}} */
2513916
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|etym}}===
{{bor+|cmg|xct|ཕུར་བུ|t=Sao Mộc; Thứ Năm}}.
==={{ĐM|pron}}===
{{rfp|cmg}}
==={{ĐM|noun}}===
{{head|cmg|Danh từ|tr=phürbü}}
# [[thứ Năm]].
===={{ĐM|decl}}====
{{rfinfl|cmg|noun}}
===={{ĐM|desc}}====
* {{desc|mn|ᠫᠦᠷᠪᠦ}}
*: {{desc|mn|пүрэв|sclang=1}}
* {{desc|bua|пүрбэ|bor=1}}
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ|tr=phürbü}}
# {{given name|cmg|nam|from=tiếng Tây Tạng cổ điển}}
===={{ĐM|decl}}====
{{rfinfl|cmg|noun}}
===={{ĐM|desc}}====
* {{desc|mn|ᠫᠦᠷᠪᠦ}}
*: {{desc|mn|Пүрэв|sclang=1}}
* {{desc|bua|Пүрбэ|bor=1}}
* {{desc|ru|Пурбо́|bor=1}}
{{C|cmg|Ngày trong tuần}}
8uqcibrlsaox75ukqwxzqd6nqlzzgx1
joga
0
412089
2513917
2026-08-14T03:24:35Z
Hiyuune
50834
Trang mới: “{{also|jóga}} =={{langname|en}}== ==={{section|n}}=== {{en-noun|?}} # {{alt form|en|yoga}} {{gloss|cây, {{taxlink|Elaeocarpus joga|loài}}}} ==={{section|ref}}=== * {{pedia|lang=en|Elaeocarpus joga|i=1}} ==={{section|ana}}=== * {{anagrams|en|a=agjo|ajog|gajo}} =={{langname|pl}}== {{wp|pl:}} ==={{section|etym}}=== {{lbor|pl|sa|योग}}, từ {{der|pl|inc-pro|*yáwgas}}, từ {{der|pl|iir-pro|*yáwgas}}, từ {{der|pl|ine-pro||*yówgos}}, từ {{m|ine-pro|*yewg-}…”
2513917
wikitext
text/x-wiki
{{also|jóga}}
=={{langname|en}}==
==={{section|n}}===
{{en-noun|?}}
# {{alt form|en|yoga}} {{gloss|cây, {{taxlink|Elaeocarpus joga|loài}}}}
==={{section|ref}}===
* {{pedia|lang=en|Elaeocarpus joga|i=1}}
==={{section|ana}}===
* {{anagrams|en|a=agjo|ajog|gajo}}
=={{langname|pl}}==
{{wp|pl:}}
==={{section|etym}}===
{{lbor|pl|sa|योग}}, từ {{der|pl|inc-pro|*yáwgas}}, từ {{der|pl|iir-pro|*yáwgas}}, từ {{der|pl|ine-pro||*yówgos}}, từ {{m|ine-pro|*yewg-}}.
==={{section|pron}}===
{{pl-pr|a=Pl-joga.ogg}}
==={{section|n}}===
{{pl-noun|f}}
# [[yoga]] {{gl|Ấn Độ giáo}}
===={{section|decl}}====
{{pl-decl-noun-f|jo|g}}
===={{section|rel}}====
{{col|pl|title=danh từ|jogin|joginka|jogizm}}
==={{section|further}}===
* {{R:pl:WSJP}}
* {{R:pl:PWN}}
{{C|pl|Yoga}}
=={{langname|se}}==
==={{section|pron}}===
* {{se-IPA}}
==={{section|n}}===
{{head|se|Biến thể hình thái danh từ}}
# {{inflection of|se|johka||acc//gen|s}}
=={{langname|pt}}==
==={{section|v}}===
{{head|pt|Biến thể hình thái động từ}}
# {{pt-verb form of|jogar}}
=={{langname|hu}}==
==={{section|etym}}===
{{affix|hu|jog|-a|pos2=hậu tố sở hữu|nocat=y}}
==={{section|pron}}===
* {{hu-IPA}}
* {{hyphenation|hu|jo|ga}}
==={{section|n}}===
{{head|hu|Biến thể hình thái danh từ}}
# {{inflection of|hu|jog||poss|với|spos|ở|3|s}}
===={{section|decl}}====
{{hu-infl-nom|jogá|o|n=sg|esm_sg=1}}
=={{langname|krl}}==
{{krl-regional|joka|joga}}
==={{section|etym}}===
Từ {{inh|krl|urj-fin-pro|*joka}}. Các từ cùng gốc bao gồm {{cog|fi|joka}} và {{cog|vep|joga}}.
==={{section|pron}}===
* {{IPA|krl|/ˈjoɡɑ/}}
* {{hyph|krl|jo|ga}}
==={{section|det}}===
{{head|krl|Từ hạn định}} {{tlb|krl|invariable}}
# {{lb|krl|South Karelian|dialectal}} [[mỗi|Mỗi]]
===={{section|syn}}====
* {{q|Bắc Karelia}} {{l|krl|joka}}, {{q|Nam Karelia}} {{l|krl|jogo}}
==={{section|ref}}===
* {{R:krl:KKS|joka}}
=={{langname|sh}}==
==={{section|pron}}===
* {{IPA|sh|/jǒːɡa/}}
* {{hyphenation|sh|jo|ga}}
==={{section|n}}===
{{sh-noun|jóga|f}}
# {{lb|sh|uncountable}} [[yoga]] {{gl|Ấn Độ giáo}}
===={{section|decl}}====
{{sh-decl-noun-unc
|joga
|joge
|jozi
|jogu
|jogo
|jozi
|jogom
}}
=={{langname|sk}}==
==={{section|etym}}===
{{der+|sk|sa|योग}}, từ {{der|sk|ine-pro|*yewg-}}.
==={{section|pron}}===
* {{IPA|sk|/ˈjɔɡa/}}
==={{section|n}}===
{{sk-noun|f|gen=jogy}}
# {{lb|sk|uncountable}} [[yoga]] {{gl|Ấn Độ giáo}}.
===={{section|decl}}====
{{sk-decl-noun-sg|joga|jogy|joge|jogu|joge|jogou}}
==={{section|further}}===
* {{R:sk:SDK}}
=={{langname|sl}}==
==={{section|pron}}===
* {{sl-IPA|jọ̑ga}}
==={{section|n}}===
{{sl-noun|jọ̑ga|f}}
# [[yoga]] {{gl|Ấn Độ giáo}}.
===={{section|decl}}====
{{sl-decl-noun-f|jóg|n=sg}}
=={{langname|tl}}==
==={{section|alt}}===
* {{alt|tl|diyoga|dyoga}}
==={{section|n}}===
{{tl-pr|jogà}}
==={{section|n}}===
{{tl-noun|jogà|b=+}} {{tlb|tl|gay slang}}
# [[ngực|Ngực]] [[to]].
#: {{syn|tl|suso|dede}}
==={{section|further}}===
* {{R:Tagalog Slang Dictionary|42}}
=={{langname|vep}}==
==={{section|etym}}===
Từ {{der|vep|urj-fin-pro|*joka}}. Từ cùng gốc bao gồm {{cog|fi|joka}}.
==={{section|adj}}===
{{head|vep|Tính từ}}
# [[mỗi|Mỗi]].
l1wr2ly1d4hwhs4ao8uvtnthblj0zax
2513919
2513917
2026-08-14T03:25:00Z
Hiyuune
50834
2513919
wikitext
text/x-wiki
{{also|jóga}}
{{minitoc}}
=={{langname|en}}==
==={{section|n}}===
{{en-noun|?}}
# {{alt form|en|yoga}} {{gloss|cây, {{taxlink|Elaeocarpus joga|loài}}}}
==={{section|ref}}===
* {{pedia|lang=en|Elaeocarpus joga|i=1}}
==={{section|ana}}===
* {{anagrams|en|a=agjo|ajog|gajo}}
=={{langname|pl}}==
{{wp|pl:}}
==={{section|etym}}===
{{lbor|pl|sa|योग}}, từ {{der|pl|inc-pro|*yáwgas}}, từ {{der|pl|iir-pro|*yáwgas}}, từ {{der|pl|ine-pro||*yówgos}}, từ {{m|ine-pro|*yewg-}}.
==={{section|pron}}===
{{pl-pr|a=Pl-joga.ogg}}
==={{section|n}}===
{{pl-noun|f}}
# [[yoga]] {{gl|Ấn Độ giáo}}
===={{section|decl}}====
{{pl-decl-noun-f|jo|g}}
===={{section|rel}}====
{{col|pl|title=danh từ|jogin|joginka|jogizm}}
==={{section|further}}===
* {{R:pl:WSJP}}
* {{R:pl:PWN}}
{{C|pl|Yoga}}
=={{langname|se}}==
==={{section|pron}}===
* {{se-IPA}}
==={{section|n}}===
{{head|se|Biến thể hình thái danh từ}}
# {{inflection of|se|johka||acc//gen|s}}
=={{langname|pt}}==
==={{section|v}}===
{{head|pt|Biến thể hình thái động từ}}
# {{pt-verb form of|jogar}}
=={{langname|hu}}==
==={{section|etym}}===
{{affix|hu|jog|-a|pos2=hậu tố sở hữu|nocat=y}}
==={{section|pron}}===
* {{hu-IPA}}
* {{hyphenation|hu|jo|ga}}
==={{section|n}}===
{{head|hu|Biến thể hình thái danh từ}}
# {{inflection of|hu|jog||poss|với|spos|ở|3|s}}
===={{section|decl}}====
{{hu-infl-nom|jogá|o|n=sg|esm_sg=1}}
=={{langname|krl}}==
{{krl-regional|joka|joga}}
==={{section|etym}}===
Từ {{inh|krl|urj-fin-pro|*joka}}. Các từ cùng gốc bao gồm {{cog|fi|joka}} và {{cog|vep|joga}}.
==={{section|pron}}===
* {{IPA|krl|/ˈjoɡɑ/}}
* {{hyph|krl|jo|ga}}
==={{section|det}}===
{{head|krl|Từ hạn định}} {{tlb|krl|invariable}}
# {{lb|krl|South Karelian|dialectal}} [[mỗi|Mỗi]]
===={{section|syn}}====
* {{q|Bắc Karelia}} {{l|krl|joka}}, {{q|Nam Karelia}} {{l|krl|jogo}}
==={{section|ref}}===
* {{R:krl:KKS|joka}}
=={{langname|sh}}==
==={{section|pron}}===
* {{IPA|sh|/jǒːɡa/}}
* {{hyphenation|sh|jo|ga}}
==={{section|n}}===
{{sh-noun|jóga|f}}
# {{lb|sh|uncountable}} [[yoga]] {{gl|Ấn Độ giáo}}
===={{section|decl}}====
{{sh-decl-noun-unc
|joga
|joge
|jozi
|jogu
|jogo
|jozi
|jogom
}}
=={{langname|sk}}==
==={{section|etym}}===
{{der+|sk|sa|योग}}, từ {{der|sk|ine-pro|*yewg-}}.
==={{section|pron}}===
* {{IPA|sk|/ˈjɔɡa/}}
==={{section|n}}===
{{sk-noun|f|gen=jogy}}
# {{lb|sk|uncountable}} [[yoga]] {{gl|Ấn Độ giáo}}.
===={{section|decl}}====
{{sk-decl-noun-sg|joga|jogy|joge|jogu|joge|jogou}}
==={{section|further}}===
* {{R:sk:SDK}}
=={{langname|sl}}==
==={{section|pron}}===
* {{sl-IPA|jọ̑ga}}
==={{section|n}}===
{{sl-noun|jọ̑ga|f}}
# [[yoga]] {{gl|Ấn Độ giáo}}.
===={{section|decl}}====
{{sl-decl-noun-f|jóg|n=sg}}
=={{langname|tl}}==
==={{section|alt}}===
* {{alt|tl|diyoga|dyoga}}
==={{section|n}}===
{{tl-pr|jogà}}
==={{section|n}}===
{{tl-noun|jogà|b=+}} {{tlb|tl|gay slang}}
# [[ngực|Ngực]] [[to]].
#: {{syn|tl|suso|dede}}
==={{section|further}}===
* {{R:Tagalog Slang Dictionary|42}}
=={{langname|vep}}==
==={{section|etym}}===
Từ {{der|vep|urj-fin-pro|*joka}}. Từ cùng gốc bao gồm {{cog|fi|joka}}.
==={{section|adj}}===
{{head|vep|Tính từ}}
# [[mỗi|Mỗi]].
d0qwzk8ynjsaat131ct02gpru86fiey
ᠵᠢᠨᠣᠩ
0
412090
2513918
2026-08-14T03:24:50Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|etym}}=== Từ {{der|cmg|cmn-ear|晉王|t=Tấn vương}}. ==={{ĐM|pron}}=== {{rfp|cmg}} ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # {{lb|cmg|historical}} [[jinong]] {{gloss|tước hiệu quý tộc Mông Cổ}} ===={{ĐM|decl}}==== {{rfinfl|cmg|noun}} ===={{ĐM|coord}}==== {{col|cmg|ᠬᠠᠭᠠᠨ|ᠬᠠᠨ|ᠨᠣᠶᠠᠨ|ᠲᠠᠢᠵᠢ|ᠲᠠᠪᠤᠨᠠᠩ|ᠵᠠᠢᠰᠠᠩ}} ===={{ĐM|desc}}==== * {{desc|mn|ᠵᠢᠨᠣᠩ}}…”
2513918
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|etym}}===
Từ {{der|cmg|cmn-ear|晉王|t=Tấn vương}}.
==={{ĐM|pron}}===
{{rfp|cmg}}
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# {{lb|cmg|historical}} [[jinong]] {{gloss|tước hiệu quý tộc Mông Cổ}}
===={{ĐM|decl}}====
{{rfinfl|cmg|noun}}
===={{ĐM|coord}}====
{{col|cmg|ᠬᠠᠭᠠᠨ|ᠬᠠᠨ|ᠨᠣᠶᠠᠨ|ᠲᠠᠢᠵᠢ|ᠲᠠᠪᠤᠨᠠᠩ|ᠵᠠᠢᠰᠠᠩ}}
===={{ĐM|desc}}====
* {{desc|mn|ᠵᠢᠨᠣᠩ}}
*: {{desc|mn|жонон|sclang=1}}
* {{desc|mnc|ᠵᡳᠨᠣᠩ|bor=1}}
** {{desc|cmn|濟農|tr=jìnóng|bor=1}}
a2a8ccubywz66wn7e8khkqbqbth0c3x
ᠵᠠᠮᠴᠤ
0
412091
2513920
2026-08-14T03:40:52Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|etym}}=== {{bor+|cmg|xct|རྒྱ་མཚོ|t=biển, đại dương}}. ==={{ĐM|pron}}=== {{rfp|cmg}} ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ riêng}} # {{given name|cmg|nam|from=tiếng Tây Tạng cổ điển}}. ===={{ĐM|decl}}==== {{rfinfl|cmg|proper noun}} ===={{ĐM|desc}}==== * {{desc|mn|ᠵᠠᠮᠴᠤ}} *: {{desc|mn|Жамц|sclang=1}}”
2513920
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|etym}}===
{{bor+|cmg|xct|རྒྱ་མཚོ|t=biển, đại dương}}.
==={{ĐM|pron}}===
{{rfp|cmg}}
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{given name|cmg|nam|from=tiếng Tây Tạng cổ điển}}.
===={{ĐM|decl}}====
{{rfinfl|cmg|proper noun}}
===={{ĐM|desc}}====
* {{desc|mn|ᠵᠠᠮᠴᠤ}}
*: {{desc|mn|Жамц|sclang=1}}
2d75q4yx1utq17o8nczte27csgl0tck
ᠬᠣᠩᠵᠢᠨ
0
412092
2513921
2026-08-14T03:47:15Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|etym}}=== Từ {{der|cmg|cmn-ear|鴻臣}}. ==={{ĐM|pron}}=== {{rfp|cmg}} ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # {{lb|cmg|historical}} {{rfdef|cmg}} ===={{ĐM|decl}}==== {{rfinfl|cmg|noun}} ===={{ĐM|desc}}==== * {{desc|mn|ᠬᠣᠩᠵᠢᠨ}} *: {{desc|mn|хонжин|sclang=1}}”
2513921
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|etym}}===
Từ {{der|cmg|cmn-ear|鴻臣}}.
==={{ĐM|pron}}===
{{rfp|cmg}}
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# {{lb|cmg|historical}} {{rfdef|cmg}}
===={{ĐM|decl}}====
{{rfinfl|cmg|noun}}
===={{ĐM|desc}}====
* {{desc|mn|ᠬᠣᠩᠵᠢᠨ}}
*: {{desc|mn|хонжин|sclang=1}}
0cixqxy1vy0hddjohfdhdilnq4f2s4o
ᠯᠣᠪᠰᠠᠩ
0
412093
2513922
2026-08-14T03:51:51Z
WhoAlone
40420
Trang mới: “=={{langname|cmn}}== ==={{ĐM|etym}}=== {{bor+|cmg|xct|བློ་བཟང}}. ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ riêng}} # {{given name|cmg|nam|from=tiếng Tây Tạng cổ điển}} ===={{ĐM|decl}}==== {{rfinfl|cmg|danh từ riêng}} ===={{ĐM|alter form}}==== * {{alt|cmg|ᠯᠣᠪᠽᠠᠩ}} ===={{ĐM|desc}}==== * {{desc|mn|ᠯᠣᠪᠰᠠᠩ}} *: {{desc|mn|Лувсан|sclang=1}} * {{desc|tyv|Лопсаң|bor=1}}”
2513922
wikitext
text/x-wiki
=={{langname|cmn}}==
==={{ĐM|etym}}===
{{bor+|cmg|xct|བློ་བཟང}}.
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{given name|cmg|nam|from=tiếng Tây Tạng cổ điển}}
===={{ĐM|decl}}====
{{rfinfl|cmg|danh từ riêng}}
===={{ĐM|alter form}}====
* {{alt|cmg|ᠯᠣᠪᠽᠠᠩ}}
===={{ĐM|desc}}====
* {{desc|mn|ᠯᠣᠪᠰᠠᠩ}}
*: {{desc|mn|Лувсан|sclang=1}}
* {{desc|tyv|Лопсаң|bor=1}}
k2cbmf034e3izfcydw6l52trf04vn3e
2513923
2513922
2026-08-14T03:53:06Z
WhoAlone
40420
2513923
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|etym}}===
{{bor+|cmg|xct|བློ་བཟང}}.
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{given name|cmg|nam|from=tiếng Tây Tạng cổ điển}}
===={{ĐM|decl}}====
{{rfinfl|cmg|danh từ riêng}}
===={{ĐM|alter form}}====
* {{alt|cmg|ᠯᠣᠪᠽᠠᠩ}}
===={{ĐM|desc}}====
* {{desc|mn|ᠯᠣᠪᠰᠠᠩ}}
*: {{desc|mn|Лувсан|sclang=1}}
* {{desc|tyv|Лопсаң|bor=1}}
4tdzwslgx1bps4r5boiirp19p7k24ef
Thể loại:Danh từ riêng tiếng Mông Cổ cổ điển
14
412094
2513924
2026-08-14T03:53:18Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513924
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
ᠰᠣᠨᠣᠮᠹᠢᠯ
0
412095
2513925
2026-08-14T03:55:46Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|etym}}=== {{bor+|cmg|xct|[[བསོད་ནམས]]་[[འཕེལ]]}}. ==={{ĐM|pron}}=== {{rfp|cmg}} ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ riêng}} # {{given name|cmg|nam|from=tiếng Tây Tạng cổ điển}} ===={{ĐM|decl}}==== {{rfinfl|cmg|danh từ riêng}}”
2513925
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|etym}}===
{{bor+|cmg|xct|[[བསོད་ནམས]]་[[འཕེལ]]}}.
==={{ĐM|pron}}===
{{rfp|cmg}}
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{given name|cmg|nam|from=tiếng Tây Tạng cổ điển}}
===={{ĐM|decl}}====
{{rfinfl|cmg|danh từ riêng}}
o1ppmvpgbenfdv9gapt721tr2kzqfyo
ᠪᠡᠢᠰᠡ
0
412096
2513926
2026-08-14T04:02:54Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|etym}}=== Gọi tắt từ {{mention|cmg|ᠬᠣᠰᠢᠭᠤᠨ ᠤ ᠪᠡᠢᠰᠡ}}. {{bor+|cmg|mnc|ᠪᡝᡳᠰᡝ}}, dạng số nhiều của {{mention|mnc|ᠪᡝᡳᠯᡝ}}, từ {{der|cmg|juc|}}. Thay thế tước hiệu Mông Cổ bản địa {{mention|cmg| ᠬᠣᠰᠢᠭᠤᠨ ᠤ ᠲᠠᠢᠵᠢ}}. ==={{ĐM|pron}}=== {{rfp|cmg}} ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # {{tlb|cmg|historical}} [[thân vương]] một k…”
2513926
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|etym}}===
Gọi tắt từ {{mention|cmg|ᠬᠣᠰᠢᠭᠤᠨ ᠤ ᠪᠡᠢᠰᠡ}}. {{bor+|cmg|mnc|ᠪᡝᡳᠰᡝ}}, dạng số nhiều của {{mention|mnc|ᠪᡝᡳᠯᡝ}}, từ {{der|cmg|juc|}}. Thay thế tước hiệu Mông Cổ bản địa {{mention|cmg| ᠬᠣᠰᠢᠭᠤᠨ ᠤ ᠲᠠᠢᠵᠢ}}.
==={{ĐM|pron}}===
{{rfp|cmg}}
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# {{tlb|cmg|historical}} [[thân vương]] một [[kỳ]] {{gl|tước hiệu quý tộc thời nhà Thanh}}
===={{ĐM|decl}}====
{{rfinfl|cmg|danh từ}}
===={{ĐM|desc}}====
* {{desc|mn|ᠪᠡᠢᠰᠡ}}
*: {{desc|mn|бэйс|sclang=1}}
* {{desc|tyv|бээзи|bor=1}}
d6080okuz0tq84934q11m6p75qr1b5r
ᠪᠡᠰᠦᠳ
0
412097
2513927
2026-08-14T04:06:49Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|etym}}=== {{rfe|cmg}} ==={{ĐM|pron}}=== {{rfp|cmg}} ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # Besut {{gloss|một trong bảy ''otog''/''khoshuu'' của ''Ar Khalkh''}} ===={{ĐM|decl}}==== {{rfinfl|cmg|danh từ}}”
2513927
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|etym}}===
{{rfe|cmg}}
==={{ĐM|pron}}===
{{rfp|cmg}}
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# Besut {{gloss|một trong bảy ''otog''/''khoshuu'' của ''Ar Khalkh''}}
===={{ĐM|decl}}====
{{rfinfl|cmg|danh từ}}
e34kjz9ehqsidxkvltc88kc9t7dq3ie
ᠠ᠌
0
412098
2513928
2026-08-14T04:09:04Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|pron}}=== {{rfp|cmg}} ==={{ĐM|suffix}}=== {{head|cmg|Hậu tố|tr=-a}} # {{ng|Dạng sở hữu cách-vị trí cách sau ''n'' và ''r''.}} #: {{sufex|mn|ᠣᠨ|ᠠ᠌|[[ᠣᠨ]] '''ᠠ᠌'''}} #: {{sufex|mn|ᠭᠠᠵᠠᠷ|ᠠ᠌|[[ᠭᠠᠵᠠᠷ]] '''ᠠ᠌'''}} ===={{ĐM|desc}}==== * {{desc|mn|ᠠ᠌}} *: {{desc|mn|-аа|-оо|sclang=1}}”
2513928
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|pron}}===
{{rfp|cmg}}
==={{ĐM|suffix}}===
{{head|cmg|Hậu tố|tr=-a}}
# {{ng|Dạng sở hữu cách-vị trí cách sau ''n'' và ''r''.}}
#: {{sufex|mn|ᠣᠨ|ᠠ᠌|[[ᠣᠨ]] '''ᠠ᠌'''}}
#: {{sufex|mn|ᠭᠠᠵᠠᠷ|ᠠ᠌|[[ᠭᠠᠵᠠᠷ]] '''ᠠ᠌'''}}
===={{ĐM|desc}}====
* {{desc|mn|ᠠ᠌}}
*: {{desc|mn|-аа|-оо|sclang=1}}
355qghgutxsw9uk8ihf82165x96uzlh
Thể loại:Hậu tố tiếng Mông Cổ cổ điển
14
412099
2513929
2026-08-14T04:09:18Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513929
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
ᠠᠪᠤᠭ ᠠ
0
412100
2513930
2026-08-14T04:14:17Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|noun}}=== {{head|cmg|Danh từ|head=ᠠᠪᠤᠭᠠ}} # {{taxfmt|Capsella bursa-pastoris|species}} ===={{ĐM|desc}}==== * {{desc|mn|авга|ᠠᠪᠤᠭᠠ}}”
2513930
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|noun}}===
{{head|cmg|Danh từ|head=ᠠᠪᠤᠭᠠ}}
# {{taxfmt|Capsella bursa-pastoris|species}}
===={{ĐM|desc}}====
* {{desc|mn|авга|ᠠᠪᠤᠭᠠ}}
ijcr3fmgkgsnzpy76hne9q14a52hr4o
ᠠᠭᠤ
0
412101
2513931
2026-08-14T04:17:00Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|alter form}}=== * {{alt|cmg|ᠠᠭ᠍ᠤ||không dấu}} ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ riêng}} # {{place|cmg|sông|krai/Zabaykalsky Krai|c/Nga|t=[[Aga]]}} # {{place|cmg|khu định cư kiểu đô thị|krai/Zabaykalsky Krai|c/Nga|t=Aginskoye}} ===={{ĐM|decl}}==== {{rfinfl|cmg|danh từ riêng}} ===={{ĐM|desc}}==== * {{desc|mn|-}} *: {{desc|mn|ᠠᠭᠤ|sclang=1}} *: {{desc|mn|Ага|sclang=1}} * {{desc|ykh|Агу<tr:Agu>…”
2513931
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|alter form}}===
* {{alt|cmg|ᠠᠭ᠍ᠤ||không dấu}}
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{place|cmg|sông|krai/Zabaykalsky Krai|c/Nga|t=[[Aga]]}}
# {{place|cmg|khu định cư kiểu đô thị|krai/Zabaykalsky Krai|c/Nga|t=Aginskoye}}
===={{ĐM|decl}}====
{{rfinfl|cmg|danh từ riêng}}
===={{ĐM|desc}}====
* {{desc|mn|-}}
*: {{desc|mn|ᠠᠭᠤ|sclang=1}}
*: {{desc|mn|Ага|sclang=1}}
* {{desc|ykh|Агу<tr:Agu>|Ага<tr:Aga>}}
* {{desc|bua|Ага|bor=1}}
* {{desc|mnc|ᠠᡤᡡ|bor=1}}
*: {{desc|zh|阿古|tr=Āgǔ|bor=1}}
* {{desc|ru|Ага́|g=m-in|bor=1}}
*: {{desc|zh|阿加|tr=Ājiā|bor=1}}
f7wwd3osgghoviywpjmpnphzuzazbh2
ᠠᠭ᠍ᠤ
0
412102
2513932
2026-08-14T04:19:13Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ riêng}} # {{alt form of|cmg|ᠠᠭᠤ}}”
2513932
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{alt form of|cmg|ᠠᠭᠤ}}
f1kkpk15qgi6hv6k20210ia20s7pqg5
ᠡᠳᠦ᠋ᠷ
0
412103
2513933
2026-08-14T04:20:23Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|pron}}=== {{rfp|cmg}} ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # {{alternative spelling of|cmg|ᠡᠳᠦᠷ|t=ngày}} ===={{ĐM|desc}}==== {{rfinfl|cmg|danh từ}}”
2513933
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|pron}}===
{{rfp|cmg}}
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# {{alternative spelling of|cmg|ᠡᠳᠦᠷ|t=ngày}}
===={{ĐM|desc}}====
{{rfinfl|cmg|danh từ}}
o9kb95p3io9hoiaft88jvxxidkx67w1
ᠡᠳᠦᠷ
0
412104
2513934
2026-08-14T04:25:19Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # [[ngày]].”
2513934
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# [[ngày]].
pir5cixgdwjn15fgt19b1e7u2m0furt
Thành viên:Hiyuune/Gadget-popups.js
2
412105
2513935
2026-08-14T04:32:05Z
Hiyuune
50834
Trang mới: “// Page Previews for Wiktionary, by User:Ioaxxere // <nowiki> // Nhập từ [[:en:MediaWiki:Gadget-PagePreviews.js]] // Generated using: [[Thành viên:Hiyuune/Gadget-PagePreviews.js/ConstGeneration]] const INTERWIKI_PREFIXES = new Map([["aa","aa"],["ab","ab"],["abstract","abstract"],["acc",0],["ace","ace"],["acronym",0],["advisory",0],["ady","ady"],["af","af"],["ak","ak"],["als","als"],["alt","alt"],["am","am"],["ami","ami"],["an","an"],["ang","ang"],["ann","ann"],["anp…”
2513935
javascript
text/javascript
// Page Previews for Wiktionary, by User:Ioaxxere
// <nowiki>
// Nhập từ [[:en:MediaWiki:Gadget-PagePreviews.js]]
// Generated using: [[Thành viên:Hiyuune/Gadget-PagePreviews.js/ConstGeneration]]
const INTERWIKI_PREFIXES = new Map([["aa","aa"],["ab","ab"],["abstract","abstract"],["acc",0],["ace","ace"],["acronym",0],["advisory",0],["ady","ady"],["af","af"],["ak","ak"],["als","als"],["alt","alt"],["am","am"],["ami","ami"],["an","an"],["ang","ang"],["ann","ann"],["anp","anp"],["antwiki",0],["appropedia",0],["aquariumwiki",0],["ar","ar"],["arbcom-zh",0],["arborwiki",0],["arc","arc"],["arxiv",0],["ary","ary"],["arz","arz"],["as","as"],["ast","ast"],["atj","atj"],["av","av"],["avk","avk"],["awa","awa"],["ay","ay"],["az","az"],["azb","azb"],["b",0],["ba","ba"],["ban","ban"],["bar","bar"],["bat-smg","bat-smg"],["battlestarwiki",0],["bbc","bbc"],["bcl","bcl"],["bcnbio",0],["bdr","bdr"],["be","be"],["be-tarask","be-tarask"],["be-x-old","be-tarask"],["beacha",0],["betawiki",0],["betawikiversity",0],["bew","bew"],["bg","bg"],["bh","bh"],["bi","bi"],["bibcode",0],["bibliowiki",0],["bjn","bjn"],["blk","blk"],["bm","bm"],["bn","bn"],["bo","bo"],["botwiki",0],["boxrec",0],["bpy","bpy"],["br","br"],["bs","bs"],["btm","btm"],["bug","bug"],["bugzilla",0],["bulba",0],["bxr","bxr"],["c",0],["c2",0],["ca","ca"],["cache",0],["cbk-zam","cbk-zam"],["ccorg",0],["cdo","cdo"],["ce","ce"],["ceb","ceb"],["centralwikia",0],["ch","ch"],["chapter",0],["cho","cho"],["choralwiki",0],["chr","chr"],["chy","chy"],["citizendium",0],["ckb","ckb"],["cmn","zh"],["co","co"],["commons",0],["communitywiki",0],["comune",0],["cr","cr"],["creativecommons",0],["creativecommonswiki",0],["crh","crh"],["cs","cs"],["csb","csb"],["cu","cu"],["cv","cv"],["cy","cy"],["cz","cs"],["d",0],["da","da"],["dag","dag"],["dbdump",0],["dcdatabase",0],["dcw",0],["de","de"],["debian",0],["devmo",0],["dga","dga"],["dico",0],["dicoado",0],["dict",0],["dictionary",0],["diffblog",0],["din","din"],["diq","diq"],["discord",0],["disinfopedia",0],["dk","da"],["dmoz",0],["dmozs",0],["doi",0],["donate",0],["doom_wiki",0],["download",0],["dpd",0],["dpla",0],["drae",0],["dsb","dsb"],["dtp","dtp"],["dty","dty"],["dv","dv"],["dz","dz"],["ee","ee"],["egl","eml"],["el","el"],["elibre",0],["emacswiki",0],["eml","eml"],["en","en"],["en-simple","simple"],["encyc",0],["englyphwiki",0],["enkol",0],["eo","eo"],["epo","eo"],["es","es"],["esolang",0],["et","et"],["etherpad",0],["etherpadbackup",0],["ethnologue",0],["ethnologuefamily",0],["eu","eu"],["exkcd",0],["exotica",0],["ext","ext"],["f",0],["fa","fa"],["fandom",0],["fanimutationwiki",0],["fat","fat"],["fedora",0],["ff","ff"],["fi","fi"],["finalfantasy",0],["finnix",0],["fiu-vro","fiu-vro"],["fj","fj"],["flickrphoto",0],["flickruser",0],["fo","fo"],["foldoc",0],["fon","fon"],["foundation",0],["foundationsite",0],["fr","fr"],["freebsdman",0],["freedomdefined",0],["freenode",0],["freesoft",0],["frp","frp"],["frr","frr"],["fur","fur"],["fy","fy"],["ga","ga"],["gag","gag"],["gan","gan"],["gcr","gcr"],["gd","gd"],["gentoo",0],["genwiki",0],["gerrit",0],["git",0],["github",0],["gitiles",0],["gitlab",0],["gl","gl"],["glk","glk"],["globalcontribs",0],["glottolog",0],["glottopedia",0],["gn","gn"],["gom","gom"],["google",0],["googledefine",0],["googlegroups",0],["gor","gor"],["got","got"],["gpe","gpe"],["gs",0],["gsw","als"],["gu","gu"],["guc","guc"],["gucprefix",0],["guildwarswiki",0],["gur","gur"],["gutenberg",0],["gutenbergwiki",0],["guw","guw"],["gv","gv"],["ha","ha"],["hackerspaces",0],["hak","hak"],["hammondwiki",0],["haw","haw"],["hdl",0],["he","he"],["heraldik",0],["hi","hi"],["hif","hif"],["ho","ho"],["horizonlabs",0],["hr","hr"],["hrfwiki",0],["hrwiki",0],["hsb","hsb"],["ht","ht"],["hu","hu"],["hy","hy"],["hyw","hyw"],["hz","hz"],["ia","ia"],["iarchive",0],["iba","iba"],["id","id"],["ie","ie"],["ig","ig"],["igl","igl"],["ii","ii"],["ik","ik"],["ilo","ilo"],["imdbcompany",0],["imdbname",0],["imdbtitle",0],["incubator",0],["infosphere",0],["inh","inh"],["io","io"],["irc",0],["ircrc",0],["ircs",0],["is","is"],["isni",0],["iso639-3",0],["issn",0],["it","it"],["iu","iu"],["ja","ja"],["jaglyphwiki",0],["jam","jam"],["jbo","jbo"],["jira",0],["jp","ja"],["jstor",0],["jv","jv"],["ka","ka"],["kaa","kaa"],["kab","kab"],["kai","kai"],["kaj","kaj"],["kamelo",0],["karlsruhe",0],["kbd","kbd"],["kbp","kbp"],["kcg","kcg"],["kg","kg"],["kge","kge"],["ki","ki"],["kj","kj"],["kk","kk"],["kl","kl"],["km","km"],["kn","kn"],["knc","knc"],["ko","ko"],["koi","koi"],["kr","kr"],["krc","krc"],["ks","ks"],["ksh","ksh"],["ku","ku"],["kus","kus"],["kv","kv"],["kw","kw"],["ky","ky"],["la","la"],["labsconsole",0],["lad","lad"],["lb","lb"],["lbe","lbe"],["lexemes",0],["lez","lez"],["lfn","lfn"],["lg","lg"],["li","li"],["liberachat",0],["libreplanet",0],["lij","lij"],["lingualibre",0],["linguistlist",0],["listarchive",0],["lld","lld"],["lmo","lmo"],["ln","ln"],["lo","lo"],["localwiki",0],["lofc",0],["lojban",0],["lokalhistoriewiki",0],["lostpedia",0],["lrc","lrc"],["lt","lt"],["ltg","ltg"],["luxo",0],["lv","lv"],["lzh","zh-classical"],["m",0],["mad","mad"],["mag","mag"],["mai","mai"],["mail",0],["mailarchive",0],["map-bms","map-bms"],["mariowiki",0],["marveldatabase",0],["mdf","mdf"],["mdwiki",0],["meatball",0],["mediawikiwiki",0],["mediazilla",0],["memoryalpha",0],["meta",0],["metawiki",0],["metawikimedia",0],["metawikipedia",0],["mg","mg"],["mh","mh"],["mhr","mhr"],["mi","mi"],["min","min"],["mineralienatlas",0],["miraheze",0],["mixnmatch",0],["mk","mk"],["ml","ml"],["mn","mn"],["mni","mni"],["mnw","mnw"],["mo","mo"],["moinmoin",0],["mos","mos"],["mosapedia",0],["mozillawiki",0],["mozillazinekb",0],["mr","mr"],["mrj","mrj"],["ms","ms"],["mt","mt"],["mus","mus"],["mw",0],["mwl","mwl"],["mwod",0],["mwot",0],["my","my"],["myv","myv"],["mzn","mzn"],["n",0],["na","na"],["nah","nah"],["nan","zh-min-nan"],["nap","nap"],["nara",0],["nb","no"],["nds","nds"],["nds-nl","nds-nl"],["ne","ne"],["new","new"],["ng","ng"],["nia","nia"],["nl","nl"],["nlab",0],["nn","nn"],["no","no"],["nost","nostalgia"],["nostalgia","nostalgia"],["nov","nov"],["nqo","nqo"],["nr","nr"],["nrm","nrm"],["nso","nso"],["nup","nup"],["nv","nv"],["ny","ny"],["oc","oc"],["oclc",0],["oeis",0],["oewiki",0],["oldwikisource",0],["olo","olo"],["olpc",0],["om","om"],["openlibrary",0],["openstreetmap",0],["openwetware",0],["or","or"],["organicdesign",0],["orthodoxwiki",0],["os","os"],["osmwiki",0],["otrs",0],["otrswiki",0],["outreach",0],["outreachwiki",0],["owasp",0],["pa","pa"],["pag","pag"],["pam","pam"],["pap","pap"],["paws",0],["pcd","pcd"],["pcm","pcm"],["pdc","pdc"],["petscan",0],["pfl","pfl"],["phab",0],["phabricator",0],["pi","pi"],["pih","pih"],["pl","pl"],["planetmath",0],["pmid",0],["pms","pms"],["pnb","pnb"],["pnt","pnt"],["pokewiki",0],["pokéwiki",0],["policy",0],["ppl","ppl"],["proofwiki",0],["ps","ps"],["pt","pt"],["pwn","pwn"],["pyrev",0],["pythoninfo",0],["q",0],["qu","qu"],["quality",0],["quarry",0],["rcirc",0],["regiowiki",0],["rev",0],["revo",0],["rfc",0],["rheinneckar",0],["rki","rki"],["rm","rm"],["rmy","rmy"],["rn","rn"],["ro","ro"],["roa-rup","roa-rup"],["roa-tara","roa-tara"],["rodovid",0],["rsk","rsk"],["rt",0],["ru","ru"],["rue","rue"],["rup","roa-rup"],["rw","rw"],["s",0],["sa","sa"],["sah","sah"],["sat","sat"],["sc","sc"],["scholar",0],["schoolswp",0],["schoolwiki",0],["scn","scn"],["sco","sco"],["scores",0],["scoutwiki",0],["sd","sd"],["se","se"],["securewikidc",0],["semantic-mw",0],["senseislibrary",0],["sep11",0],["sg","sg"],["sgs","bat-smg"],["sh","sh"],["sharemap",0],["shi","shi"],["shn","shn"],["shoutwiki",0],["shy","shy"],["si","si"],["silcode",0],["simple","simple"],["sk","sk"],["skr","skr"],["sl","sl"],["slashdot",0],["sm","sm"],["smn","smn"],["sn","sn"],["so","so"],["sourceforge",0],["spamcheck",0],["spcom",0],["species",0],["sq","sq"],["sr","sr"],["srn","srn"],["ss","ss"],["st","st"],["stats",0],["stewardry",0],["stq","stq"],["strategy",0],["strategywiki",0],["su","su"],["sulutil",0],["sv","sv"],["svn",0],["sw","sw"],["swtrain",0],["syl","syl"],["szl","szl"],["szy","szy"],["ta","ta"],["tardis",0],["tay","tay"],["tclerswiki",0],["tcy","tcy"],["tdd","tdd"],["te","te"],["tenwiki","ten"],["test2wiki","test2"],["testcommons",0],["testwiki","test"],["testwikidata",0],["tet","tet"],["tfwiki",0],["tg","tg"],["th","th"],["thelemapedia",0],["theopedia",0],["ti","ti"],["ticket",0],["tig","tig"],["tk","tk"],["tl","tl"],["tly","tly"],["tmbw",0],["tn","tn"],["to","to"],["tok","tok"],["tolkiengateway",0],["toolforge",0],["toolhub",0],["toollabs",0],["tools",0],["tpi","tpi"],["tr","tr"],["translatewiki",0],["trv","trv"],["ts","ts"],["tswiki",0],["tt","tt"],["tum","tum"],["tviv",0],["tw","tw"],["twiki",0],["twl",0],["ty","ty"],["tyv","tyv"],["tyvawiki",0],["udm","udm"],["ug","ug"],["uk","uk"],["umap",0],["uncyclopedia",0],["unihan",0],["ur","ur"],["urbandict",0],["usability",0],["usemod",0],["utrs",0],["uz","uz"],["v",0],["ve","ve"],["vec","vec"],["vep","vep"],["vi","vi"],["viaf",0],["vikidia",0],["vlos",0],["vls","vls"],["vo","vo"],["votewiki",0],["voy",0],["vro","fiu-vro"],["vrts",0],["vrtwiki",0],["w","en"],["wa","wa"],["war","war"],["wcna",0],["weirdgloop",0],["werelate",0],["wg","wg-en"],["wikia",0],["wikiapiary",0],["wikiasite",0],["wikibooks",0],["wikicities",0],["wikicity",0],["wikiconference",0],["wikidata",0],["wikiedudashboard",0],["wikifunctions",0],["wikifur",0],["wikihow",0],["wikiindex",0],["wikilivres",0],["wikilivresru",0],["wikimania",0],["wikimedia",0],["wikinews",0],["wikinfo",0],["wikinvest",0],["wikipapers",0],["wikipedia",0],["wikipediawikipedia",0],["wikiquote",0],["wikiskripta",0],["wikisophia",0],["wikisource",0],["wikisp",0],["wikispecies",0],["wikispore",0],["wikispot",0],["wikitech",0],["wikiti",0],["wikitrek",0],["wikiversity",0],["wikivoyage",0],["wikiwikiweb",0],["wikt",0],["wiktionary",0],["wm2005",0],["wm2006",0],["wm2007",0],["wm2008",0],["wm2009",0],["wm2010",0],["wm2011",0],["wm2012",0],["wm2013",0],["wm2014",0],["wm2015",0],["wm2016",0],["wm2017",0],["wm2018",0],["wmam",0],["wmania",0],["wmar",0],["wmat",0],["wmau",0],["wmbd",0],["wmbe",0],["wmbr",0],["wmca",0],["wmch",0],["wmcl",0],["wmcn",0],["wmco",0],["wmcz",0],["wmcz_docs",0],["wmcz_old",0],["wmdc",0],["wmde",0],["wmdeblog",0],["wmdk",0],["wmdoc",0],["wmec",0],["wmee",0],["wmes",0],["wmet",0],["wmf",0],["wmfblog",0],["wmfdashboard",0],["wmfi",0],["wmfr",0],["wmge",0],["wmhi",0],["wmhk",0],["wmhu",0],["wmid",0],["wmil",0],["wmin",0],["wmit",0],["wmke",0],["wmmk",0],["wmmx",0],["wmnl",0],["wmno",0],["wmnoc",0],["wmnoch",0],["wmnyc",0],["wmpa-us",0],["wmph",0],["wmpl",0],["wmplsite",0],["wmpt",0],["wmpunjabi",0],["wmromd",0],["wmrs",0],["wmru",0],["wmse",0],["wmsk",0],["wmteam",0],["wmtr",0],["wmtw",0],["wmua",0],["wmuk",0],["wmve",0],["wmza",0],["wo","wo"],["wookieepedia",0],["wowwiki",0],["wplibrary",0],["wurmpedia",0],["wuu","wuu"],["xal","xal"],["xh","xh"],["xkcd",0],["xmf","xmf"],["xtools",0],["yi","yi"],["yo","yo"],["yue","zh-yue"],["za","za"],["zea","zea"],["zgh","zgh"],["zh","zh"],["zh-classical","zh-classical"],["zh-cn","zh"],["zh-min-nan","zh-min-nan"],["zh-tw","zh"],["zh-yue","zh-yue"],["zu","zu"],["zum",0]]);
const WP_LANGUAGES = new Map([["ab","Abkhazian"],["ace","Acehnese"],["ady","Adyghe"],["af","Afrikaans"],["als","Alemannic"],["alt","Southern Altai"],["am","Amharic"],["ami","Amis"],["an","Aragonese"],["ang","Old English"],["ann","Obolo"],["anp","Angika"],["ar","Arabic"],["arc","Aramaic"],["ary","Moroccan Arabic"],["arz","Egyptian Arabic"],["as","Assamese"],["ast","Asturian"],["atj","Atikamekw"],["av","Avaric"],["avk","Kotava"],["awa","Awadhi"],["ay","Aymara"],["az","Azerbaijani"],["azb","South Azerbaijani"],["ba","Bashkir"],["ban","Balinese"],["bar","Bavarian"],["bat-smg","Samogitian"],["bbc","Batak Toba"],["bcl","Central Bikol"],["bdr","West Coast Bajau"],["be","Belarusian"],["be-tarask","Belarusian (Taraškievica orthography)"],["bew","Betawi"],["bg","Bulgarian"],["bh","Bhojpuri"],["bi","Bislama"],["bjn","Banjar"],["blk","Pa'O"],["bm","Bambara"],["bn","Bangla"],["bo","Tibetan"],["bpy","Bishnupriya"],["br","Breton"],["bs","Bosnian"],["btm","Batak Mandailing"],["bug","Buginese"],["bxr","Russia Buriat"],["ca","Catalan"],["cbk-zam","Chavacano"],["cdo","Mindong"],["ce","Chechen"],["ceb","Cebuano"],["ch","Chamorro"],["chr","Cherokee"],["chy","Cheyenne"],["ckb","Central Kurdish"],["co","Corsican"],["crh","Crimean Tatar"],["cs","Czech"],["csb","Kashubian"],["cu","Church Slavic"],["cv","Chuvash"],["cy","Welsh"],["da","Danish"],["dag","Dagbani"],["de","German"],["dga","Southern Dagaare"],["din","Dinka"],["diq","Dimli"],["dsb","Lower Sorbian"],["dtp","Central Dusun"],["dty","Doteli"],["dv","Divehi"],["dz","Dzongkha"],["ee","Ewe"],["el","Greek"],["eml","Emiliano-Romagnolo"],["en","English"],["eo","Esperanto"],["es","Spanish"],["et","Estonian"],["eu","Basque"],["ext","Extremaduran"],["fa","Persian"],["fat","Fanti"],["ff","Fula"],["fi","Finnish"],["fiu-vro","Võro"],["fj","Fijian"],["fo","Faroese"],["fon","Fon"],["fr","French"],["frp","Arpitan"],["frr","Northern Frisian"],["fur","Friulian"],["fy","Western Frisian"],["ga","Irish"],["gag","Gagauz"],["gan","Gan"],["gcr","Guianan Creole"],["gd","Scottish Gaelic"],["gl","Galician"],["glk","Gilaki"],["gn","Guarani"],["gom","Goan Konkani"],["gor","Gorontalo"],["got","Gothic"],["gpe","Ghanaian Pidgin"],["gu","Gujarati"],["guc","Wayuu"],["gur","Frafra"],["guw","Gun"],["gv","Manx"],["ha","Hausa"],["hak","Hakka Chinese"],["haw","Hawaiian"],["he","Hebrew"],["hi","Hindi"],["hif","Fiji Hindi"],["hr","Croatian"],["hsb","Upper Sorbian"],["ht","Haitian Creole"],["hu","Hungarian"],["hy","Armenian"],["hyw","Western Armenian"],["ia","Interlingua"],["iba","Iban"],["id","Indonesian"],["ie","Interlingue"],["ig","Igbo"],["igl","Igala"],["ik","Inupiaq"],["ilo","Iloko"],["inh","Ingush"],["io","Ido"],["is","Icelandic"],["it","Italian"],["iu","Inuktitut"],["ja","Japanese"],["jam","Jamaican Creole English"],["jbo","Lojban"],["jv","Javanese"],["ka","Georgian"],["kaa","Kara-Kalpak"],["kab","Kabyle"],["kai","Karekare"],["kaj","Jju"],["kbd","Kabardian"],["kbp","Kabiye"],["kcg","Tyap"],["kg","Kongo"],["kge","Komering"],["ki","Kikuyu"],["kk","Kazakh"],["km","Khmer"],["kn","Kannada"],["knc","Central Kanuri"],["ko","Korean"],["koi","Komi-Permyak"],["krc","Karachay-Balkar"],["ks","Kashmiri"],["ksh","Colognian"],["ku","Kurdish"],["kus","Kusaal"],["kv","Komi"],["kw","Cornish"],["ky","Kyrgyz"],["la","Latin"],["lad","Ladino"],["lb","Luxembourgish"],["lbe","Lak"],["lez","Lezghian"],["lfn","Lingua Franca Nova"],["lg","Ganda"],["li","Limburgish"],["lij","Ligurian"],["lld","Ladin"],["lmo","Lombard"],["ln","Lingala"],["lo","Lao"],["lt","Lithuanian"],["ltg","Latgalian"],["lv","Latvian"],["mad","Madurese"],["mag","Magahi"],["mai","Maithili"],["map-bms","Banyumasan"],["mdf","Moksha"],["mg","Malagasy"],["mhr","Eastern Mari"],["mi","Māori"],["min","Minangkabau"],["mk","Macedonian"],["ml","Malayalam"],["mn","Mongolian"],["mni","Manipuri"],["mnw","Mon"],["mos","Mossi"],["mr","Marathi"],["mrj","Western Mari"],["ms","Malay"],["mt","Maltese"],["mwl","Mirandese"],["my","Burmese"],["myv","Erzya"],["mzn","Mazanderani"],["nah","Nahuatl"],["nap","Neapolitan"],["nds","Low German"],["nds-nl","Low Saxon"],["ne","Nepali"],["new","Newari"],["nia","Nias"],["nl","Dutch"],["nn","Norwegian Nynorsk"],["no","Norwegian"],["nov","Novial"],["nqo","N’Ko"],["nr","South Ndebele"],["nrm","Norman"],["nso","Northern Sotho"],["nup","Nupe"],["nv","Navajo"],["ny","Nyanja"],["oc","Occitan"],["olo","Livvi-Karelian"],["om","Oromo"],["or","Odia"],["os","Ossetic"],["pa","Punjabi"],["pag","Pangasinan"],["pam","Pampanga"],["pap","Papiamento"],["pcd","Picard"],["pcm","Nigerian Pidgin"],["pdc","Pennsylvania German"],["pfl","Palatine German"],["pi","Pali"],["pl","Polish"],["pms","Piedmontese"],["pnb","Western Punjabi"],["pnt","Pontic"],["ppl","Nawat"],["ps","Pashto"],["pt","Portuguese"],["pwn","Paiwan"],["qu","Quechua"],["rki","Arakanese"],["rm","Romansh"],["rmy","Vlax Romani"],["rn","Rundi"],["ro","Romanian"],["roa-rup","Aromanian"],["roa-tara","Tarantino"],["rsk","Pannonian Rusyn"],["ru","Russian"],["rue","Rusyn"],["rw","Kinyarwanda"],["sa","Sanskrit"],["sah","Yakut"],["sat","Santali"],["sc","Sardinian"],["scn","Sicilian"],["sco","Scots"],["sd","Sindhi"],["se","Northern Sami"],["sg","Sango"],["sh","Serbo-Croatian"],["shi","Tachelhit"],["shn","Shan"],["si","Sinhala"],["simple","Simple English"],["sk","Slovak"],["skr","Saraiki"],["sl","Slovenian"],["sm","Samoan"],["smn","Inari Sami"],["sn","Shona"],["so","Somali"],["sq","Albanian"],["sr","Serbian"],["srn","Sranan Tongo"],["ss","Swati"],["st","Southern Sotho"],["stq","Saterland Frisian"],["su","Sundanese"],["sv","Swedish"],["sw","Swahili"],["syl","Sylheti"],["szl","Silesian"],["szy","Sakizaya"],["ta","Tamil"],["tay","Atayal"],["tcy","Tulu"],["tdd","Tai Nuea"],["te","Telugu"],["tet","Tetum"],["tg","Tajik"],["th","Thai"],["ti","Tigrinya"],["tig","Tigre"],["tk","Turkmen"],["tl","Tagalog"],["tly","Talysh"],["tn","Tswana"],["to","Tongan"],["tok","Toki Pona"],["tpi","Tok Pisin"],["tr","Turkish"],["trv","Taroko"],["ts","Tsonga"],["tt","Tatar"],["tum","Tumbuka"],["tw","Twi"],["ty","Tahitian"],["tyv","Tuvinian"],["udm","Udmurt"],["ug","Uyghur"],["uk","Ukrainian"],["ur","Urdu"],["uz","Uzbek"],["ve","Venda"],["vec","Venetian"],["vep","Veps"],["vi","Vietnamese"],["vls","West Flemish"],["vo","Volapük"],["wa","Walloon"],["war","Waray"],["wo","Wolof"],["wuu","Wu"],["xal","Kalmyk"],["xh","Xhosa"],["xmf","Mingrelian"],["yi","Yiddish"],["yo","Yoruba"],["za","Zhuang"],["zea","Zeelandic"],["zgh","Standard Moroccan Tamazight"],["zh","Chinese"],["zh-classical","Literary Chinese"],["zh-min-nan","Minnan"],["zh-yue","Cantonese"],["zu","Zulu"]]);
const appendixWithSlash = ["Adûni", "Afrihili", "Arabic_roots", "Belter_Creole", "Bibliography", "Black_Speech", "Bolak", "Communicationssprache", "Dothraki", "Eloi", "Gestures", "Glosa", "Goa'uld", "High_Valyrian", "Idiom_Neutral", "Interlingue", "Interslavic", "Klingon", "Kotava", "Láadan", "Lapine", "Lingua_Franca_Nova", "Lojban", "Mandalorian", "Medefaidrin", "Minecraft", "Mundolinco", "Na'vi", "Neo", "Novial", "Noxilo", "Protologisms/Long_words", "Quenya", "Romanova", "Sindarin", "Snowclones", "Talossan", "Toki_Pona", "Unas"];
const appendixNoSlash = ["Boldface", "Capital_letter", "Italics", "Lowercase_letter", "Possessive", "Repetition", "Small_caps", "Strikethrough", "Subscript", "Superscript", "Underline", "Uppercase_letter"];
const animationSpeed = 0.2; // seconds
mw.util.addCSS(`
.wikt-page-preview ol {
margin: 0 0.5em 0 1.5em;
padding: 0;
}
.wikt-page-preview dl {
margin-bottom: 0;
}
.wikt-page-preview p {
margin: 0;
}
/* popupContainer has the opacity animation, while popup gets translated. */
.popup-fade-in-up, .popup-fade-in-down {
animation: popup-fade-in ${animationSpeed}s ease forwards;
}
.popup-fade-out-up, .popup-fade-out-down {
animation: popup-fade-out ${animationSpeed}s ease forwards;
}
.popup-fade-in-up > div {
animation: popup-move-in-up ${animationSpeed}s ease forwards;
}
.popup-fade-in-down > div {
animation: popup-move-in-down ${animationSpeed}s ease forwards;
}
.popup-fade-out-up > div {
animation: popup-move-out-up ${animationSpeed}s ease forwards;
}
.popup-fade-out-down > div {
animation: popup-move-out-down ${animationSpeed}s ease forwards;
}
@keyframes popup-move-in-up {
0% {
transform: translate(0, 20px);
}
}
@keyframes popup-move-in-down {
0% {
transform: translate(0, -20px);
}
}
@keyframes popup-move-out-up {
100% {
transform: translate(0, -20px);
}
}
@keyframes popup-move-out-down {
100% {
transform: translate(0, 20px);
}
}
@keyframes popup-fade-out {
100% {
opacity: 0;
}
}
@keyframes popup-fade-in {
0% {
opacity: 0;
}
}
.ring-loader {
margin: auto;
width: 24px;
height: 24px;
border-radius: 50%;
border-top: 5px solid var(--wikt-palette-black);
border-bottom: 5px solid var(--wikt-palette-black);
border-left: 5px solid transparent;
border-right: 5px solid transparent;
animation: spin 1.2s linear infinite;
}
@keyframes spin {
100% {
transform: rotate(360deg);
}
}
.preview-headerlink, .preview-headerlink:visited {
color: inherit;
font-weight: bold;
}
/* Hack: get the speaker icon on Wikipedia articles without having to load Phonos. */
.ext-phonos .oo-ui-buttonElement-button:after {
content: "🔊";
}
.ext-phonos * {
display: inline !important;
padding: 0 !important;
margin: 0 !important;
}
`);
const definitionIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M15 2a7.65 7.65 0 00-5 2 7.65 7.65 0 00-5-2H1v15h4a7.65 7.65 0 015 2 7.65 7.65 0 015-2h4V2zm2.5 13.5H14a4.38 4.38 0 00-3 1V5s1-1.5 4-1.5h2.5z"></path></g></svg>`;
const glossaryIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M12.43 14.34A5 5 0 0110 15a5 5 0 113.95-2L17 16.09V3a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h10a2 2 0 001.45-.63z"></path><circle cx="10" cy="10" r="3"></circle></g></svg>`;
const articleIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M5 1a2 2 0 00-2 2v14a2 2 0 002 2h10a2 2 0 002-2V3a2 2 0 00-2-2zm0 3h5v1H5zm0 2h5v1H5zm0 2h5v1H5zm10 7H5v-1h10zm0-2H5v-1h10zm0-2H5v-1h10zm0-2h-4V4h4z"></path></g></svg>`;
const bibliographyIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M3 2h12c.5 0 1 .5 1 1v1c0 .5-.5 1-1 1H3c0 0 .5-.5.5-1V3c0-.5-.5-1-.5-1zM4 6h12c.5 0 1 .5 1 1v1c0 .5-.5 1-1 1H4c0 0 .5-.5.5-1V7c0-.5-.5-1-.5-1zM3 10h12c.5 0 1 .5 1 1v1c0 .5-.5 1-1 1H3c0 0 .5-.5.5-1v-1c0-.5-.5-1-.5-1zM4 14h12c.5 0 1 .5 1 1v1c0 .5-.5 1-1 1H4c0 0 .5-.5.5-1v-1c0-.5-.5-1-.5-1z"/></g></svg>`;
const loader = document.createElement("div");
loader.className = "ring-loader";
const popupContainer = document.createElement("div");
const popup = document.createElement("div");
const popupContent = document.createElement("div");
popupContainer.style = `display: none; position: absolute; filter: drop-shadow(0px 30px 30px rgba(0, 0, 0, 0.15)) drop-shadow(0px 0px 0.75px var(--wikt-palette-dullblue)); z-index: 801`;
popupContainer.className = "wikt-page-preview";
popup.style = "box-sizing: border-box; height: 100%; background: var(--wikt-palette-white)";
popupContent.style = "display: flex; flex-direction: column; border-radius: 2px; box-sizing: border-box; color: var(--wikt-palette-black); overflow: auto; height: 100%; overflow-wrap: break-word; scrollbar-width: thin";
document.body.append(popupContainer);
popupContainer.append(popup);
popup.append(popupContent);
let popupTimer;
let openLink;
let mouseX, mouseY;
let API_controller = new AbortController();
function closePopup() {
if (!openLink) return;
openLink = null;
API_controller.abort();
API_controller = new AbortController();
if (popupContainer.classList.contains("popup-fade-in-up")) {
popupContainer.classList.remove("popup-fade-in-up");
popupContainer.classList.add("popup-fade-out-down");
} else {
popupContainer.classList.remove("popup-fade-in-down");
popupContainer.classList.add("popup-fade-out-up");
}
// After the animation has completed, reset the popup to its initial state.
setTimeout(() => {
popupContainer.style.display = "none";
popupContainer.classList.remove("popup-fade-out-down", "popup-fade-out-up");
}, animationSpeed * 1000);
}
popup.addEventListener("pointerenter", () => {
clearTimeout(popupTimer);
});
popup.addEventListener("pointerleave", () => {
clearTimeout(popupTimer);
popupTimer = setTimeout(closePopup, 300);
});
function processLink(link) {
let linkTitle = decodeURIComponent(link.pathname.split("/wiki/")[1]);
let titlePrefix = linkTitle.includes(":") ? linkTitle.toLowerCase().split(":")[0] : "";
let resolvedTitle = linkTitle;
let API_domain = link.origin;
let linkAnchor = decodeURIComponent(link.hash.slice(1) || "");
let isWPlink = link.href.match(/^https:\/\/[^.]+.wikipedia.org\/wiki\//);
let isPreviewLink = Boolean(link.closest(".wikt-page-preview"));
let isGlossary = /^Appendix:.*[Gg]lossary$/.test(linkTitle);
// Start with various checks to determine whether a link should be processed.
if (link.matches(`nav a, .cancelLink a, .mw-widget-titleOptionWidget a, .new, .preview-headerlink, .external, [href$="#"]`)) {
return;
} else if (isGlossary) {
// Links to glossary *must* have an anchor.
if (!linkAnchor) return;
} else if (linkTitle.startsWith("Appendix:Bibliography/")) {
// Bibliography subpages must have an anchor starting with `bib-`.
if (!linkAnchor || !linkAnchor.startsWith("bib-")) return;
} else if (!isWPlink) {
// Other Wiktionary pages must not have a lowercase-starting anchor (filters out some unhelpful links).
if (/^[a-z]/.test(linkAnchor)) return;
}
if (link.href.startsWith("https://en.wiktionary.org/wiki/")) {
// Filter out all interwiki prefixes.
if (INTERWIKI_PREFIXES.has(titlePrefix))
return;
let {namespace, title} = new mw.Title(linkTitle);
if (namespace === 0) { // Mainspace
if (title.endsWith("/translations"))
return;
} else if (namespace === 100) { // Appendix space
if (!appendixWithSlash.some(lang => title.startsWith(lang + "/")) && !appendixNoSlash.includes(title) && !isGlossary)
return;
} else if (namespace !== 118) { // Reconstruction space
return;
}
} else if (isWPlink) {
// Filter out invalid interwiki prefixes.
if (INTERWIKI_PREFIXES.get(titlePrefix) === 0)
return;
// Get the resolved title if it's a language interwiki.
if (INTERWIKI_PREFIXES.has(titlePrefix)) {
API_domain = `https://${INTERWIKI_PREFIXES.get(titlePrefix)}.wikipedia.org`;
resolvedTitle = linkTitle.substring(linkTitle.indexOf(":") + 1);
// If the resolved title contains another interwiki, return.
let resolvedTitlePrefix = resolvedTitle.includes(":") ? resolvedTitle.toLowerCase().split(":")[0] : ""; // same as titlePrefix
if (INTERWIKI_PREFIXES.has(resolvedTitlePrefix))
return;
}
} else {
return;
}
link.addEventListener("pointerover", event => {
[mouseX, mouseY] = [event.clientX, event.clientY];
clearTimeout(popupTimer);
// Ignore links which are already open.
if (link === openLink)
return;
if (!isPreviewLink)
closePopup();
// Fetch popup text immediately on hover to reduce delay.
let responsePromise = fetch(`${API_domain}/api/rest_v1/page/html/${encodeURIComponent(resolvedTitle)}`, {
headers: {"Api-User-Agent": "Tiện ích được phát triển bởi User:Ioaxxere"},
signal: API_controller.signal
}).then(r => r.text()).catch(() => { /* fetch was aborted */ });
popupTimer = setTimeout(() => {
popupContainer.style.display = "";
popupContent.innerHTML = "";
popupContent.append(loader);
popupContent.style.padding = "14px 16px 8px";
// Disable link hovering until animation has completed.
popupContent.style.pointerEvents = "none";
setTimeout(() => popupContent.style.pointerEvents = "", animationSpeed * 1000);
// Adapt the popup for large, standard, and small size preference in Vector 2022.
let sizeSetting = 0;
if (document.documentElement.matches(".vector-feature-custom-font-size-clientpref-1"))
sizeSetting = 1;
else if (document.documentElement.matches(".vector-feature-custom-font-size-clientpref-2"))
sizeSetting = 2;
let width = [320, 340, 360][sizeSetting];
let height = [210, 225, 240][sizeSetting];
let triangleSize = [9, 10, 11][sizeSetting];
popupContainer.style.fontSize = ["14px", "15px", "17px"][sizeSetting];
popupContainer.style.lineHeight = ["21px", "22px", "24px"][sizeSetting];
popupContainer.style.width = width + "px";
popupContainer.style.height = height + "px";
if (isPreviewLink) {
// Trigger fade-in-down animation.
popupContainer.classList.remove("popup-fade-in-up", "popup-fade-in-down");
void popupContainer.offsetLeft; // force reflow
popupContainer.classList.add("popup-fade-in-down");
} else {
openLink = link;
// Get list of rects (lines) of the target element, then find the one whose midpoint is closest to mouseY.
// This ensures that the code can correctly handle multi-line links.
let linkBlock = [...event.target.getClientRects()].reduce((prev, next) => Math.abs((next.top + next.bottom) / 2 - mouseY) < Math.abs((prev.top + prev.bottom) / 2 - mouseY) ? next : prev);
// Horizontal position. Choose left or right depending on the side of the screen the mouse is in.
let leftPosition = mouseX - 30;
if (mouseX > document.documentElement.clientWidth / 2)
leftPosition = mouseX - width + 30;
// Ensure that the popup is at least 5px away from the side of the screen.
leftPosition = Math.max(5, Math.min(leftPosition, document.documentElement.clientWidth - width - 5));
popupContainer.style.left = leftPosition + document.documentElement.scrollLeft + "px";
// Vertical position. Prioritize fade-in-down unless there would be less than 10px of room above.
if (linkBlock.top > height + 10 || linkBlock.top > document.documentElement.clientHeight - linkBlock.bottom) {
popupContainer.style.top = linkBlock.top + document.documentElement.scrollTop - height + "px";
popupContainer.classList.add("popup-fade-in-down");
popup.style.padding = `0 0 ${triangleSize}px`;
// Create a triangle on the bottom.
popup.style.clipPath = `polygon(0 0, 100% 0, 100% calc(100% - ${triangleSize}px), ${mouseX - leftPosition + triangleSize}px calc(100% - ${triangleSize}px), ${mouseX - leftPosition}px 100%, ${mouseX - leftPosition - triangleSize}px calc(100% - ${triangleSize}px), 0 calc(100% - ${triangleSize}px))`;
} else {
popupContainer.style.top = linkBlock.bottom + document.documentElement.scrollTop + "px";
popupContainer.classList.add("popup-fade-in-up");
popup.style.padding = `${triangleSize}px 0 0`;
// Create a triangle on top.
popup.style.clipPath = `polygon(0 ${triangleSize}px, ${mouseX - leftPosition - triangleSize}px ${triangleSize}px, ${mouseX - leftPosition}px 0, ${mouseX - leftPosition + triangleSize}px ${triangleSize}px, 100% ${triangleSize}px, 100% 100%, 0 100%)`;
}
}
responsePromise.then(response => {
if (!response) return; // if the fetch was aborted
popupContent.innerHTML = "";
let responseDocument = new DOMParser().parseFromString(response, "text/html");
// Convert to absolute URLs.
responseDocument.querySelectorAll("a[href]").forEach(link => link.setAttribute("href", link.href));
let anchoredElement = responseDocument.getElementById(linkAnchor);
let popupHeader = document.createElement("div");
popupHeader.style = "font-size: 90%; color: var(--wikt-palette-deepblue)";
popupContent.append(popupHeader);
let iconContainer = document.createElement("span");
iconContainer.style = "float: right; margin-left: 10px; height: 20px; color: var(--wikt-palette-black)";
popupHeader.append(iconContainer);
let titleLink = document.createElement("a");
titleLink.href = link.href;
titleLink.title = link.title;
titleLink.className = "skin-invert-image preview-headerlink";
// Scrape entry content.
if (isWPlink) {
iconContainer.innerHTML = articleIcon;
titleLink.textContent = linkTitle.replaceAll("_", " ");
if (responseDocument.title)
titleLink.innerHTML = responseDocument.title; // sometimes gives HTML text
let WikipediaName = WP_LANGUAGES.get(API_domain.substr(8).split(".")[0]);
popupHeader.append(WikipediaName + " Wikipedia có bài viết về ", titleLink);
let articleContent = document.createElement("div");
articleContent.style.margin = "5px 0 0 10px";
let firstSection = responseDocument.querySelector("section");
if (firstSection && responseDocument.querySelector(`meta[property="mw:pageNamespace"][content="0"]`)) {
firstSection.querySelectorAll("b").forEach(boldElem => boldElem.outerHTML = boldElem.innerHTML);
firstSection.querySelectorAll(":scope > p, :scope > ul, :scope > ol").forEach(elem => articleContent.append(elem));
}
if (articleContent.childElementCount)
popupContent.append(articleContent);
} else if (isGlossary) {
iconContainer.innerHTML = glossaryIcon;
titleLink.textContent = linkAnchor.replaceAll("_", " ");
popupHeader.append("Glossary definition of ", titleLink);
if (anchoredElement && anchoredElement.matches(".template-anchor")) {
// Get succeeding `dd` elements. The HTML is <dt>...<span class="template-anchor"></span>...</dt><dd>...</dd><dd>...</dd>
for (let elem = anchoredElement.parentElement.nextElementSibling; elem && elem.matches("dd"); elem = elem.nextElementSibling) {
elem.style = "margin: 5px 0 0 15px";
popupContent.append(elem.cloneNode(true));
}
}
} else if (linkTitle.startsWith("Phụ lục:Thư mục học/")) {
iconContainer.innerHTML = bibliographyIcon;
let language = linkTitle.slice(22).replaceAll("_", " "); // trim "Appendix:Bibliography/" prefix
popupHeader.append(`${language} bibliography`);
if (anchoredElement && anchoredElement.matches(".bibliography-id")) {
let bibliographyCitation = document.createElement("div");
bibliographyCitation.style = "margin: 5px 0 0 15px";
bibliographyCitation.innerHTML = anchoredElement.innerHTML;
popupContent.append(bibliographyCitation);
}
} else {
const hasDefinitions = elem => Boolean(elem.querySelector("ol:not(.references), .ja-see, .zh-see"));
iconContainer.innerHTML = definitionIcon;
// Try to resolve the anchor element to #Chinese.
if (!anchoredElement && ["Tiếng Quan Thoại", "Gan", "Hakka", "Hokkien", "Literary_Chinese", "Mandarin", "Middle_Chinese", "Old_Chinese", "Wu", "Xiang"].includes(linkAnchor))
anchoredElement = responseDocument.querySelector("#Tiếng Trung Quốc");
// Try to guess the anchor target in the following order: English, Chinese, Translingual, [first h2 on page]
// Always prioritize an L2 section which contains definitions.
if (!linkAnchor) {
let pageH2s = [...responseDocument.querySelectorAll("h2")];
if (pageH2s.some(h2 => hasDefinitions(h2.parentElement)))
pageH2s = pageH2s.filter(h2 => hasDefinitions(h2.parentElement));
anchoredElement = pageH2s.find(h2 => h2.id === "English");
if (!anchoredElement) anchoredElement = pageH2s.find(h2 => h2.id === "Chinese");
if (!anchoredElement) anchoredElement = pageH2s.find(h2 => h2.id === "Translingual");
if (!anchoredElement) anchoredElement = pageH2s[0];
}
let displayTitle = document.createElement("strong");
displayTitle.textContent = linkTitle.split("/").pop().replaceAll("_", " ");
if (linkTitle.startsWith("Reconstruction:"))
displayTitle.textContent = "*" + displayTitle.textContent;
// Find localest section containing an h2.
let languageSection = anchoredElement;
while (languageSection && !languageSection.querySelector(":scope > h2"))
languageSection = languageSection.parentElement.closest("section");
// Make sure that the entry is well-formed.
if (languageSection) {
let language = languageSection.querySelector(":scope > h2").textContent;
let scrapeSection = anchoredElement.closest("section");
// Find localest section which contains any definitions.
while (scrapeSection && !hasDefinitions(scrapeSection))
scrapeSection = scrapeSection.parentElement.closest("section");
if (scrapeSection) {
let headwords = [...scrapeSection.querySelectorAll(".headword-line > strong")];
if (new Set(headwords.map(h => h.textContent)).size === 1) {
// If there is a single unique headword, replace the display title with that.
displayTitle = headwords[0];
if (displayTitle.querySelector("a > img"))
displayTitle = displayTitle.querySelector("a > img").cloneNode();
} else if (new Set(headwords.map(h => h.cloneNode().outerHTML)).size === 1) {
// If there is a single unique set of element attributes, use that with the display title.
headwords[0].innerHTML = displayTitle.innerHTML;
displayTitle = headwords[0];
}
// Remove existing links in the headword.
displayTitle.querySelectorAll("a").forEach(elem => elem.replaceWith(elem.textContent));
let ols = scrapeSection.querySelectorAll(":scope > ol:not(.references), section > ol:not(.references)");
if (anchoredElement.matches(".senseid")) {
[...anchoredElement.parentElement.childNodes].forEach(child => {
if (child !== anchoredElement) child.remove();
});
ols = [anchoredElement.closest("ol")];
}
for (let ol of ols) {
let POS_container = document.createElement("div");
POS_container.style.margin = "8px 0 5px 0";
let POS = document.createElement("span");
POS.style = "font-size: 110%; font-weight: bold";
POS.textContent = ol.closest("section").firstChild.textContent;
POS_container.append(POS);
let tlb = ol.closest("section").querySelector(".headword-line ~ .usage-label-term");
if (tlb)
POS_container.append(document.createElement("br"), tlb);
popupContent.append(POS_container, ol);
}
// {{ja-see}} and {{zh-see}} don't follow the normal format, so just insert them verbatim.
for (let seeTemplate of scrapeSection.querySelectorAll(".ja-see, .zh-see"))
popupContent.append(seeTemplate);
}
titleLink.append(displayTitle);
popupHeader.append("Xem trước định nghĩa " + language + " ", titleLink);
} else {
titleLink.append(displayTitle);
popupHeader.append("Xem trước định nghĩa ", titleLink);
}
}
// Post-fetch error handling.
if (popupContent.childElementCount > 1) {
// Clean up HTML.
popupContent.querySelectorAll("link, .previewonly, .maintenance-line, .mw-empty-elt, .reference, .Inline-Template, .vsHide").forEach(elem => elem.remove());
if (!isWPlink)
popupContent.querySelectorAll("li > ul").forEach(elem => elem.remove()); // remove quotations
for (let elem of popupContent.querySelectorAll("*"))
elem.removeAttribute("id"); // avoid inadvertently repeating IDs within a page
} else if (isWPlink) {
// Display a message if the Wikipedia article was invalid or not in mainspace.
let noArticle = document.createElement("div");
noArticle.style = "margin: 5px 0 0 15px; font-size: 90%";
noArticle.textContent = "(article content could not be previewed)";
popupContent.append(noArticle);
} else if (linkAnchor && !anchoredElement) {
// Display a message if the anchor is invalid.
let noSectionFound = document.createElement("div");
noSectionFound.style = "margin: 10px 0 0 7.5px; font-size: 90%";
let strong = document.createElement("strong");
strong.textContent = linkAnchor.replaceAll("_", " ");
noSectionFound.append("The ", strong, " section was not found on this page.");
popupContent.append(noSectionFound);
} else {
// Display a message if no definitions were found in the section.
let noDefinitionsFound = document.createElement("div");
noDefinitionsFound.style = "margin: 5px 0 0 15px; font-size: 90%";
noDefinitionsFound.textContent = "(không tìm thấy định nghĩa)";
popupContent.append(noDefinitionsFound);
}
// Reduce the padding if a scrollbar is present. The intended padding is 16px on each side.
if (width - popupHeader.clientWidth > 32)
popupContent.style.paddingRight = Math.max(4, 48 + popupHeader.clientWidth - width) + "px";
});
// (Hack?) make sure that `link pointerleave` doesn't cause the popup to immediately close.
setTimeout(() => clearTimeout(popupTimer), 0);
}, isPreviewLink ? 1200 : 400);
});
link.addEventListener("pointerout", () => {
clearTimeout(popupTimer);
if (link === openLink) {
popupTimer = setTimeout(closePopup, 300);
} else {
API_controller.abort();
API_controller = new AbortController();
}
});
link.addEventListener("pointermove", event => [mouseX, mouseY] = [event.clientX, event.clientY]);
link.addEventListener("click", () => clearTimeout(popupTimer));
}
// Process all links.
document.querySelectorAll("a").forEach(processLink);
// Process links which are added to the DOM after the gadget has run.
(new MutationObserver(events => events.flatMap(event => [...event.addedNodes]).forEach(node => {
if (node instanceof HTMLAnchorElement)
processLink(node);
if (node instanceof HTMLElement)
node.querySelectorAll("a").forEach(processLink);
}))).observe(document.body, {childList: true, subtree: true});
// Dispatch hover events to support [[w:MediaWiki:Gadget-ReferenceTooltips.js]].
const linkEvent = ev => openLink && openLink.dispatchEvent(new Event(ev, {bubbles: true, cancelable: true}));
// Note: for some reason an event listener on `popup` doesn't catch everything...
popupContainer.addEventListener("pointerenter", () => setTimeout(() => linkEvent("mouseover")));
popupContainer.addEventListener("pointerleave", () => linkEvent("mouseout"));
// Custom events
popupContainer.addEventListener("pointerenter", () => linkEvent("linkPopupHover"));
popupContainer.addEventListener("pointerleave", () => linkEvent("linkPopupUnhover"));
// </nowiki>
ovuydfpj83ga0m085ik9m34d0led8pu
2513942
2513935
2026-08-14T04:41:35Z
Hiyuune
50834
Hiyuune đã đổi [[Thành viên:Hiyuune/Gadget-PagePreviews.js]] thành [[Thành viên:Hiyuune/Gadget-popups.js]]
2513935
javascript
text/javascript
// Page Previews for Wiktionary, by User:Ioaxxere
// <nowiki>
// Nhập từ [[:en:MediaWiki:Gadget-PagePreviews.js]]
// Generated using: [[Thành viên:Hiyuune/Gadget-PagePreviews.js/ConstGeneration]]
const INTERWIKI_PREFIXES = new Map([["aa","aa"],["ab","ab"],["abstract","abstract"],["acc",0],["ace","ace"],["acronym",0],["advisory",0],["ady","ady"],["af","af"],["ak","ak"],["als","als"],["alt","alt"],["am","am"],["ami","ami"],["an","an"],["ang","ang"],["ann","ann"],["anp","anp"],["antwiki",0],["appropedia",0],["aquariumwiki",0],["ar","ar"],["arbcom-zh",0],["arborwiki",0],["arc","arc"],["arxiv",0],["ary","ary"],["arz","arz"],["as","as"],["ast","ast"],["atj","atj"],["av","av"],["avk","avk"],["awa","awa"],["ay","ay"],["az","az"],["azb","azb"],["b",0],["ba","ba"],["ban","ban"],["bar","bar"],["bat-smg","bat-smg"],["battlestarwiki",0],["bbc","bbc"],["bcl","bcl"],["bcnbio",0],["bdr","bdr"],["be","be"],["be-tarask","be-tarask"],["be-x-old","be-tarask"],["beacha",0],["betawiki",0],["betawikiversity",0],["bew","bew"],["bg","bg"],["bh","bh"],["bi","bi"],["bibcode",0],["bibliowiki",0],["bjn","bjn"],["blk","blk"],["bm","bm"],["bn","bn"],["bo","bo"],["botwiki",0],["boxrec",0],["bpy","bpy"],["br","br"],["bs","bs"],["btm","btm"],["bug","bug"],["bugzilla",0],["bulba",0],["bxr","bxr"],["c",0],["c2",0],["ca","ca"],["cache",0],["cbk-zam","cbk-zam"],["ccorg",0],["cdo","cdo"],["ce","ce"],["ceb","ceb"],["centralwikia",0],["ch","ch"],["chapter",0],["cho","cho"],["choralwiki",0],["chr","chr"],["chy","chy"],["citizendium",0],["ckb","ckb"],["cmn","zh"],["co","co"],["commons",0],["communitywiki",0],["comune",0],["cr","cr"],["creativecommons",0],["creativecommonswiki",0],["crh","crh"],["cs","cs"],["csb","csb"],["cu","cu"],["cv","cv"],["cy","cy"],["cz","cs"],["d",0],["da","da"],["dag","dag"],["dbdump",0],["dcdatabase",0],["dcw",0],["de","de"],["debian",0],["devmo",0],["dga","dga"],["dico",0],["dicoado",0],["dict",0],["dictionary",0],["diffblog",0],["din","din"],["diq","diq"],["discord",0],["disinfopedia",0],["dk","da"],["dmoz",0],["dmozs",0],["doi",0],["donate",0],["doom_wiki",0],["download",0],["dpd",0],["dpla",0],["drae",0],["dsb","dsb"],["dtp","dtp"],["dty","dty"],["dv","dv"],["dz","dz"],["ee","ee"],["egl","eml"],["el","el"],["elibre",0],["emacswiki",0],["eml","eml"],["en","en"],["en-simple","simple"],["encyc",0],["englyphwiki",0],["enkol",0],["eo","eo"],["epo","eo"],["es","es"],["esolang",0],["et","et"],["etherpad",0],["etherpadbackup",0],["ethnologue",0],["ethnologuefamily",0],["eu","eu"],["exkcd",0],["exotica",0],["ext","ext"],["f",0],["fa","fa"],["fandom",0],["fanimutationwiki",0],["fat","fat"],["fedora",0],["ff","ff"],["fi","fi"],["finalfantasy",0],["finnix",0],["fiu-vro","fiu-vro"],["fj","fj"],["flickrphoto",0],["flickruser",0],["fo","fo"],["foldoc",0],["fon","fon"],["foundation",0],["foundationsite",0],["fr","fr"],["freebsdman",0],["freedomdefined",0],["freenode",0],["freesoft",0],["frp","frp"],["frr","frr"],["fur","fur"],["fy","fy"],["ga","ga"],["gag","gag"],["gan","gan"],["gcr","gcr"],["gd","gd"],["gentoo",0],["genwiki",0],["gerrit",0],["git",0],["github",0],["gitiles",0],["gitlab",0],["gl","gl"],["glk","glk"],["globalcontribs",0],["glottolog",0],["glottopedia",0],["gn","gn"],["gom","gom"],["google",0],["googledefine",0],["googlegroups",0],["gor","gor"],["got","got"],["gpe","gpe"],["gs",0],["gsw","als"],["gu","gu"],["guc","guc"],["gucprefix",0],["guildwarswiki",0],["gur","gur"],["gutenberg",0],["gutenbergwiki",0],["guw","guw"],["gv","gv"],["ha","ha"],["hackerspaces",0],["hak","hak"],["hammondwiki",0],["haw","haw"],["hdl",0],["he","he"],["heraldik",0],["hi","hi"],["hif","hif"],["ho","ho"],["horizonlabs",0],["hr","hr"],["hrfwiki",0],["hrwiki",0],["hsb","hsb"],["ht","ht"],["hu","hu"],["hy","hy"],["hyw","hyw"],["hz","hz"],["ia","ia"],["iarchive",0],["iba","iba"],["id","id"],["ie","ie"],["ig","ig"],["igl","igl"],["ii","ii"],["ik","ik"],["ilo","ilo"],["imdbcompany",0],["imdbname",0],["imdbtitle",0],["incubator",0],["infosphere",0],["inh","inh"],["io","io"],["irc",0],["ircrc",0],["ircs",0],["is","is"],["isni",0],["iso639-3",0],["issn",0],["it","it"],["iu","iu"],["ja","ja"],["jaglyphwiki",0],["jam","jam"],["jbo","jbo"],["jira",0],["jp","ja"],["jstor",0],["jv","jv"],["ka","ka"],["kaa","kaa"],["kab","kab"],["kai","kai"],["kaj","kaj"],["kamelo",0],["karlsruhe",0],["kbd","kbd"],["kbp","kbp"],["kcg","kcg"],["kg","kg"],["kge","kge"],["ki","ki"],["kj","kj"],["kk","kk"],["kl","kl"],["km","km"],["kn","kn"],["knc","knc"],["ko","ko"],["koi","koi"],["kr","kr"],["krc","krc"],["ks","ks"],["ksh","ksh"],["ku","ku"],["kus","kus"],["kv","kv"],["kw","kw"],["ky","ky"],["la","la"],["labsconsole",0],["lad","lad"],["lb","lb"],["lbe","lbe"],["lexemes",0],["lez","lez"],["lfn","lfn"],["lg","lg"],["li","li"],["liberachat",0],["libreplanet",0],["lij","lij"],["lingualibre",0],["linguistlist",0],["listarchive",0],["lld","lld"],["lmo","lmo"],["ln","ln"],["lo","lo"],["localwiki",0],["lofc",0],["lojban",0],["lokalhistoriewiki",0],["lostpedia",0],["lrc","lrc"],["lt","lt"],["ltg","ltg"],["luxo",0],["lv","lv"],["lzh","zh-classical"],["m",0],["mad","mad"],["mag","mag"],["mai","mai"],["mail",0],["mailarchive",0],["map-bms","map-bms"],["mariowiki",0],["marveldatabase",0],["mdf","mdf"],["mdwiki",0],["meatball",0],["mediawikiwiki",0],["mediazilla",0],["memoryalpha",0],["meta",0],["metawiki",0],["metawikimedia",0],["metawikipedia",0],["mg","mg"],["mh","mh"],["mhr","mhr"],["mi","mi"],["min","min"],["mineralienatlas",0],["miraheze",0],["mixnmatch",0],["mk","mk"],["ml","ml"],["mn","mn"],["mni","mni"],["mnw","mnw"],["mo","mo"],["moinmoin",0],["mos","mos"],["mosapedia",0],["mozillawiki",0],["mozillazinekb",0],["mr","mr"],["mrj","mrj"],["ms","ms"],["mt","mt"],["mus","mus"],["mw",0],["mwl","mwl"],["mwod",0],["mwot",0],["my","my"],["myv","myv"],["mzn","mzn"],["n",0],["na","na"],["nah","nah"],["nan","zh-min-nan"],["nap","nap"],["nara",0],["nb","no"],["nds","nds"],["nds-nl","nds-nl"],["ne","ne"],["new","new"],["ng","ng"],["nia","nia"],["nl","nl"],["nlab",0],["nn","nn"],["no","no"],["nost","nostalgia"],["nostalgia","nostalgia"],["nov","nov"],["nqo","nqo"],["nr","nr"],["nrm","nrm"],["nso","nso"],["nup","nup"],["nv","nv"],["ny","ny"],["oc","oc"],["oclc",0],["oeis",0],["oewiki",0],["oldwikisource",0],["olo","olo"],["olpc",0],["om","om"],["openlibrary",0],["openstreetmap",0],["openwetware",0],["or","or"],["organicdesign",0],["orthodoxwiki",0],["os","os"],["osmwiki",0],["otrs",0],["otrswiki",0],["outreach",0],["outreachwiki",0],["owasp",0],["pa","pa"],["pag","pag"],["pam","pam"],["pap","pap"],["paws",0],["pcd","pcd"],["pcm","pcm"],["pdc","pdc"],["petscan",0],["pfl","pfl"],["phab",0],["phabricator",0],["pi","pi"],["pih","pih"],["pl","pl"],["planetmath",0],["pmid",0],["pms","pms"],["pnb","pnb"],["pnt","pnt"],["pokewiki",0],["pokéwiki",0],["policy",0],["ppl","ppl"],["proofwiki",0],["ps","ps"],["pt","pt"],["pwn","pwn"],["pyrev",0],["pythoninfo",0],["q",0],["qu","qu"],["quality",0],["quarry",0],["rcirc",0],["regiowiki",0],["rev",0],["revo",0],["rfc",0],["rheinneckar",0],["rki","rki"],["rm","rm"],["rmy","rmy"],["rn","rn"],["ro","ro"],["roa-rup","roa-rup"],["roa-tara","roa-tara"],["rodovid",0],["rsk","rsk"],["rt",0],["ru","ru"],["rue","rue"],["rup","roa-rup"],["rw","rw"],["s",0],["sa","sa"],["sah","sah"],["sat","sat"],["sc","sc"],["scholar",0],["schoolswp",0],["schoolwiki",0],["scn","scn"],["sco","sco"],["scores",0],["scoutwiki",0],["sd","sd"],["se","se"],["securewikidc",0],["semantic-mw",0],["senseislibrary",0],["sep11",0],["sg","sg"],["sgs","bat-smg"],["sh","sh"],["sharemap",0],["shi","shi"],["shn","shn"],["shoutwiki",0],["shy","shy"],["si","si"],["silcode",0],["simple","simple"],["sk","sk"],["skr","skr"],["sl","sl"],["slashdot",0],["sm","sm"],["smn","smn"],["sn","sn"],["so","so"],["sourceforge",0],["spamcheck",0],["spcom",0],["species",0],["sq","sq"],["sr","sr"],["srn","srn"],["ss","ss"],["st","st"],["stats",0],["stewardry",0],["stq","stq"],["strategy",0],["strategywiki",0],["su","su"],["sulutil",0],["sv","sv"],["svn",0],["sw","sw"],["swtrain",0],["syl","syl"],["szl","szl"],["szy","szy"],["ta","ta"],["tardis",0],["tay","tay"],["tclerswiki",0],["tcy","tcy"],["tdd","tdd"],["te","te"],["tenwiki","ten"],["test2wiki","test2"],["testcommons",0],["testwiki","test"],["testwikidata",0],["tet","tet"],["tfwiki",0],["tg","tg"],["th","th"],["thelemapedia",0],["theopedia",0],["ti","ti"],["ticket",0],["tig","tig"],["tk","tk"],["tl","tl"],["tly","tly"],["tmbw",0],["tn","tn"],["to","to"],["tok","tok"],["tolkiengateway",0],["toolforge",0],["toolhub",0],["toollabs",0],["tools",0],["tpi","tpi"],["tr","tr"],["translatewiki",0],["trv","trv"],["ts","ts"],["tswiki",0],["tt","tt"],["tum","tum"],["tviv",0],["tw","tw"],["twiki",0],["twl",0],["ty","ty"],["tyv","tyv"],["tyvawiki",0],["udm","udm"],["ug","ug"],["uk","uk"],["umap",0],["uncyclopedia",0],["unihan",0],["ur","ur"],["urbandict",0],["usability",0],["usemod",0],["utrs",0],["uz","uz"],["v",0],["ve","ve"],["vec","vec"],["vep","vep"],["vi","vi"],["viaf",0],["vikidia",0],["vlos",0],["vls","vls"],["vo","vo"],["votewiki",0],["voy",0],["vro","fiu-vro"],["vrts",0],["vrtwiki",0],["w","en"],["wa","wa"],["war","war"],["wcna",0],["weirdgloop",0],["werelate",0],["wg","wg-en"],["wikia",0],["wikiapiary",0],["wikiasite",0],["wikibooks",0],["wikicities",0],["wikicity",0],["wikiconference",0],["wikidata",0],["wikiedudashboard",0],["wikifunctions",0],["wikifur",0],["wikihow",0],["wikiindex",0],["wikilivres",0],["wikilivresru",0],["wikimania",0],["wikimedia",0],["wikinews",0],["wikinfo",0],["wikinvest",0],["wikipapers",0],["wikipedia",0],["wikipediawikipedia",0],["wikiquote",0],["wikiskripta",0],["wikisophia",0],["wikisource",0],["wikisp",0],["wikispecies",0],["wikispore",0],["wikispot",0],["wikitech",0],["wikiti",0],["wikitrek",0],["wikiversity",0],["wikivoyage",0],["wikiwikiweb",0],["wikt",0],["wiktionary",0],["wm2005",0],["wm2006",0],["wm2007",0],["wm2008",0],["wm2009",0],["wm2010",0],["wm2011",0],["wm2012",0],["wm2013",0],["wm2014",0],["wm2015",0],["wm2016",0],["wm2017",0],["wm2018",0],["wmam",0],["wmania",0],["wmar",0],["wmat",0],["wmau",0],["wmbd",0],["wmbe",0],["wmbr",0],["wmca",0],["wmch",0],["wmcl",0],["wmcn",0],["wmco",0],["wmcz",0],["wmcz_docs",0],["wmcz_old",0],["wmdc",0],["wmde",0],["wmdeblog",0],["wmdk",0],["wmdoc",0],["wmec",0],["wmee",0],["wmes",0],["wmet",0],["wmf",0],["wmfblog",0],["wmfdashboard",0],["wmfi",0],["wmfr",0],["wmge",0],["wmhi",0],["wmhk",0],["wmhu",0],["wmid",0],["wmil",0],["wmin",0],["wmit",0],["wmke",0],["wmmk",0],["wmmx",0],["wmnl",0],["wmno",0],["wmnoc",0],["wmnoch",0],["wmnyc",0],["wmpa-us",0],["wmph",0],["wmpl",0],["wmplsite",0],["wmpt",0],["wmpunjabi",0],["wmromd",0],["wmrs",0],["wmru",0],["wmse",0],["wmsk",0],["wmteam",0],["wmtr",0],["wmtw",0],["wmua",0],["wmuk",0],["wmve",0],["wmza",0],["wo","wo"],["wookieepedia",0],["wowwiki",0],["wplibrary",0],["wurmpedia",0],["wuu","wuu"],["xal","xal"],["xh","xh"],["xkcd",0],["xmf","xmf"],["xtools",0],["yi","yi"],["yo","yo"],["yue","zh-yue"],["za","za"],["zea","zea"],["zgh","zgh"],["zh","zh"],["zh-classical","zh-classical"],["zh-cn","zh"],["zh-min-nan","zh-min-nan"],["zh-tw","zh"],["zh-yue","zh-yue"],["zu","zu"],["zum",0]]);
const WP_LANGUAGES = new Map([["ab","Abkhazian"],["ace","Acehnese"],["ady","Adyghe"],["af","Afrikaans"],["als","Alemannic"],["alt","Southern Altai"],["am","Amharic"],["ami","Amis"],["an","Aragonese"],["ang","Old English"],["ann","Obolo"],["anp","Angika"],["ar","Arabic"],["arc","Aramaic"],["ary","Moroccan Arabic"],["arz","Egyptian Arabic"],["as","Assamese"],["ast","Asturian"],["atj","Atikamekw"],["av","Avaric"],["avk","Kotava"],["awa","Awadhi"],["ay","Aymara"],["az","Azerbaijani"],["azb","South Azerbaijani"],["ba","Bashkir"],["ban","Balinese"],["bar","Bavarian"],["bat-smg","Samogitian"],["bbc","Batak Toba"],["bcl","Central Bikol"],["bdr","West Coast Bajau"],["be","Belarusian"],["be-tarask","Belarusian (Taraškievica orthography)"],["bew","Betawi"],["bg","Bulgarian"],["bh","Bhojpuri"],["bi","Bislama"],["bjn","Banjar"],["blk","Pa'O"],["bm","Bambara"],["bn","Bangla"],["bo","Tibetan"],["bpy","Bishnupriya"],["br","Breton"],["bs","Bosnian"],["btm","Batak Mandailing"],["bug","Buginese"],["bxr","Russia Buriat"],["ca","Catalan"],["cbk-zam","Chavacano"],["cdo","Mindong"],["ce","Chechen"],["ceb","Cebuano"],["ch","Chamorro"],["chr","Cherokee"],["chy","Cheyenne"],["ckb","Central Kurdish"],["co","Corsican"],["crh","Crimean Tatar"],["cs","Czech"],["csb","Kashubian"],["cu","Church Slavic"],["cv","Chuvash"],["cy","Welsh"],["da","Danish"],["dag","Dagbani"],["de","German"],["dga","Southern Dagaare"],["din","Dinka"],["diq","Dimli"],["dsb","Lower Sorbian"],["dtp","Central Dusun"],["dty","Doteli"],["dv","Divehi"],["dz","Dzongkha"],["ee","Ewe"],["el","Greek"],["eml","Emiliano-Romagnolo"],["en","English"],["eo","Esperanto"],["es","Spanish"],["et","Estonian"],["eu","Basque"],["ext","Extremaduran"],["fa","Persian"],["fat","Fanti"],["ff","Fula"],["fi","Finnish"],["fiu-vro","Võro"],["fj","Fijian"],["fo","Faroese"],["fon","Fon"],["fr","French"],["frp","Arpitan"],["frr","Northern Frisian"],["fur","Friulian"],["fy","Western Frisian"],["ga","Irish"],["gag","Gagauz"],["gan","Gan"],["gcr","Guianan Creole"],["gd","Scottish Gaelic"],["gl","Galician"],["glk","Gilaki"],["gn","Guarani"],["gom","Goan Konkani"],["gor","Gorontalo"],["got","Gothic"],["gpe","Ghanaian Pidgin"],["gu","Gujarati"],["guc","Wayuu"],["gur","Frafra"],["guw","Gun"],["gv","Manx"],["ha","Hausa"],["hak","Hakka Chinese"],["haw","Hawaiian"],["he","Hebrew"],["hi","Hindi"],["hif","Fiji Hindi"],["hr","Croatian"],["hsb","Upper Sorbian"],["ht","Haitian Creole"],["hu","Hungarian"],["hy","Armenian"],["hyw","Western Armenian"],["ia","Interlingua"],["iba","Iban"],["id","Indonesian"],["ie","Interlingue"],["ig","Igbo"],["igl","Igala"],["ik","Inupiaq"],["ilo","Iloko"],["inh","Ingush"],["io","Ido"],["is","Icelandic"],["it","Italian"],["iu","Inuktitut"],["ja","Japanese"],["jam","Jamaican Creole English"],["jbo","Lojban"],["jv","Javanese"],["ka","Georgian"],["kaa","Kara-Kalpak"],["kab","Kabyle"],["kai","Karekare"],["kaj","Jju"],["kbd","Kabardian"],["kbp","Kabiye"],["kcg","Tyap"],["kg","Kongo"],["kge","Komering"],["ki","Kikuyu"],["kk","Kazakh"],["km","Khmer"],["kn","Kannada"],["knc","Central Kanuri"],["ko","Korean"],["koi","Komi-Permyak"],["krc","Karachay-Balkar"],["ks","Kashmiri"],["ksh","Colognian"],["ku","Kurdish"],["kus","Kusaal"],["kv","Komi"],["kw","Cornish"],["ky","Kyrgyz"],["la","Latin"],["lad","Ladino"],["lb","Luxembourgish"],["lbe","Lak"],["lez","Lezghian"],["lfn","Lingua Franca Nova"],["lg","Ganda"],["li","Limburgish"],["lij","Ligurian"],["lld","Ladin"],["lmo","Lombard"],["ln","Lingala"],["lo","Lao"],["lt","Lithuanian"],["ltg","Latgalian"],["lv","Latvian"],["mad","Madurese"],["mag","Magahi"],["mai","Maithili"],["map-bms","Banyumasan"],["mdf","Moksha"],["mg","Malagasy"],["mhr","Eastern Mari"],["mi","Māori"],["min","Minangkabau"],["mk","Macedonian"],["ml","Malayalam"],["mn","Mongolian"],["mni","Manipuri"],["mnw","Mon"],["mos","Mossi"],["mr","Marathi"],["mrj","Western Mari"],["ms","Malay"],["mt","Maltese"],["mwl","Mirandese"],["my","Burmese"],["myv","Erzya"],["mzn","Mazanderani"],["nah","Nahuatl"],["nap","Neapolitan"],["nds","Low German"],["nds-nl","Low Saxon"],["ne","Nepali"],["new","Newari"],["nia","Nias"],["nl","Dutch"],["nn","Norwegian Nynorsk"],["no","Norwegian"],["nov","Novial"],["nqo","N’Ko"],["nr","South Ndebele"],["nrm","Norman"],["nso","Northern Sotho"],["nup","Nupe"],["nv","Navajo"],["ny","Nyanja"],["oc","Occitan"],["olo","Livvi-Karelian"],["om","Oromo"],["or","Odia"],["os","Ossetic"],["pa","Punjabi"],["pag","Pangasinan"],["pam","Pampanga"],["pap","Papiamento"],["pcd","Picard"],["pcm","Nigerian Pidgin"],["pdc","Pennsylvania German"],["pfl","Palatine German"],["pi","Pali"],["pl","Polish"],["pms","Piedmontese"],["pnb","Western Punjabi"],["pnt","Pontic"],["ppl","Nawat"],["ps","Pashto"],["pt","Portuguese"],["pwn","Paiwan"],["qu","Quechua"],["rki","Arakanese"],["rm","Romansh"],["rmy","Vlax Romani"],["rn","Rundi"],["ro","Romanian"],["roa-rup","Aromanian"],["roa-tara","Tarantino"],["rsk","Pannonian Rusyn"],["ru","Russian"],["rue","Rusyn"],["rw","Kinyarwanda"],["sa","Sanskrit"],["sah","Yakut"],["sat","Santali"],["sc","Sardinian"],["scn","Sicilian"],["sco","Scots"],["sd","Sindhi"],["se","Northern Sami"],["sg","Sango"],["sh","Serbo-Croatian"],["shi","Tachelhit"],["shn","Shan"],["si","Sinhala"],["simple","Simple English"],["sk","Slovak"],["skr","Saraiki"],["sl","Slovenian"],["sm","Samoan"],["smn","Inari Sami"],["sn","Shona"],["so","Somali"],["sq","Albanian"],["sr","Serbian"],["srn","Sranan Tongo"],["ss","Swati"],["st","Southern Sotho"],["stq","Saterland Frisian"],["su","Sundanese"],["sv","Swedish"],["sw","Swahili"],["syl","Sylheti"],["szl","Silesian"],["szy","Sakizaya"],["ta","Tamil"],["tay","Atayal"],["tcy","Tulu"],["tdd","Tai Nuea"],["te","Telugu"],["tet","Tetum"],["tg","Tajik"],["th","Thai"],["ti","Tigrinya"],["tig","Tigre"],["tk","Turkmen"],["tl","Tagalog"],["tly","Talysh"],["tn","Tswana"],["to","Tongan"],["tok","Toki Pona"],["tpi","Tok Pisin"],["tr","Turkish"],["trv","Taroko"],["ts","Tsonga"],["tt","Tatar"],["tum","Tumbuka"],["tw","Twi"],["ty","Tahitian"],["tyv","Tuvinian"],["udm","Udmurt"],["ug","Uyghur"],["uk","Ukrainian"],["ur","Urdu"],["uz","Uzbek"],["ve","Venda"],["vec","Venetian"],["vep","Veps"],["vi","Vietnamese"],["vls","West Flemish"],["vo","Volapük"],["wa","Walloon"],["war","Waray"],["wo","Wolof"],["wuu","Wu"],["xal","Kalmyk"],["xh","Xhosa"],["xmf","Mingrelian"],["yi","Yiddish"],["yo","Yoruba"],["za","Zhuang"],["zea","Zeelandic"],["zgh","Standard Moroccan Tamazight"],["zh","Chinese"],["zh-classical","Literary Chinese"],["zh-min-nan","Minnan"],["zh-yue","Cantonese"],["zu","Zulu"]]);
const appendixWithSlash = ["Adûni", "Afrihili", "Arabic_roots", "Belter_Creole", "Bibliography", "Black_Speech", "Bolak", "Communicationssprache", "Dothraki", "Eloi", "Gestures", "Glosa", "Goa'uld", "High_Valyrian", "Idiom_Neutral", "Interlingue", "Interslavic", "Klingon", "Kotava", "Láadan", "Lapine", "Lingua_Franca_Nova", "Lojban", "Mandalorian", "Medefaidrin", "Minecraft", "Mundolinco", "Na'vi", "Neo", "Novial", "Noxilo", "Protologisms/Long_words", "Quenya", "Romanova", "Sindarin", "Snowclones", "Talossan", "Toki_Pona", "Unas"];
const appendixNoSlash = ["Boldface", "Capital_letter", "Italics", "Lowercase_letter", "Possessive", "Repetition", "Small_caps", "Strikethrough", "Subscript", "Superscript", "Underline", "Uppercase_letter"];
const animationSpeed = 0.2; // seconds
mw.util.addCSS(`
.wikt-page-preview ol {
margin: 0 0.5em 0 1.5em;
padding: 0;
}
.wikt-page-preview dl {
margin-bottom: 0;
}
.wikt-page-preview p {
margin: 0;
}
/* popupContainer has the opacity animation, while popup gets translated. */
.popup-fade-in-up, .popup-fade-in-down {
animation: popup-fade-in ${animationSpeed}s ease forwards;
}
.popup-fade-out-up, .popup-fade-out-down {
animation: popup-fade-out ${animationSpeed}s ease forwards;
}
.popup-fade-in-up > div {
animation: popup-move-in-up ${animationSpeed}s ease forwards;
}
.popup-fade-in-down > div {
animation: popup-move-in-down ${animationSpeed}s ease forwards;
}
.popup-fade-out-up > div {
animation: popup-move-out-up ${animationSpeed}s ease forwards;
}
.popup-fade-out-down > div {
animation: popup-move-out-down ${animationSpeed}s ease forwards;
}
@keyframes popup-move-in-up {
0% {
transform: translate(0, 20px);
}
}
@keyframes popup-move-in-down {
0% {
transform: translate(0, -20px);
}
}
@keyframes popup-move-out-up {
100% {
transform: translate(0, -20px);
}
}
@keyframes popup-move-out-down {
100% {
transform: translate(0, 20px);
}
}
@keyframes popup-fade-out {
100% {
opacity: 0;
}
}
@keyframes popup-fade-in {
0% {
opacity: 0;
}
}
.ring-loader {
margin: auto;
width: 24px;
height: 24px;
border-radius: 50%;
border-top: 5px solid var(--wikt-palette-black);
border-bottom: 5px solid var(--wikt-palette-black);
border-left: 5px solid transparent;
border-right: 5px solid transparent;
animation: spin 1.2s linear infinite;
}
@keyframes spin {
100% {
transform: rotate(360deg);
}
}
.preview-headerlink, .preview-headerlink:visited {
color: inherit;
font-weight: bold;
}
/* Hack: get the speaker icon on Wikipedia articles without having to load Phonos. */
.ext-phonos .oo-ui-buttonElement-button:after {
content: "🔊";
}
.ext-phonos * {
display: inline !important;
padding: 0 !important;
margin: 0 !important;
}
`);
const definitionIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M15 2a7.65 7.65 0 00-5 2 7.65 7.65 0 00-5-2H1v15h4a7.65 7.65 0 015 2 7.65 7.65 0 015-2h4V2zm2.5 13.5H14a4.38 4.38 0 00-3 1V5s1-1.5 4-1.5h2.5z"></path></g></svg>`;
const glossaryIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M12.43 14.34A5 5 0 0110 15a5 5 0 113.95-2L17 16.09V3a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h10a2 2 0 001.45-.63z"></path><circle cx="10" cy="10" r="3"></circle></g></svg>`;
const articleIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M5 1a2 2 0 00-2 2v14a2 2 0 002 2h10a2 2 0 002-2V3a2 2 0 00-2-2zm0 3h5v1H5zm0 2h5v1H5zm0 2h5v1H5zm10 7H5v-1h10zm0-2H5v-1h10zm0-2H5v-1h10zm0-2h-4V4h4z"></path></g></svg>`;
const bibliographyIcon = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true" fill="currentColor"><g><path d="M3 2h12c.5 0 1 .5 1 1v1c0 .5-.5 1-1 1H3c0 0 .5-.5.5-1V3c0-.5-.5-1-.5-1zM4 6h12c.5 0 1 .5 1 1v1c0 .5-.5 1-1 1H4c0 0 .5-.5.5-1V7c0-.5-.5-1-.5-1zM3 10h12c.5 0 1 .5 1 1v1c0 .5-.5 1-1 1H3c0 0 .5-.5.5-1v-1c0-.5-.5-1-.5-1zM4 14h12c.5 0 1 .5 1 1v1c0 .5-.5 1-1 1H4c0 0 .5-.5.5-1v-1c0-.5-.5-1-.5-1z"/></g></svg>`;
const loader = document.createElement("div");
loader.className = "ring-loader";
const popupContainer = document.createElement("div");
const popup = document.createElement("div");
const popupContent = document.createElement("div");
popupContainer.style = `display: none; position: absolute; filter: drop-shadow(0px 30px 30px rgba(0, 0, 0, 0.15)) drop-shadow(0px 0px 0.75px var(--wikt-palette-dullblue)); z-index: 801`;
popupContainer.className = "wikt-page-preview";
popup.style = "box-sizing: border-box; height: 100%; background: var(--wikt-palette-white)";
popupContent.style = "display: flex; flex-direction: column; border-radius: 2px; box-sizing: border-box; color: var(--wikt-palette-black); overflow: auto; height: 100%; overflow-wrap: break-word; scrollbar-width: thin";
document.body.append(popupContainer);
popupContainer.append(popup);
popup.append(popupContent);
let popupTimer;
let openLink;
let mouseX, mouseY;
let API_controller = new AbortController();
function closePopup() {
if (!openLink) return;
openLink = null;
API_controller.abort();
API_controller = new AbortController();
if (popupContainer.classList.contains("popup-fade-in-up")) {
popupContainer.classList.remove("popup-fade-in-up");
popupContainer.classList.add("popup-fade-out-down");
} else {
popupContainer.classList.remove("popup-fade-in-down");
popupContainer.classList.add("popup-fade-out-up");
}
// After the animation has completed, reset the popup to its initial state.
setTimeout(() => {
popupContainer.style.display = "none";
popupContainer.classList.remove("popup-fade-out-down", "popup-fade-out-up");
}, animationSpeed * 1000);
}
popup.addEventListener("pointerenter", () => {
clearTimeout(popupTimer);
});
popup.addEventListener("pointerleave", () => {
clearTimeout(popupTimer);
popupTimer = setTimeout(closePopup, 300);
});
function processLink(link) {
let linkTitle = decodeURIComponent(link.pathname.split("/wiki/")[1]);
let titlePrefix = linkTitle.includes(":") ? linkTitle.toLowerCase().split(":")[0] : "";
let resolvedTitle = linkTitle;
let API_domain = link.origin;
let linkAnchor = decodeURIComponent(link.hash.slice(1) || "");
let isWPlink = link.href.match(/^https:\/\/[^.]+.wikipedia.org\/wiki\//);
let isPreviewLink = Boolean(link.closest(".wikt-page-preview"));
let isGlossary = /^Appendix:.*[Gg]lossary$/.test(linkTitle);
// Start with various checks to determine whether a link should be processed.
if (link.matches(`nav a, .cancelLink a, .mw-widget-titleOptionWidget a, .new, .preview-headerlink, .external, [href$="#"]`)) {
return;
} else if (isGlossary) {
// Links to glossary *must* have an anchor.
if (!linkAnchor) return;
} else if (linkTitle.startsWith("Appendix:Bibliography/")) {
// Bibliography subpages must have an anchor starting with `bib-`.
if (!linkAnchor || !linkAnchor.startsWith("bib-")) return;
} else if (!isWPlink) {
// Other Wiktionary pages must not have a lowercase-starting anchor (filters out some unhelpful links).
if (/^[a-z]/.test(linkAnchor)) return;
}
if (link.href.startsWith("https://en.wiktionary.org/wiki/")) {
// Filter out all interwiki prefixes.
if (INTERWIKI_PREFIXES.has(titlePrefix))
return;
let {namespace, title} = new mw.Title(linkTitle);
if (namespace === 0) { // Mainspace
if (title.endsWith("/translations"))
return;
} else if (namespace === 100) { // Appendix space
if (!appendixWithSlash.some(lang => title.startsWith(lang + "/")) && !appendixNoSlash.includes(title) && !isGlossary)
return;
} else if (namespace !== 118) { // Reconstruction space
return;
}
} else if (isWPlink) {
// Filter out invalid interwiki prefixes.
if (INTERWIKI_PREFIXES.get(titlePrefix) === 0)
return;
// Get the resolved title if it's a language interwiki.
if (INTERWIKI_PREFIXES.has(titlePrefix)) {
API_domain = `https://${INTERWIKI_PREFIXES.get(titlePrefix)}.wikipedia.org`;
resolvedTitle = linkTitle.substring(linkTitle.indexOf(":") + 1);
// If the resolved title contains another interwiki, return.
let resolvedTitlePrefix = resolvedTitle.includes(":") ? resolvedTitle.toLowerCase().split(":")[0] : ""; // same as titlePrefix
if (INTERWIKI_PREFIXES.has(resolvedTitlePrefix))
return;
}
} else {
return;
}
link.addEventListener("pointerover", event => {
[mouseX, mouseY] = [event.clientX, event.clientY];
clearTimeout(popupTimer);
// Ignore links which are already open.
if (link === openLink)
return;
if (!isPreviewLink)
closePopup();
// Fetch popup text immediately on hover to reduce delay.
let responsePromise = fetch(`${API_domain}/api/rest_v1/page/html/${encodeURIComponent(resolvedTitle)}`, {
headers: {"Api-User-Agent": "Tiện ích được phát triển bởi User:Ioaxxere"},
signal: API_controller.signal
}).then(r => r.text()).catch(() => { /* fetch was aborted */ });
popupTimer = setTimeout(() => {
popupContainer.style.display = "";
popupContent.innerHTML = "";
popupContent.append(loader);
popupContent.style.padding = "14px 16px 8px";
// Disable link hovering until animation has completed.
popupContent.style.pointerEvents = "none";
setTimeout(() => popupContent.style.pointerEvents = "", animationSpeed * 1000);
// Adapt the popup for large, standard, and small size preference in Vector 2022.
let sizeSetting = 0;
if (document.documentElement.matches(".vector-feature-custom-font-size-clientpref-1"))
sizeSetting = 1;
else if (document.documentElement.matches(".vector-feature-custom-font-size-clientpref-2"))
sizeSetting = 2;
let width = [320, 340, 360][sizeSetting];
let height = [210, 225, 240][sizeSetting];
let triangleSize = [9, 10, 11][sizeSetting];
popupContainer.style.fontSize = ["14px", "15px", "17px"][sizeSetting];
popupContainer.style.lineHeight = ["21px", "22px", "24px"][sizeSetting];
popupContainer.style.width = width + "px";
popupContainer.style.height = height + "px";
if (isPreviewLink) {
// Trigger fade-in-down animation.
popupContainer.classList.remove("popup-fade-in-up", "popup-fade-in-down");
void popupContainer.offsetLeft; // force reflow
popupContainer.classList.add("popup-fade-in-down");
} else {
openLink = link;
// Get list of rects (lines) of the target element, then find the one whose midpoint is closest to mouseY.
// This ensures that the code can correctly handle multi-line links.
let linkBlock = [...event.target.getClientRects()].reduce((prev, next) => Math.abs((next.top + next.bottom) / 2 - mouseY) < Math.abs((prev.top + prev.bottom) / 2 - mouseY) ? next : prev);
// Horizontal position. Choose left or right depending on the side of the screen the mouse is in.
let leftPosition = mouseX - 30;
if (mouseX > document.documentElement.clientWidth / 2)
leftPosition = mouseX - width + 30;
// Ensure that the popup is at least 5px away from the side of the screen.
leftPosition = Math.max(5, Math.min(leftPosition, document.documentElement.clientWidth - width - 5));
popupContainer.style.left = leftPosition + document.documentElement.scrollLeft + "px";
// Vertical position. Prioritize fade-in-down unless there would be less than 10px of room above.
if (linkBlock.top > height + 10 || linkBlock.top > document.documentElement.clientHeight - linkBlock.bottom) {
popupContainer.style.top = linkBlock.top + document.documentElement.scrollTop - height + "px";
popupContainer.classList.add("popup-fade-in-down");
popup.style.padding = `0 0 ${triangleSize}px`;
// Create a triangle on the bottom.
popup.style.clipPath = `polygon(0 0, 100% 0, 100% calc(100% - ${triangleSize}px), ${mouseX - leftPosition + triangleSize}px calc(100% - ${triangleSize}px), ${mouseX - leftPosition}px 100%, ${mouseX - leftPosition - triangleSize}px calc(100% - ${triangleSize}px), 0 calc(100% - ${triangleSize}px))`;
} else {
popupContainer.style.top = linkBlock.bottom + document.documentElement.scrollTop + "px";
popupContainer.classList.add("popup-fade-in-up");
popup.style.padding = `${triangleSize}px 0 0`;
// Create a triangle on top.
popup.style.clipPath = `polygon(0 ${triangleSize}px, ${mouseX - leftPosition - triangleSize}px ${triangleSize}px, ${mouseX - leftPosition}px 0, ${mouseX - leftPosition + triangleSize}px ${triangleSize}px, 100% ${triangleSize}px, 100% 100%, 0 100%)`;
}
}
responsePromise.then(response => {
if (!response) return; // if the fetch was aborted
popupContent.innerHTML = "";
let responseDocument = new DOMParser().parseFromString(response, "text/html");
// Convert to absolute URLs.
responseDocument.querySelectorAll("a[href]").forEach(link => link.setAttribute("href", link.href));
let anchoredElement = responseDocument.getElementById(linkAnchor);
let popupHeader = document.createElement("div");
popupHeader.style = "font-size: 90%; color: var(--wikt-palette-deepblue)";
popupContent.append(popupHeader);
let iconContainer = document.createElement("span");
iconContainer.style = "float: right; margin-left: 10px; height: 20px; color: var(--wikt-palette-black)";
popupHeader.append(iconContainer);
let titleLink = document.createElement("a");
titleLink.href = link.href;
titleLink.title = link.title;
titleLink.className = "skin-invert-image preview-headerlink";
// Scrape entry content.
if (isWPlink) {
iconContainer.innerHTML = articleIcon;
titleLink.textContent = linkTitle.replaceAll("_", " ");
if (responseDocument.title)
titleLink.innerHTML = responseDocument.title; // sometimes gives HTML text
let WikipediaName = WP_LANGUAGES.get(API_domain.substr(8).split(".")[0]);
popupHeader.append(WikipediaName + " Wikipedia có bài viết về ", titleLink);
let articleContent = document.createElement("div");
articleContent.style.margin = "5px 0 0 10px";
let firstSection = responseDocument.querySelector("section");
if (firstSection && responseDocument.querySelector(`meta[property="mw:pageNamespace"][content="0"]`)) {
firstSection.querySelectorAll("b").forEach(boldElem => boldElem.outerHTML = boldElem.innerHTML);
firstSection.querySelectorAll(":scope > p, :scope > ul, :scope > ol").forEach(elem => articleContent.append(elem));
}
if (articleContent.childElementCount)
popupContent.append(articleContent);
} else if (isGlossary) {
iconContainer.innerHTML = glossaryIcon;
titleLink.textContent = linkAnchor.replaceAll("_", " ");
popupHeader.append("Glossary definition of ", titleLink);
if (anchoredElement && anchoredElement.matches(".template-anchor")) {
// Get succeeding `dd` elements. The HTML is <dt>...<span class="template-anchor"></span>...</dt><dd>...</dd><dd>...</dd>
for (let elem = anchoredElement.parentElement.nextElementSibling; elem && elem.matches("dd"); elem = elem.nextElementSibling) {
elem.style = "margin: 5px 0 0 15px";
popupContent.append(elem.cloneNode(true));
}
}
} else if (linkTitle.startsWith("Phụ lục:Thư mục học/")) {
iconContainer.innerHTML = bibliographyIcon;
let language = linkTitle.slice(22).replaceAll("_", " "); // trim "Appendix:Bibliography/" prefix
popupHeader.append(`${language} bibliography`);
if (anchoredElement && anchoredElement.matches(".bibliography-id")) {
let bibliographyCitation = document.createElement("div");
bibliographyCitation.style = "margin: 5px 0 0 15px";
bibliographyCitation.innerHTML = anchoredElement.innerHTML;
popupContent.append(bibliographyCitation);
}
} else {
const hasDefinitions = elem => Boolean(elem.querySelector("ol:not(.references), .ja-see, .zh-see"));
iconContainer.innerHTML = definitionIcon;
// Try to resolve the anchor element to #Chinese.
if (!anchoredElement && ["Tiếng Quan Thoại", "Gan", "Hakka", "Hokkien", "Literary_Chinese", "Mandarin", "Middle_Chinese", "Old_Chinese", "Wu", "Xiang"].includes(linkAnchor))
anchoredElement = responseDocument.querySelector("#Tiếng Trung Quốc");
// Try to guess the anchor target in the following order: English, Chinese, Translingual, [first h2 on page]
// Always prioritize an L2 section which contains definitions.
if (!linkAnchor) {
let pageH2s = [...responseDocument.querySelectorAll("h2")];
if (pageH2s.some(h2 => hasDefinitions(h2.parentElement)))
pageH2s = pageH2s.filter(h2 => hasDefinitions(h2.parentElement));
anchoredElement = pageH2s.find(h2 => h2.id === "English");
if (!anchoredElement) anchoredElement = pageH2s.find(h2 => h2.id === "Chinese");
if (!anchoredElement) anchoredElement = pageH2s.find(h2 => h2.id === "Translingual");
if (!anchoredElement) anchoredElement = pageH2s[0];
}
let displayTitle = document.createElement("strong");
displayTitle.textContent = linkTitle.split("/").pop().replaceAll("_", " ");
if (linkTitle.startsWith("Reconstruction:"))
displayTitle.textContent = "*" + displayTitle.textContent;
// Find localest section containing an h2.
let languageSection = anchoredElement;
while (languageSection && !languageSection.querySelector(":scope > h2"))
languageSection = languageSection.parentElement.closest("section");
// Make sure that the entry is well-formed.
if (languageSection) {
let language = languageSection.querySelector(":scope > h2").textContent;
let scrapeSection = anchoredElement.closest("section");
// Find localest section which contains any definitions.
while (scrapeSection && !hasDefinitions(scrapeSection))
scrapeSection = scrapeSection.parentElement.closest("section");
if (scrapeSection) {
let headwords = [...scrapeSection.querySelectorAll(".headword-line > strong")];
if (new Set(headwords.map(h => h.textContent)).size === 1) {
// If there is a single unique headword, replace the display title with that.
displayTitle = headwords[0];
if (displayTitle.querySelector("a > img"))
displayTitle = displayTitle.querySelector("a > img").cloneNode();
} else if (new Set(headwords.map(h => h.cloneNode().outerHTML)).size === 1) {
// If there is a single unique set of element attributes, use that with the display title.
headwords[0].innerHTML = displayTitle.innerHTML;
displayTitle = headwords[0];
}
// Remove existing links in the headword.
displayTitle.querySelectorAll("a").forEach(elem => elem.replaceWith(elem.textContent));
let ols = scrapeSection.querySelectorAll(":scope > ol:not(.references), section > ol:not(.references)");
if (anchoredElement.matches(".senseid")) {
[...anchoredElement.parentElement.childNodes].forEach(child => {
if (child !== anchoredElement) child.remove();
});
ols = [anchoredElement.closest("ol")];
}
for (let ol of ols) {
let POS_container = document.createElement("div");
POS_container.style.margin = "8px 0 5px 0";
let POS = document.createElement("span");
POS.style = "font-size: 110%; font-weight: bold";
POS.textContent = ol.closest("section").firstChild.textContent;
POS_container.append(POS);
let tlb = ol.closest("section").querySelector(".headword-line ~ .usage-label-term");
if (tlb)
POS_container.append(document.createElement("br"), tlb);
popupContent.append(POS_container, ol);
}
// {{ja-see}} and {{zh-see}} don't follow the normal format, so just insert them verbatim.
for (let seeTemplate of scrapeSection.querySelectorAll(".ja-see, .zh-see"))
popupContent.append(seeTemplate);
}
titleLink.append(displayTitle);
popupHeader.append("Xem trước định nghĩa " + language + " ", titleLink);
} else {
titleLink.append(displayTitle);
popupHeader.append("Xem trước định nghĩa ", titleLink);
}
}
// Post-fetch error handling.
if (popupContent.childElementCount > 1) {
// Clean up HTML.
popupContent.querySelectorAll("link, .previewonly, .maintenance-line, .mw-empty-elt, .reference, .Inline-Template, .vsHide").forEach(elem => elem.remove());
if (!isWPlink)
popupContent.querySelectorAll("li > ul").forEach(elem => elem.remove()); // remove quotations
for (let elem of popupContent.querySelectorAll("*"))
elem.removeAttribute("id"); // avoid inadvertently repeating IDs within a page
} else if (isWPlink) {
// Display a message if the Wikipedia article was invalid or not in mainspace.
let noArticle = document.createElement("div");
noArticle.style = "margin: 5px 0 0 15px; font-size: 90%";
noArticle.textContent = "(article content could not be previewed)";
popupContent.append(noArticle);
} else if (linkAnchor && !anchoredElement) {
// Display a message if the anchor is invalid.
let noSectionFound = document.createElement("div");
noSectionFound.style = "margin: 10px 0 0 7.5px; font-size: 90%";
let strong = document.createElement("strong");
strong.textContent = linkAnchor.replaceAll("_", " ");
noSectionFound.append("The ", strong, " section was not found on this page.");
popupContent.append(noSectionFound);
} else {
// Display a message if no definitions were found in the section.
let noDefinitionsFound = document.createElement("div");
noDefinitionsFound.style = "margin: 5px 0 0 15px; font-size: 90%";
noDefinitionsFound.textContent = "(không tìm thấy định nghĩa)";
popupContent.append(noDefinitionsFound);
}
// Reduce the padding if a scrollbar is present. The intended padding is 16px on each side.
if (width - popupHeader.clientWidth > 32)
popupContent.style.paddingRight = Math.max(4, 48 + popupHeader.clientWidth - width) + "px";
});
// (Hack?) make sure that `link pointerleave` doesn't cause the popup to immediately close.
setTimeout(() => clearTimeout(popupTimer), 0);
}, isPreviewLink ? 1200 : 400);
});
link.addEventListener("pointerout", () => {
clearTimeout(popupTimer);
if (link === openLink) {
popupTimer = setTimeout(closePopup, 300);
} else {
API_controller.abort();
API_controller = new AbortController();
}
});
link.addEventListener("pointermove", event => [mouseX, mouseY] = [event.clientX, event.clientY]);
link.addEventListener("click", () => clearTimeout(popupTimer));
}
// Process all links.
document.querySelectorAll("a").forEach(processLink);
// Process links which are added to the DOM after the gadget has run.
(new MutationObserver(events => events.flatMap(event => [...event.addedNodes]).forEach(node => {
if (node instanceof HTMLAnchorElement)
processLink(node);
if (node instanceof HTMLElement)
node.querySelectorAll("a").forEach(processLink);
}))).observe(document.body, {childList: true, subtree: true});
// Dispatch hover events to support [[w:MediaWiki:Gadget-ReferenceTooltips.js]].
const linkEvent = ev => openLink && openLink.dispatchEvent(new Event(ev, {bubbles: true, cancelable: true}));
// Note: for some reason an event listener on `popup` doesn't catch everything...
popupContainer.addEventListener("pointerenter", () => setTimeout(() => linkEvent("mouseover")));
popupContainer.addEventListener("pointerleave", () => linkEvent("mouseout"));
// Custom events
popupContainer.addEventListener("pointerenter", () => linkEvent("linkPopupHover"));
popupContainer.addEventListener("pointerleave", () => linkEvent("linkPopupUnhover"));
// </nowiki>
ovuydfpj83ga0m085ik9m34d0led8pu
2513945
2513942
2026-08-14T04:42:50Z
Hiyuune
50834
2513945
javascript
text/javascript
// {{documentation}}
// _________________________________________________________________________________________
// | |
// | === WARNING: GLOBAL GADGET FILE === |
// | Changes to this page affect many users. |
// | Please discuss changes on the [[Wiktionary:Grease Pit]] before editing. |
// |_________________________________________________________________________________________|
mw.loader.load('//en.wikipedia.org/w/index.php?action=raw&title=MediaWiki:Gadget-navpop.css&ctype=text/css', "text/css");
// Forked from [[:w:MediaWiki:Gadget-popups.js]]
$(function () {
//////////////////////////////////////////////////
// Globals
//
// Trying to shove as many of these as possible into the pg (popup globals) object
var pg = {
re: {}, // regexps
ns: {}, // namespaces
string: {}, // translatable strings
wiki: {}, // local site info
user: {}, // current user info
misc: {}, // YUCK PHOOEY
option: {}, // options, see newOption etc
optionDefault: {}, // default option values
flag: {}, // misc flags
cache: {}, // page and image cache
structures: {}, // navlink structures
timer: {}, // all sorts of timers (too damn many)
counter: {}, // .. and all sorts of counters
current: {}, // state info
fn: {}, // functions
endoflist: null
};
/* Bail if the gadget/script is being loaded twice */
if( window.pg ) {
return;
}
/* Export to global context */
window.pg = pg;
/// Local Variables: ///
/// mode:c ///
/// End: ///
// ENDFILE: main.js
// STARTFILE: actions.js
function setupTooltips(container, remove, force, popData) {
log('setupTooltips, container='+container+', remove='+remove);
if (!container) {
//<NOLITE>
// the main initial call
if (getValueOf('popupOnEditSelection') && document && document.editform && document.editform.wpTextbox1) {
document.editform.wpTextbox1.onmouseup=doSelectionPopup;
}
//</NOLITE>
// article/content is a structure-dependent thing
container = defaultPopupsContainer();
}
if (!remove && !force && container.ranSetupTooltipsAlready) { return; }
container.ranSetupTooltipsAlready = !remove;
var anchors;
anchors=container.getElementsByTagName('A');
setupTooltipsLoop(anchors, 0, 250, 100, remove, popData);
}
function defaultPopupsContainer() {
if (getValueOf('popupOnlyArticleLinks')) {
return document.getElementById('mw_content') ||
document.getElementById('content') ||
document.getElementById('article') || document;
}
return document;
}
function setupTooltipsLoop(anchors,begin,howmany,sleep, remove, popData) {
log(simplePrintf('setupTooltipsLoop(%s,%s,%s,%s,%s)', arguments));
var finish=begin+howmany;
var loopend = Math.min(finish, anchors.length);
var j=loopend - begin;
log ('setupTooltips: anchors.length=' + anchors.length + ', begin=' + begin +
', howmany=' + howmany + ', loopend=' + loopend + ', remove=' + remove);
var doTooltip= remove ? removeTooltip : addTooltip;
// try a faster (?) loop construct
if (j > 0) {
do {
var a=anchors[loopend - j];
if (typeof a==='undefined' || !a || !a.href) {
log('got null anchor at index ' + loopend - j);
continue;
}
doTooltip(a, popData);
} while (--j);
}
if (finish < anchors.length) {
setTimeout(function() {
setupTooltipsLoop(anchors,finish,howmany,sleep,remove,popData);},
sleep);
} else {
if ( !remove && ! getValueOf('popupTocLinks')) { rmTocTooltips(); }
pg.flag.finishedLoading=true;
}
}
// eliminate popups from the TOC
// This also kills any onclick stuff that used to be going on in the toc
function rmTocTooltips() {
var toc=document.getElementById('toc');
if (toc) {
var tocLinks=toc.getElementsByTagName('A');
var tocLen = tocLinks.length;
for (var j=0; j<tocLen; ++j) {
removeTooltip(tocLinks[j], true);
}
}
}
function addTooltip(a, popData) {
if ( !isPopupLink(a) ) { return; }
a.onmouseover=mouseOverWikiLink;
a.onmouseout= mouseOutWikiLink;
a.onmousedown = killPopup;
a.hasPopup = true;
a.popData = popData;
}
function removeTooltip(a) {
if ( !a.hasPopup ) { return; }
a.onmouseover = null;
a.onmouseout = null;
if (a.originalTitle) { a.title = a.originalTitle; }
a.hasPopup=false;
}
function removeTitle(a) {
if (!a.originalTitle) {
a.originalTitle=a.title;
}
a.title='';
}
function restoreTitle(a) {
if ( a.title || !a.originalTitle ) { return; }
a.title = a.originalTitle;
}
function registerHooks(np) {
var popupMaxWidth=getValueOf('popupMaxWidth');
if (typeof popupMaxWidth === 'number') {
var setMaxWidth = function () {
np.mainDiv.style.maxWidth = popupMaxWidth + 'px';
np.maxWidth = popupMaxWidth;
};
np.addHook(setMaxWidth, 'unhide', 'before');
}
//<NOLITE>
np.addHook(addPopupShortcuts, 'unhide', 'after');
np.addHook(rmPopupShortcuts, 'hide', 'before');
//</NOLITE>
}
function removeModifierKeyHandler(a) {
//remove listeners for modifier key if any that were added in mouseOverWikiLink
document.removeEventListener('keydown', a.modifierKeyHandler, false);
document.removeEventListener('keyup', a.modifierKeyHandler, false);
}
function mouseOverWikiLink(evt) {
if (!evt && window.event) {evt=window.event;}
// if the modifier is needed, listen for it,
// we will remove the listener when we mouseout of this link or kill popup.
if (getValueOf('popupModifier')) {
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
var key = action=='disable' ? 'keyup' : 'keydown';
var a = this;
a.modifierKeyHandler = function(evt) {
mouseOverWikiLink2(a, evt);
};
document.addEventListener(key, a.modifierKeyHandler, false);
}
return mouseOverWikiLink2(this, evt);
}
/**
* Gets the references list item that the provided footnote link targets. This
* is typically a li element within the ol.references element inside the reflist.
* @param {Element} a - A footnote link.
* @returns {Element|boolean} The targeted element, or false if one can't be found.
*/
function footnoteTarget(a) {
var aTitle=Title.fromAnchor(a);
// We want ".3A" rather than "%3A" or "?" here, so use the anchor property directly
var anch = aTitle.anchor;
if ( ! /^(cite_note-|_note-|endnote)/.test(anch) ) { return false; }
var lTitle=Title.fromURL(location.href);
if ( lTitle.toString(true) !== aTitle.toString(true) ) { return false; }
var el=document.getElementById(anch);
while ( el && typeof el.nodeName === 'string') {
var nt = el.nodeName.toLowerCase();
if ( nt === 'li' ) { return el; }
else if ( nt === 'body' ) { return false; }
else if ( el.parentNode ) { el=el.parentNode; }
else { return false; }
}
return false;
}
function footnotePreview(x, navpop) {
setPopupHTML('<hr />' + x.innerHTML, 'popupPreview', navpop.idNumber);
}
function modifierPressed(evt) {
var mod=getValueOf('popupModifier');
if (!mod) { return false; }
if (!evt && window.event) {evt=window.event;}
return ( evt && mod && evt[mod.toLowerCase() + 'Key'] );
}
// Checks if the correct modifier pressed/unpressed if needed
function isCorrectModifier(a,evt) {
if (!getValueOf('popupModifier')) { return true; }
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
return ( action == 'enable' && modifierPressed(evt) ||
action == 'disable' && !modifierPressed(evt) );
}
function mouseOverWikiLink2(a, evt) {
if (!isCorrectModifier(a,evt)) { return; }
if ( getValueOf('removeTitles') ) { removeTitle(a); }
if ( a==pg.current.link && a.navpopup && a.navpopup.isVisible() ) { return; }
pg.current.link=a;
if (getValueOf('simplePopups') && !pg.option.popupStructure) {
// reset *default value* of popupStructure
setDefault('popupStructure', 'original');
}
var article=(new Title()).fromAnchor(a);
// set global variable (ugh) to hold article (wikipage)
pg.current.article = article;
if (!a.navpopup) {
a.navpopup=newNavpopup(a, article);
pg.current.linksHash[a.href] = a.navpopup;
pg.current.links.push(a);
}
if (a.navpopup.pending === null || a.navpopup.pending !== 0) {
// either fresh popups or those with unfinshed business are redone from scratch
simplePopupContent(a, article);
}
a.navpopup.showSoonIfStable(a.navpopup.delay);
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=setInterval(checkPopupPosition, 600);
if(getValueOf('simplePopups')) {
if (getValueOf('popupPreviewButton') && !a.simpleNoMore) {
var d=document.createElement('div');
d.className='popupPreviewButtonDiv';
var s=document.createElement('span');
d.appendChild(s);
s.className='popupPreviewButton';
s['on' + getValueOf('popupPreviewButtonEvent')] = function() {
a.simpleNoMore=true;
d.style.display = "none";
nonsimplePopupContent(a,article);
};
s.innerHTML=popupString('show preview');
setPopupHTML(d, 'popupPreview', a.navpopup.idNumber);
}
}
if (a.navpopup.pending !== 0 ) {
nonsimplePopupContent(a, article);
}
}
// simplePopupContent: the content that do not require additional download
// (it is shown even when simplePopups is true)
function simplePopupContent(a, article) {
/* FIXME hack */ a.navpopup.hasPopupMenu=false;
a.navpopup.setInnerHTML(popupHTML(a));
fillEmptySpans({navpopup:a.navpopup});
if (getValueOf('popupDraggable'))
{
var dragHandle = getValueOf('popupDragHandle') || null;
if (dragHandle && dragHandle != 'all') {
dragHandle += a.navpopup.idNumber;
}
setTimeout(function(){a.navpopup.makeDraggable(dragHandle);}, 150);
}
//<NOLITE>
if (getValueOf('popupRedlinkRemoval') && a.className=='new') {
setPopupHTML('<br>'+popupRedlinkHTML(article), 'popupRedlink', a.navpopup.idNumber);
}
//</NOLITE>
}
function debugData(navpopup) {
if(getValueOf('popupDebugging') && navpopup.idNumber) {
setPopupHTML('idNumber='+navpopup.idNumber + ', pending=' + navpopup.pending,
'popupError', navpopup.idNumber);
}
}
function newNavpopup(a, article) {
var navpopup = new Navpopup();
navpopup.fuzz=5;
navpopup.delay=getValueOf('popupDelay')*1000;
// increment global counter now
navpopup.idNumber = ++pg.idNumber;
navpopup.parentAnchor = a;
navpopup.parentPopup = (a.popData && a.popData.owner);
navpopup.article = article;
registerHooks(navpopup);
return navpopup;
}
// Should we show nonsimple context?
// If simplePopups is set to true, then we do not show nonsimple context,
// but if a bottom "show preview" was clicked we do show nonsimple context
function shouldShowNonSimple(a) {
return !getValueOf('simplePopups') || a.simpleNoMore;
}
// Should we show nonsimple context govern by the option (e.g. popupUserInfo)?
// If the user explicitly asked for nonsimple context by setting the option to true,
// then we show it even in nonsimple mode.
function shouldShow(a,option) {
if (shouldShowNonSimple(a)) {
return getValueOf(option);
} else {
return (typeof window[option] != 'undefined' ) && window[option];
}
}
function nonsimplePopupContent(a, article) {
var diff=null, history=null;
var params=parseParams(a.href);
var oldid=(typeof params.oldid=='undefined' ? null : params.oldid);
//<NOLITE>
if(shouldShow(a,'popupPreviewDiffs')) {
diff=params.diff;
}
if(shouldShow(a,'popupPreviewHistory')) {
history=(params.action=='history');
}
//</NOLITE>
a.navpopup.pending=0;
var referenceElement = footnoteTarget(a);
if (referenceElement) {
footnotePreview(referenceElement, a.navpopup);
//<NOLITE>
} else if ( diff || diff === 0 ) {
loadDiff(article, oldid, diff, a.navpopup);
} else if ( history ) {
loadAPIPreview('history', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.contribs.test(a.href) ) {
loadAPIPreview('contribs', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.backlinks.test(a.href) ) {
loadAPIPreview('backlinks', article, a.navpopup);
} else if ( // FIXME should be able to get all preview combinations with options
article.namespaceId()==pg.nsImageId &&
( shouldShow(a,'imagePopupsForImages') || ! anchorContainsImage(a) )
) {
loadAPIPreview('imagepagepreview', article, a.navpopup);
loadImage(article, a.navpopup);
//</NOLITE>
} else {
if (article.namespaceId() == pg.nsCategoryId &&
shouldShow(a,'popupCategoryMembers')) {
loadAPIPreview('category', article, a.navpopup);
} else if ((article.namespaceId() == pg.nsUserId || article.namespaceId() == pg.nsUsertalkId) &&
shouldShow(a,'popupUserInfo')) {
loadAPIPreview('userinfo', article, a.navpopup);
}
if (shouldShowNonSimple(a)) startArticlePreview(article, oldid, a.navpopup);
}
}
function pendingNavpopTask(navpop) {
if (navpop && navpop.pending === null) { navpop.pending=0; }
++navpop.pending;
debugData(navpop);
}
function completedNavpopTask(navpop) {
if (navpop && navpop.pending) { --navpop.pending; }
debugData(navpop);
}
function startArticlePreview(article, oldid, navpop) {
navpop.redir=0;
loadPreview(article, oldid, navpop);
}
function loadPreview(article, oldid, navpop) {
if (!navpop.redir) { navpop.originalArticle=article; }
article.oldid = oldid;
loadAPIPreview('revision', article, navpop);
}
function loadPreviewFromRedir(redirMatch, navpop) {
// redirMatch is a regex match
var target = new Title().fromWikiText(redirMatch[2]);
// overwrite (or add) anchor from original target
// mediawiki does overwrite; eg User:Lupin/foo3#Done
if ( navpop.article.anchor ) { target.anchor = navpop.article.anchor; }
navpop.redir++;
navpop.redirTarget=target;
//<NOLITE>
var warnRedir = redirLink(target, navpop.article);
setPopupHTML(warnRedir, 'popupWarnRedir', navpop.idNumber);
//</NOLITE>
navpop.article=target;
fillEmptySpans({redir: true, redirTarget: target, navpopup:navpop});
return loadPreview(target, null, navpop);
}
function insertPreview(download) {
if (!download.owner) { return; }
var redirMatch = pg.re.redirect.exec(download.data);
if (download.owner.redir === 0 && redirMatch) {
loadPreviewFromRedir(redirMatch, download.owner);
return;
}
if (download.owner.visible || !getValueOf('popupLazyPreviews')) {
insertPreviewNow(download);
} else {
var id=(download.owner.redir) ? 'PREVIEW_REDIR_HOOK' : 'PREVIEW_HOOK';
download.owner.addHook( function(){insertPreviewNow(download); return true;},
'unhide', 'after', id );
}
}
function insertPreviewNow(download) {
if (!download.owner) { return; }
var wikiText=download.data;
var navpop=download.owner;
var art=navpop.redirTarget || navpop.originalArticle;
//<NOLITE>
makeFixDabs(wikiText, navpop);
if (getValueOf('popupSummaryData')) {
getPageInfo(wikiText, download);
setPopupTrailer(getPageInfo(wikiText, download), navpop.idNumber);
}
var imagePage='';
if (art.namespaceId()==pg.nsImageId) { imagePage=art.toString(); }
else { imagePage=getValidImageFromWikiText(wikiText); }
if(imagePage) { loadImage(Title.fromWikiText(imagePage), navpop); }
//</NOLITE>
if (getValueOf('popupPreviews')) { insertArticlePreview(download, art, navpop); }
}
function insertArticlePreview(download, art, navpop) {
if (download && typeof download.data == typeof ''){
if (art.namespaceId()==pg.nsTemplateId && getValueOf('popupPreviewRawTemplates')) {
// FIXME compare/consolidate with diff escaping code for wikitext
var h='<hr /><span style="font-family: monospace;">' + download.data.entify().split('\\n').join('<br />\\n') + '</span>';
setPopupHTML(h, 'popupPreview', navpop.idNumber);
}
else {
var p=prepPreviewmaker(download.data, art, navpop);
p.showPreview();
}
}
}
function prepPreviewmaker(data, article, navpop) {
// deal with tricksy anchors
var d=anchorize(data, article.anchorString());
var urlBase=joinPath([pg.wiki.articlebase, article.urlString()]);
var p=new Previewmaker(d, urlBase, navpop);
return p;
}
// Try to imitate the way mediawiki generates HTML anchors from section titles
function anchorize(d, anch) {
if (!anch) { return d; }
var anchRe=RegExp('(?:=+\\s*' + literalizeRegex(anch).replace(/[_ ]/g, '[_ ]') + '\\s*=+|\\{\\{\\s*'+getValueOf('popupAnchorRegexp')+'\\s*(?:\\|[^|}]*)*?\\s*'+literalizeRegex(anch)+'\\s*(?:\\|[^}]*)?}})');
var match=d.match(anchRe);
if(match && match.length > 0 && match[0]) { return d.substring(d.indexOf(match[0])); }
// now try to deal with == foo [[bar|baz]] boom == -> #foo_baz_boom
var lines=d.split('\n');
for (var i=0; i<lines.length; ++i) {
lines[i]=lines[i].replace(RegExp('[[]{2}([^|\\]]*?[|])?(.*?)[\\]]{2}', 'g'), '$2')
.replace(/'''([^'])/g, '$1').replace(RegExp("''([^'])", 'g'), '$1');
if (lines[i].match(anchRe)) {
return d.split('\n').slice(i).join('\n').replace(RegExp('^[^=]*'), '');
}
}
return d;
}
function killPopup() {
removeModifierKeyHandler(this);
if (getValueOf('popupShortcutKeys')) { rmPopupShortcuts(); }
if (!pg) { return; }
if (pg.current.link && pg.current.link.navpopup) { pg.current.link.navpopup.banish(); }
pg.current.link=null;
abortAllDownloads();
if (pg.timer.checkPopupPosition) {
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=null;
}
return true; // preserve default action
}
// ENDFILE: actions.js
// STARTFILE: domdrag.js
/**
@fileoverview
The {@link Drag} object, which enables objects to be dragged around.
<pre>
*************************************************
dom-drag.js
09.25.2001
www.youngpup.net
**************************************************
10.28.2001 - fixed minor bug where events
sometimes fired off the handle, not the root.
*************************************************
Pared down, some hooks added by User:Lupin
Copyright Aaron Boodman.
Saying stupid things daily since March 2001.
</pre>
*/
/**
Creates a new Drag object. This is used to make various DOM elements draggable.
@constructor
*/
function Drag () {
/**
Condition to determine whether or not to drag. This function should take one parameter, an Event.
To disable this, set it to <code>null</code>.
@type Function
*/
this.startCondition = null;
/**
Hook to be run when the drag finishes. This is passed the final coordinates of
the dragged object (two integers, x and y). To disables this, set it to <code>null</code>.
@type Function
*/
this.endHook = null;
}
/**
Gets an event in a cross-browser manner.
@param {Event} e
@private
*/
Drag.prototype.fixE = function(e) {
if (typeof e == 'undefined') { e = window.event; }
if (typeof e.layerX == 'undefined') { e.layerX = e.offsetX; }
if (typeof e.layerY == 'undefined') { e.layerY = e.offsetY; }
return e;
};
/**
Initialises the Drag instance by telling it which object you want to be draggable, and what you want to drag it by.
@param {DOMElement} o The "handle" by which <code>oRoot</code> is dragged.
@param {DOMElement} oRoot The object which moves when <code>o</code> is dragged, or <code>o</code> if omitted.
*/
Drag.prototype.init = function(o, oRoot) {
var dragObj = this;
this.obj = o;
o.onmousedown = function(e) { dragObj.start.apply( dragObj, [e]); };
o.dragging = false;
o.popups_draggable = true;
o.hmode = true;
o.vmode = true;
o.root = oRoot ? oRoot : o ;
if (isNaN(parseInt(o.root.style.left, 10))) { o.root.style.left = "0px"; }
if (isNaN(parseInt(o.root.style.top, 10))) { o.root.style.top = "0px"; }
o.root.onthisStart = function(){};
o.root.onthisEnd = function(){};
o.root.onthis = function(){};
};
/**
Starts the drag.
@private
@param {Event} e
*/
Drag.prototype.start = function(e) {
var o = this.obj; // = this;
e = this.fixE(e);
if (this.startCondition && !this.startCondition(e)) { return; }
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10);
o.root.onthisStart(x, y);
o.lastMouseX = e.clientX;
o.lastMouseY = e.clientY;
var dragObj = this;
o.onmousemoveDefault = document.onmousemove;
o.dragging = true;
document.onmousemove = function(e) { dragObj.drag.apply( dragObj, [e] ); };
document.onmouseup = function(e) { dragObj.end.apply( dragObj, [e] ); };
return false;
};
/**
Does the drag.
@param {Event} e
@private
*/
Drag.prototype.drag = function(e) {
e = this.fixE(e);
var o = this.obj;
var ey = e.clientY;
var ex = e.clientX;
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10 );
var nx, ny;
nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));
this.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
this.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
this.obj.lastMouseX = ex;
this.obj.lastMouseY = ey;
this.obj.root.onthis(nx, ny);
return false;
};
/**
Ends the drag.
@private
*/
Drag.prototype.end = function() {
document.onmousemove=this.obj.onmousemoveDefault;
document.onmouseup = null;
this.obj.dragging = false;
if (this.endHook) {
this.endHook( parseInt(this.obj.root.style[this.obj.hmode ? "left" : "right"], 10),
parseInt(this.obj.root.style[this.obj.vmode ? "top" : "bottom"], 10));
}
};
// ENDFILE: domdrag.js
// STARTFILE: structures.js
//<NOLITE>
pg.structures.original={};
pg.structures.original.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle',
'popupUserData', 'popupData', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.original.popupRedirSpans=function () {
return ['popupRedir', 'popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'];
};
pg.structures.original.popupTitle=function (x) {
log ('defaultstructure.popupTitle');
if (!getValueOf('popupNavLinks')) {
return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
}
return '';
};
pg.structures.original.popupTopLinks=function (x) {
log ('defaultstructure.popupTopLinks');
if (getValueOf('popupNavLinks')) { return navLinksHTML(x.article, x.hint, x.params); }
return '';
};
pg.structures.original.popupImage=function(x) {
log ('original.popupImage, x.article='+x.article+', x.navpop.idNumber='+x.navpop.idNumber);
return imageHTML(x.article, x.navpop.idNumber);
};
pg.structures.original.popupRedirTitle=pg.structures.original.popupTitle;
pg.structures.original.popupRedirTopLinks=pg.structures.original.popupTopLinks;
function copyStructure(oldStructure, newStructure) {
pg.structures[newStructure]={};
for (var prop in pg.structures[oldStructure]) {
pg.structures[newStructure][prop]=pg.structures[oldStructure][prop];
}
}
copyStructure('original', 'nostalgia');
pg.structures.nostalgia.popupTopLinks=function(x) {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>';
str+='if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' +
editstr + '}';
var historystr='<<history|shortcut=h>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str += '<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>';
str += 'if(admin){<br>}else{*}<<move|shortcut=m>>';
// admin links
str += 'if(admin){*<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>}';
return navlinkStringToHTML(str, x.article, x.params);
};
pg.structures.nostalgia.popupRedirTopLinks=pg.structures.nostalgia.popupTopLinks;
/** -- fancy -- **/
copyStructure('original', 'fancy');
pg.structures.fancy.popupTitle=function (x) {
return navlinkStringToHTML('<font size=+0><<mainlink>></font>',x.article,x.params);
};
pg.structures.fancy.popupTopLinks=function(x) {
var hist='<<history|shortcut=h|hist>>|<<lastEdit|shortcut=/|last>>|<<editors|shortcut=E|eds>>';
var watch='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
var move='<<move|shortcut=m|move>>';
return navlinkStringToHTML('if(talk){' +
'<<edit|shortcut=e>>|<<new|shortcut=+|+>>*' + hist + '*' +
'<<article|shortcut=a>>|<<editArticle|edit>>' + '*' + watch + '*' + move +
'}else{<<edit|shortcut=e>>*' + hist +
'*<<talk|shortcut=t|>>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>' +
'*' + watch + '*' + move+'}<br>', x.article, x.params);
};
pg.structures.fancy.popupOtherLinks=function(x) {
var admin='<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*<<undelete|undeleteShort>>|<<delete|shortcut=d|del>>';
var user='<<contribs|shortcut=c>>if(wikimedia){|<<count|shortcut=#|#>>}';
user+='if(ipuser){|<<arin>>}else{*<<email|shortcut=E|'+
popupString('email')+'>>}if(admin){*<<block|shortcut=b>>}';
var normal='<<whatLinksHere|shortcut=l|links here>>*<<relatedChanges|shortcut=r|related>>';
return navlinkStringToHTML('<br>if(user){' + user + '*}if(admin){'+admin+'if(user){<br>}else{*}}' + normal,
x.article, x.params);
};
pg.structures.fancy.popupRedirTitle=pg.structures.fancy.popupTitle;
pg.structures.fancy.popupRedirTopLinks=pg.structures.fancy.popupTopLinks;
pg.structures.fancy.popupRedirOtherLinks=pg.structures.fancy.popupOtherLinks;
/** -- fancy2 -- **/
// hack for User:MacGyverMagic
copyStructure('fancy', 'fancy2');
pg.structures.fancy2.popupTopLinks=function(x) { // hack out the <br> at the end and put one at the beginning
return '<br>'+pg.structures.fancy.popupTopLinks(x).replace(RegExp('<br>$','i'),'');
};
pg.structures.fancy2.popupLayout=function () { // move toplinks to after the title
return ['popupError', 'popupImage', 'popupTitle', 'popupUserData', 'popupData', 'popupTopLinks', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
/** -- menus -- **/
copyStructure('original', 'menus');
pg.structures.menus.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupUserData', 'popupData', 'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.menus.popupTopLinks = function (x, shorter) {
// FIXME maybe this stuff should be cached
var s=[];
var dropdiv='<div class="popup_drop">';
var enddiv='</div>';
var hist='<<history|shortcut=h>>';
if (!shorter) { hist = '<menurow>' + hist +
'|<<historyfeed|rss>>|<<editors|shortcut=E>></menurow>'; }
var lastedit='<<lastEdit|shortcut=/|show last edit>>';
var thank='if(diff){<<thank|send thanks>>}';
var jsHistory='<<lastContrib|last set of edits>><<sinceMe|changes since mine>>';
var linkshere='<<whatLinksHere|shortcut=l|what links here>>';
var related='<<relatedChanges|shortcut=r|related changes>>';
var search='<menurow><<search|shortcut=s>>if(wikimedia){|<<globalsearch|shortcut=g|global>>}' +
'|<<google|shortcut=G|web>></menurow>';
var watch='<menurow><<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>></menurow>';
var protect='<menurow><<unprotect|unprotectShort>>|' +
'<<protect|shortcut=p>>|<<protectlog|log>></menurow>';
var del='<menurow><<undelete|undeleteShort>>|<<delete|shortcut=d>>|' +
'<<deletelog|log>></menurow>';
var move='<<move|shortcut=m|move page>>';
var nullPurge='<menurow><<nullEdit|shortcut=n|null edit>>|<<purge|shortcut=P>></menurow>';
var viewOptions='<menurow><<view|shortcut=v>>|<<render|shortcut=S>>|<<raw>></menurow>';
var editRow='if(oldid){' +
'<menurow><<edit|shortcut=e>>|<<editOld|shortcut=e|this revision>></menurow>' +
'<menurow><<revert|shortcut=v>>|<<undo>></menurow>' + '}else{<<edit|shortcut=e>>}';
var markPatrolled='if(rcid){<<markpatrolled|mark patrolled>>}';
var newTopic='if(talk){<<new|shortcut=+|new topic>>}';
var protectDelete='if(admin){' + protect + del + '}';
if (getValueOf('popupActionsMenu')) {
s.push( '<<mainlink>>*' + dropdiv + menuTitle('actions'));
} else {
s.push( dropdiv + '<<mainlink>>');
}
s.push( '<menu>');
s.push( editRow + markPatrolled + newTopic + hist + lastedit + thank );
if (!shorter) { s.push(jsHistory); }
s.push( move + linkshere + related);
if (!shorter) { s.push(nullPurge + search); }
if (!shorter) { s.push(viewOptions); }
s.push('<hr />' + watch + protectDelete);
s.push('<hr />' +
'if(talk){<<article|shortcut=a|view article>><<editArticle|edit article>>}' +
'else{<<talk|shortcut=t|talk page>><<editTalk|edit talk>>' +
'<<newTalk|shortcut=+|new topic>>}</menu>' + enddiv);
// user menu starts here
var email='<<email|shortcut=E|email user>>';
var contribs= 'if(wikimedia){<menurow>}<<contribs|shortcut=c|contributions>>if(wikimedia){</menurow>}' +
'if(admin){<menurow><<deletedContribs>></menurow>}';
s.push('if(user){*' + dropdiv + menuTitle('user'));
s.push('<menu>');
s.push('<menurow><<userPage|shortcut=u|user page>>|<<userSpace|space>></menurow>');
s.push('<<userTalk|shortcut=t|user talk>><<editUserTalk|edit user talk>>' +
'<<newUserTalk|shortcut=+|leave comment>>');
if(!shorter) { s.push( 'if(ipuser){<<arin>>}else{' + email + '}' ); }
else { s.push( 'if(ipuser){}else{' + email + '}' ); }
s.push('<hr />' + contribs + '<<userlog|shortcut=L|user log>>');
s.push('if(wikimedia){<<count|shortcut=#|edit counter>>}');
s.push('if(admin){<menurow><<unblock|unblockShort>>|<<block|shortcut=b|block user>></menurow>}');
s.push('<<blocklog|shortcut=B|block log>>');
s.push('</menu>' + enddiv + '}');
// popups menu starts here
if (getValueOf('popupSetupMenu') && !x.navpop.hasPopupMenu /* FIXME: hack */) {
x.navpop.hasPopupMenu=true;
s.push('*' + dropdiv + menuTitle('popupsMenu') + '<menu>');
s.push('<<togglePreviews|toggle previews>>');
s.push('<<purgePopups|reset>>');
s.push('<<disablePopups|disable>>');
s.push('</menu>'+enddiv);
}
return navlinkStringToHTML(s.join(''), x.article, x.params);
};
function menuTitle(s) {
return '<a href="#" noPopup=1>' + popupString(s) + '</a>';
}
pg.structures.menus.popupRedirTitle=pg.structures.menus.popupTitle;
pg.structures.menus.popupRedirTopLinks=pg.structures.menus.popupTopLinks;
copyStructure('menus', 'shortmenus');
pg.structures.shortmenus.popupTopLinks=function(x) {
return pg.structures.menus.popupTopLinks(x,true);
};
pg.structures.shortmenus.popupRedirTopLinks=pg.structures.shortmenus.popupTopLinks;
//</NOLITE>
pg.structures.lite={};
pg.structures.lite.popupLayout=function () {
return ['popupTitle', 'popupPreview' ];
};
pg.structures.lite.popupTitle=function (x) {
log (x.article + ': structures.lite.popupTitle');
//return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
return '<div><span class="popup_mainlink"><b>' + x.article.toString() + '</b></span></div>';
};
// ENDFILE: structures.js
// STARTFILE: autoedit.js
//<NOLITE>
function substitute(data,cmdBody) {
// alert('sub\nfrom: '+cmdBody.from+'\nto: '+cmdBody.to+'\nflags: '+cmdBody.flags);
var fromRe=RegExp(cmdBody.from, cmdBody.flags);
return data.replace(fromRe, cmdBody.to);
}
function execCmds(data, cmdList) {
for (var i=0; i<cmdList.length; ++i) {
data=cmdList[i].action(data, cmdList[i]);
}
return data;
}
function parseCmd(str) {
// returns a list of commands
if (!str.length) { return []; }
var p=false;
switch (str.charAt(0)) {
case 's':
p=parseSubstitute(str);
break;
default:
return false;
}
if (p) { return [p].concat(parseCmd(p.remainder)); }
return false;
}
function unEscape(str, sep) {
return str.split('\\\\').join('\\').split('\\'+sep).join(sep).split('\\n').join('\n');
}
function parseSubstitute(str) {
// takes a string like s/a/b/flags;othercmds and parses it
var from,to,flags,tmp;
if (str.length<4) { return false; }
var sep=str.charAt(1);
str=str.substring(2);
tmp=skipOver(str,sep);
if (tmp) { from=tmp.segment; str=tmp.remainder; }
else { return false; }
tmp=skipOver(str,sep);
if (tmp) { to=tmp.segment; str=tmp.remainder; }
else { return false; }
flags='';
if (str.length) {
tmp=skipOver(str,';') || skipToEnd(str, ';');
if (tmp) {flags=tmp.segment; str=tmp.remainder; }
}
return {action: substitute, from: from, to: to, flags: flags, remainder: str};
}
function skipOver(str,sep) {
var endSegment=findNext(str,sep);
if (endSegment<0) { return false; }
var segment=unEscape(str.substring(0,endSegment), sep);
return {segment: segment, remainder: str.substring(endSegment+1)};
}
/*eslint-disable*/
function skipToEnd(str,sep) {
return {segment: str, remainder: ''};
}
/*eslint-enable */
function findNext(str, ch) {
for (var i=0; i<str.length; ++i) {
if (str.charAt(i)=='\\') { i+=2; }
if (str.charAt(i)==ch) { return i; }
}
return -1;
}
function setCheckbox(param, box) {
var val=mw.util.getParamValue(param);
if (val) {
switch (val) {
case '1': case 'yes': case 'true':
box.checked=true;
break;
case '0': case 'no': case 'false':
box.checked=false;
}
}
}
function autoEdit() {
setupPopups( function () {
if (mw.util.getParamValue('autoimpl') !== popupString('autoedit_version') ) { return false; }
if (mw.util.getParamValue('autowatchlist') && mw.util.getParamValue('actoken')===autoClickToken()) {
pg.fn.modifyWatchlist(mw.util.getParamValue('title'), mw.util.getParamValue('action'));
}
if (!document.editform) { return false; }
if (autoEdit.alreadyRan) { return false; }
autoEdit.alreadyRan=true;
var cmdString=mw.util.getParamValue('autoedit');
if (cmdString) {
try {
var editbox=document.editform.wpTextbox1;
var cmdList=parseCmd(cmdString);
var input=editbox.value;
var output=execCmds(input, cmdList);
editbox.value=output;
} catch (dang) { return; }
// wikEd user script compatibility
if (typeof(wikEdUseWikEd) != 'undefined') {
if (wikEdUseWikEd === true) {
WikEdUpdateFrame();
}
}
}
setCheckbox('autominor', document.editform.wpMinoredit);
setCheckbox('autowatch', document.editform.wpWatchthis);
var rvid = mw.util.getParamValue('autorv');
if (rvid) {
var url=pg.wiki.apiwikibase + '?action=query&format=json&formatversion=2&prop=revisions&revids='+rvid;
startDownload(url, null, autoEdit2);
} else { autoEdit2(); }
} );
}
function autoEdit2(d) {
var summary=mw.util.getParamValue('autosummary');
var summaryprompt=mw.util.getParamValue('autosummaryprompt');
var summarynotice='';
if (d && d.data && mw.util.getParamValue('autorv')) {
var s = getRvSummary(summary, d.data);
if (s === false) {
summaryprompt=true;
summarynotice=popupString('Failed to get revision information, please edit manually.\n\n');
summary = simplePrintf(summary, [mw.util.getParamValue('autorv'), '(unknown)', '(unknown)']);
} else { summary = s; }
}
if (summaryprompt) {
var txt= summarynotice +
popupString('Enter a non-empty edit summary or press cancel to abort');
var response=prompt(txt, summary);
if (response) { summary=response; }
else { return; }
}
if (summary) { document.editform.wpSummary.value=summary; }
// Attempt to avoid possible premature clicking of the save button
// (maybe delays in updates to the DOM are to blame?? or a red herring)
setTimeout(autoEdit3, 100);
}
function autoClickToken() {
return mw.user.sessionId();
}
function autoEdit3() {
if( mw.util.getParamValue('actoken') != autoClickToken()) { return; }
var btn=mw.util.getParamValue('autoclick');
if (btn) {
if (document.editform && document.editform[btn]) {
var button=document.editform[btn];
var msg=tprintf('The %s button has been automatically clicked. Please wait for the next page to load.',
[ button.value ]);
bannerMessage(msg);
document.title='('+document.title+')';
button.click();
} else {
alert(tprintf('Could not find button %s. Please check the settings in your javascript file.',
[ btn ]));
}
}
}
function bannerMessage(s) {
var headings=document.getElementsByTagName('h1');
if (headings) {
var div=document.createElement('div');
div.innerHTML='<font size=+1><b>' + s + '</b></font>';
headings[0].parentNode.insertBefore(div, headings[0]);
}
}
function getRvSummary(template, json) {
try {
var o=getJsObj(json);
var edit = anyChild(o.query.pages).revisions[0];
var timestamp = edit.timestamp.split(/[A-Z]/g).join(' ').replace(/^ *| *$/g, '');
return simplePrintf(template, [edit.revid, timestamp, edit.userhidden ? '(hidden)' : edit.user ]);
} catch (badness) {
return false;
}
}
//</NOLITE>
// ENDFILE: autoedit.js
// STARTFILE: downloader.js
/**
@fileoverview
{@link Downloader}, a xmlhttprequest wrapper, and helper functions.
*/
/**
Creates a new Downloader
@constructor
@class The Downloader class. Create a new instance of this class to download stuff.
@param {String} url The url to download. This can be omitted and supplied later.
*/
function Downloader(url) {
if (typeof XMLHttpRequest!='undefined') { this.http = new XMLHttpRequest(); }
/**
The url to download
@type String
*/
this.url = url;
/**
A universally unique ID number
@type integer
*/
this.id=null;
/**
Modification date, to be culled from the incoming headers
@type Date
@private
*/
this.lastModified = null;
/**
What to do when the download completes successfully
@type Function
@private
*/
this.callbackFunction = null;
/**
What to do on failure
@type Function
@private
*/
this.onFailure = null;
/**
Flag set on <code>abort</code>
@type boolean
*/
this.aborted = false;
/**
HTTP method. See https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html for possibilities.
@type String
*/
this.method='GET';
/**
Async flag.
@type boolean
*/
this.async=true;
}
new Downloader();
/** Submits the http request. */
Downloader.prototype.send = function (x) {
if (!this.http) { return null; }
return this.http.send(x);
};
/** Aborts the download, setting the <code>aborted</code> field to true. */
Downloader.prototype.abort = function () {
if (!this.http) { return null; }
this.aborted=true;
return this.http.abort();
};
/** Returns the downloaded data. */
Downloader.prototype.getData = function () {if (!this.http) { return null; } return this.http.responseText;};
/** Prepares the download. */
Downloader.prototype.setTarget = function () {
if (!this.http) { return null; }
this.http.open(this.method, this.url, this.async);
this.http.setRequestHeader( 'Api-User-Agent', pg.misc.userAgent );
};
/** Gets the state of the download. */
Downloader.prototype.getReadyState=function () {if (!this.http) { return null; } return this.http.readyState;};
pg.misc.downloadsInProgress = { };
/** Starts the download.
Note that setTarget {@link Downloader#setTarget} must be run first
*/
Downloader.prototype.start=function () {
if (!this.http) { return; }
pg.misc.downloadsInProgress[this.id] = this;
this.http.send(null);
};
/** Gets the 'Last-Modified' date from the download headers.
Should be run after the download completes.
Returns <code>null</code> on failure.
@return {Date}
*/
Downloader.prototype.getLastModifiedDate=function () {
if(!this.http) { return null; }
var lastmod=null;
try {
lastmod=this.http.getResponseHeader('Last-Modified');
} catch (err) {}
if (lastmod) { return new Date(lastmod); }
return null;
};
/** Sets the callback function.
@param {Function} f callback function, called as <code>f(this)</code> on success
*/
Downloader.prototype.setCallback = function (f) {
if(!this.http) { return; }
this.http.onreadystatechange = f;
};
Downloader.prototype.getStatus = function() { if (!this.http) { return null; } return this.http.status; };
//////////////////////////////////////////////////
// helper functions
/** Creates a new {@link Downloader} and prepares it for action.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function newDownload(url, id, callback, onfailure) {
var d=new Downloader(url);
if (!d.http) { return 'ohdear'; }
d.id=id;
d.setTarget();
if (!onfailure) {
onfailure=2;
}
var f = function () {
if (d.getReadyState() == 4) {
delete pg.misc.downloadsInProgress[this.id];
try {
if ( d.getStatus() == 200 ) {
d.data=d.getData();
d.lastModified=d.getLastModifiedDate();
callback(d);
} else if (typeof onfailure == typeof 1) {
if (onfailure > 0) {
// retry
newDownload(url, id, callback, onfailure - 1);
}
} else if (typeof onfailure === 'function') {
onfailure(d,url,id,callback);
}
} catch (somerr) { /* ignore it */ }
}
};
d.setCallback(f);
return d;
}
/** Simulates a download from cached data.
The supplied data is put into a {@link Downloader} as if it had downloaded it.
@param {String} url The url.
@param {integer} id The ID.
@param {Function} callback The callback, which is invoked immediately as <code>callback(d)</code>,
where <code>d</code> is the new {@link Downloader}.
@param {String} data The (cached) data.
@param {Date} lastModified The (cached) last modified date.
*/
function fakeDownload(url, id, callback, data, lastModified, owner) {
var d=newDownload(url,callback);
d.owner=owner;
d.id=id; d.data=data;
d.lastModified=lastModified;
return callback(d);
}
/**
Starts a download.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function startDownload(url, id, callback) {
var d=newDownload(url, id, callback);
if (typeof d == typeof '' ) { return d; }
d.start();
return d;
}
/**
Aborts all downloads which have been started.
*/
function abortAllDownloads() {
for ( var x in pg.misc.downloadsInProgress ) {
try {
pg.misc.downloadsInProgress[x].aborted=true;
pg.misc.downloadsInProgress[x].abort();
delete pg.misc.downloadsInProgress[x];
} catch (e) {}
}
}
// ENDFILE: downloader.js
// STARTFILE: livepreview.js
// TODO: location is often not correct (eg relative links in previews)
// NOTE: removed md5 and image and math parsing. was broken, lots of bytes.
/**
* InstaView - a Mediawiki to HTML converter in JavaScript
* Version 0.6.1
* Copyright (C) Pedro Fayolle 2005-2006
* https://en.wikipedia.org/wiki/User:Pilaf
* Distributed under the BSD license
*
* Changelog:
*
* 0.6.1
* - Fixed problem caused by \r characters
* - Improved inline formatting parser
*
* 0.6
* - Changed name to InstaView
* - Some major code reorganizations and factored out some common functions
* - Handled conversion of relative links (i.e. [[/foo]])
* - Fixed misrendering of adjacent definition list items
* - Fixed bug in table headings handling
* - Changed date format in signatures to reflect Mediawiki's
* - Fixed handling of [[:Image:...]]
* - Updated MD5 function (hopefully it will work with UTF-8)
* - Fixed bug in handling of links inside images
*
* To do:
* - Better support for math tags
* - Full support for <nowiki>
* - Parser-based (as opposed to RegExp-based) inline wikicode handling (make it one-pass and bullet-proof)
* - Support for templates (through AJAX)
* - Support for coloured links (AJAX)
*/
var Insta = {};
function setupLivePreview() {
// options
Insta.conf =
{
baseUrl: '',
user: {},
wiki: {
lang: pg.wiki.lang,
interwiki: pg.wiki.interwiki,
default_thumb_width: 180
},
paths: {
articles: pg.wiki.articlePath + '/',
// Only used for Insta previews with images. (not in popups)
math: '/math/',
images: '//upload.wikimedia.org/wikipedia/en/', // FIXME getImageUrlStart(pg.wiki.hostname),
images_fallback: '//upload.wikimedia.org/wikipedia/commons/',
},
locale: {
user: mw.config.get('wgFormattedNamespaces')[pg.nsUserId],
image: mw.config.get('wgFormattedNamespaces')[pg.nsImageId],
category: mw.config.get('wgFormattedNamespaces')[pg.nsCategoryId],
// shouldn't be used in popup previews, i think
months: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
}
};
// options with default values or backreferences
Insta.conf.user.name = Insta.conf.user.name || 'Wikipedian';
Insta.conf.user.signature = '[['+Insta.conf.locale.user+':'+Insta.conf.user.name+'|'+Insta.conf.user.name+']]';
//Insta.conf.paths.images = '//upload.wikimedia.org/wikipedia/' + Insta.conf.wiki.lang + '/';
// define constants
Insta.BLOCK_IMAGE = new RegExp('^\\[\\[(?:File|Image|'+Insta.conf.locale.image+
'):.*?\\|.*?(?:frame|thumbnail|thumb|none|right|left|center)', 'i');
}
Insta.dump = function(from, to)
{
if (typeof from == 'string') { from = document.getElementById(from); }
if (typeof to == 'string') { to = document.getElementById(to); }
to.innerHTML = this.convert(from.value);
};
Insta.convert = function(wiki)
{
var ll = (typeof wiki == 'string')? wiki.replace(/\r/g,'').split(/\n/): wiki, // lines of wikicode
o = '', // output
p = 0, // para flag
$r; // result of passing a regexp to $()
// some shorthands
function remain() { return ll.length; }
function sh() { return ll.shift(); } // shift
function ps(s) { o += s; } // push
// similar to C's printf, uses ? as placeholders, ?? to escape question marks
function f()
{
var i=1, a=arguments, f=a[0], o='', c, p;
for (; i<a.length; i++) {
if ((p=f.indexOf('?'))+1) {
// allow character escaping
i -= c = f.charAt(p+1)=='?' ? 1 : 0;
o += f.substring(0,p) + (c ? '?' : a[i]);
f = f.substr(p+1+c);
} else { break; }
}
return o+f;
}
function html_entities(s) {
return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
}
// Wiki text parsing to html is a nightmare.
// The below functions deliberately don't escape the ampersand since this would make it more difficult,
// and we don't absolutely need to for how we need it.
// This means that any unescaped ampersands in wikitext will remain unescaped and can cause invalid HTML.
// Browsers should all be able to handle it though.
// We also escape significant wikimarkup characters to prevent further matching on the processed text
function htmlescape_text(s) {
return s.replace(/</g,"<").replace(/>/g,">").replace(/:/g,":").replace(/\[/g,"[").replace(/]/g,"]");
}
function htmlescape_attr(s) {
return htmlescape_text(s).replace(/'/g,"'").replace(/"/g,""");
}
// return the first non matching character position between two strings
function str_imatch(a, b)
{
for (var i=0, l=Math.min(a.length, b.length); i<l; i++) {
if (a.charAt(i)!=b.charAt(i)) { break; }
}
return i;
}
// compare current line against a string or regexp
// if passed a string it will compare only the first string.length characters
// if passed a regexp the result is stored in $r
function $(c) { return (typeof c == 'string') ? (ll[0].substr(0,c.length)==c) : ($r = ll[0].match(c)); }
function $$(c) { return ll[0]==c; } // compare current line against a string
function _(p) { return ll[0].charAt(p); } // return char at pos p
function endl(s) { ps(s); sh(); }
function parse_list()
{
var prev='';
while (remain() && $(/^([*#:;]+)(.*)$/)) {
var l_match = $r;
sh();
var ipos = str_imatch(prev, l_match[1]);
// close uncontinued lists
for (var prevPos=prev.length-1; prevPos >= ipos; prevPos--) {
var pi = prev.charAt(prevPos);
if (pi=='*') { ps('</ul>'); }
else if (pi=='#') { ps('</ol>'); }
// close a dl only if the new item is not a dl item (:, ; or empty)
else if($.inArray(l_match[1].charAt(prevPos), ['','*','#'])) { ps('</dl>'); }
}
// open new lists
for (var matchPos=ipos; matchPos<l_match[1].length; matchPos++) {
var li = l_match[1].charAt(matchPos);
if (li=='*') { ps('<ul>'); }
else if (li=='#') { ps('<ol>'); }
// open a new dl only if the prev item is not a dl item (:, ; or empty)
else if ($.inArray(prev.charAt(matchPos), ['','*','#'])) { ps('<dl>'); }
}
switch (l_match[1].charAt(l_match[1].length-1)) {
case '*': case '#':
ps('<li>' + parse_inline_nowiki(l_match[2]));
break;
case ';':
ps('<dt>');
var dt_match = l_match[2].match(/(.*?)(:.*?)$/);
// handle ;dt :dd format
if (dt_match) {
ps(parse_inline_nowiki(dt_match[1]));
ll.unshift(dt_match[2]);
} else ps(parse_inline_nowiki(l_match[2]));
break;
case ':':
ps('<dd>' + parse_inline_nowiki(l_match[2]));
}
prev=l_match[1];
}
// close remaining lists
for (var i=prev.length-1; i>=0; i--) {
ps(f('</?>', (prev.charAt(i)=='*')? 'ul': ((prev.charAt(i)=='#')? 'ol': 'dl')));
}
}
function parse_table()
{
endl(f('<table>', $(/^\{\|( .*)$/)? $r[1]: ''));
for (;remain();) if ($('|')) switch (_(1)) {
case '}':
endl('</table>');
return;
case '-':
endl(f('<tr>', $(/\|-*(.*)/)[1]));
break;
default:
parse_table_data();
}
else if ($('!')) { parse_table_data(); }
else { sh(); }
}
function parse_table_data()
{
var td_line, match_i;
// 1: "|+", '|' or '+'
// 2: ??
// 3: attributes ??
// TODO: finish commenting this regexp
var td_match = sh().match(/^(\|\+|\||!)((?:([^[|]*?)\|(?!\|))?(.*))$/);
if (td_match[1] == '|+') ps('<caption');
else ps('<t' + ((td_match[1]=='|')?'d':'h'));
if (typeof td_match[3] != 'undefined') {
//ps(' ' + td_match[3])
match_i = 4;
} else match_i = 2;
ps('>');
if (td_match[1] != '|+') {
// use || or !! as a cell separator depending on context
// NOTE: when split() is passed a regexp make sure to use non-capturing brackets
td_line = td_match[match_i].split((td_match[1] == '|')? '||': /(?:\|\||!!)/);
ps(parse_inline_nowiki(td_line.shift()));
while (td_line.length) ll.unshift(td_match[1] + td_line.pop());
} else ps(td_match[match_i]);
var tc = 0, td = [];
while (remain()) {
td.push(sh());
if ($('|')) {
if (!tc) break; // we're at the outer-most level (no nested tables), skip to td parse
else if (_(1)=='}') tc--;
}
else if (!tc && $('!')) break;
else if ($('{|')) tc++;
}
if (td.length) ps(Insta.convert(td));
}
function parse_pre()
{
ps('<pre>');
do {
endl(parse_inline_nowiki(ll[0].substring(1)) + "\n");
} while (remain() && $(' '));
ps('</pre>');
}
function parse_block_image()
{
ps(parse_image(sh()));
}
function parse_image(str)
{
//<NOLITE>
// get what's in between "[[Image:" and "]]"
var tag = str.substring(str.indexOf(':') + 1, str.length - 2);
/* eslint-disable no-unused-vars */
var width;
var attr = [], filename, caption = '';
var thumb=0, frame=0, center=0;
var align='';
/* eslint-enable no-unused-vars */
if (tag.match(/\|/)) {
// manage nested links
var nesting = 0;
var last_attr;
for (var i = tag.length-1; i > 0; i--) {
if (tag.charAt(i) == '|' && !nesting) {
last_attr = tag.substr(i+1);
tag = tag.substring(0, i);
break;
} else switch (tag.substr(i-1, 2)) {
case ']]':
nesting++;
i--;
break;
case '[[':
nesting--;
i--;
}
}
attr = tag.split(/\s*\|\s*/);
attr.push(last_attr);
filename = attr.shift();
var w_match;
for (;attr.length; attr.shift()) {
w_match = attr[0].match(/^(\d*)(?:[px]*\d*)?px$/);
if (w_match) width = w_match[1];
else switch(attr[0]) {
case 'thumb':
case 'thumbnail':
thumb=true;
frame=true;
break;
case 'frame':
frame=true;
break;
case 'none':
case 'right':
case 'left':
center=false;
align=attr[0];
break;
case 'center':
center=true;
align='none';
break;
default:
if (attr.length == 1) caption = attr[0];
}
}
} else filename = tag;
return '';
//</NOLITE>
}
function parse_inline_nowiki(str)
{
var start, lastend=0;
var substart=0, nestlev=0, open, close, subloop;
var html='';
while (-1 != (start = str.indexOf('<nowiki>', substart))) {
html += parse_inline_wiki(str.substring(lastend, start));
start += 8;
substart = start;
subloop = true;
do {
open = str.indexOf('<nowiki>', substart);
close = str.indexOf('</nowiki>', substart);
if (close<=open || open==-1) {
if (close==-1) {
return html + html_entities(str.substr(start));
}
substart = close+9;
if (nestlev) {
nestlev--;
} else {
lastend = substart;
html += html_entities(str.substring(start, lastend-9));
subloop = false;
}
} else {
substart = open+8;
nestlev++;
}
} while (subloop);
}
return html + parse_inline_wiki(str.substr(lastend));
}
function parse_inline_images(str)
{
//<NOLITE>
var start, substart=0, nestlev=0;
var loop, close, open, wiki, html;
while (-1 != (start=str.indexOf('[[', substart))) {
if(str.substr(start+2).match(RegExp('^(Image|File|' + Insta.conf.locale.image + '):','i'))) {
loop=true;
substart=start;
do {
substart+=2;
close=str.indexOf(']]',substart);
open=str.indexOf('[[',substart);
if (close<=open||open==-1) {
if (close==-1) return str;
substart=close;
if (nestlev) {
nestlev--;
} else {
wiki=str.substring(start,close+2);
html=parse_image(wiki);
str=str.replace(wiki,html);
substart=start+html.length;
loop=false;
}
} else {
substart=open;
nestlev++;
}
} while (loop);
} else break;
}
//</NOLITE>
return str;
}
// the output of this function doesn't respect the FILO structure of HTML
// but since most browsers can handle it I'll save myself the hassle
function parse_inline_formatting(str)
{
var em,st,i,li,o='';
while ((i=str.indexOf("''",li))+1) {
o += str.substring(li,i);
li=i+2;
if (str.charAt(i+2)=="'") {
li++;
st=!st;
o+=st?'<strong>':'</strong>';
} else {
em=!em;
o+=em?'<em>':'</em>';
}
}
return o+str.substr(li);
}
function parse_inline_wiki(str)
{
str = parse_inline_images(str);
str = parse_inline_formatting(str);
// math
str = str.replace(/<(?:)math>(.*?)<\/math>/ig, '');
// Build a Mediawiki-formatted date string
var date = new Date();
var minutes = date.getUTCMinutes();
if (minutes < 10) minutes = '0' + minutes;
date = f("?:?, ? ? ? (UTC)", date.getUTCHours(), minutes, date.getUTCDate(), Insta.conf.locale.months[date.getUTCMonth()], date.getUTCFullYear());
// text formatting
return str.
// signatures
replace(/~{5}(?!~)/g, date).
replace(/~{4}(?!~)/g, Insta.conf.user.name+' '+date).
replace(/~{3}(?!~)/g, Insta.conf.user.name).
// [[:Category:...]], [[:Image:...]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):[^|]*?)\\]\\](\\w*)','gi'), function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2));}).
// remove straight category and interwiki tags
replace(RegExp('\\[\\[(?:'+Insta.conf.locale.category+'|'+Insta.conf.wiki.interwiki+'):.*?\\]\\]','gi'),'').
// [[:Category:...|Links]], [[:Image:...|Links]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):.*?)\\|([^\\]]+?)\\]\\](\\w*)','gi'), function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3));}).
// [[/Relative links]]
replace(/\[\[(\/[^|]*?)\]\]/g, function($0,$1){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($1)); }).
// [[/Replaced|Relative links]]
replace(/\[\[(\/.*?)\|(.+?)\]\]/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($2)); }).
// [[Common links]]
replace(/\[\[([^[|]*?)\]\](\w*)/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2)); }).
// [[Replaced|Links]]
replace(/\[\[([^[]*?)\|([^\]]+?)\]\](\w*)/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3)); }).
// [[Stripped:Namespace|Namespace]]
replace(/\[\[([^\]]*?:)?(.*?)( *\(.*?\))?\|\]\]/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1) + htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($2)); }).
// External links
replace(/\[(https?|news|ftp|mailto|gopher|irc):(\/*)([^\]]*?) (.*?)\]/g, function($0,$1,$2,$3,$4){return f("<a class='external' href='?:?'>?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($4)); }).
replace(/\[http:\/\/(.*?)\]/g, function($0,$1){return f("<a class='external' href='http://?'>[#]</a>", htmlescape_attr($1)); }).
replace(/\[(news|ftp|mailto|gopher|irc):(\/*)(.*?)\]/g, function($0,$1,$2,$3){return f("<a class='external' href='?:?'>?:?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($1), htmlescape_text($2) + htmlescape_text($3)); }).
replace(/(^| )(https?|news|ftp|mailto|gopher|irc):(\/*)([^ $]*[^.,!?;: $])/g, function($0,$1,$2,$3,$4){return f("?<a class='external' href='?:?'>?:?</a>", htmlescape_text($1), htmlescape_attr($2), htmlescape_attr($3) + htmlescape_attr($4), htmlescape_text($2), htmlescape_text($3) + htmlescape_text($4)); }).
replace('__NOTOC__','').
replace('__NOEDITSECTION__','');
}
// begin parsing
for (;remain();) if ($(/^(={1,6})(.*)\1(.*)$/)) {
p=0;
endl(f('<h?>?</h?>?', $r[1].length, parse_inline_nowiki($r[2]), $r[1].length, $r[3]));
} else if ($(/^[*#:;]/)) {
p=0;
parse_list();
} else if ($(' ')) {
p=0;
parse_pre();
} else if ($('{|')) {
p=0;
parse_table();
} else if ($(/^----+$/)) {
p=0;
endl('<hr />');
} else if ($(Insta.BLOCK_IMAGE)) {
p=0;
parse_block_image();
} else {
// handle paragraphs
if ($$('')) {
p = (remain()>1 && ll[1]===(''));
if (p) endl('<p><br>');
} else {
if(!p) {
ps('<p>');
p=1;
}
ps(parse_inline_nowiki(ll[0]) + ' ');
}
sh();
}
return o;
};
function wiki2html(txt,baseurl) {
Insta.conf.baseUrl=baseurl;
return Insta.convert(txt);
}
// ENDFILE: livepreview.js
// STARTFILE: pageinfo.js
//<NOLITE>
function popupFilterPageSize(data) {
return formatBytes(data.length);
}
function popupFilterCountLinks(data) {
var num=countLinks(data);
return String(num) + ' ' + ((num!=1)?popupString('wikiLinks'):popupString('wikiLink'));
}
function popupFilterCountImages(data) {
var num=countImages(data);
return String(num) + ' ' + ((num!=1)?popupString('images'):popupString('image'));
}
function popupFilterCountCategories(data) {
var num=countCategories(data);
return String(num) + ' ' + ((num!=1)?popupString('categories'):popupString('category'));
}
function popupFilterLastModified(data,download) {
var lastmod=download.lastModified;
var now=new Date();
var age=now-lastmod;
if (lastmod && getValueOf('popupLastModified')) {
return (tprintf('%s old', [formatAge(age)])).replace(RegExp(' ','g'), ' ');
}
return '';
}
function formatAge(age) {
// coerce into a number
var a=0+age, aa=a;
var seclen = 1000;
var minlen = 60*seclen;
var hourlen = 60*minlen;
var daylen = 24*hourlen;
var weeklen = 7*daylen;
var numweeks = (a-a%weeklen)/weeklen; a = a-numweeks*weeklen; var sweeks = addunit(numweeks, 'week');
var numdays = (a-a%daylen)/daylen; a = a-numdays*daylen; var sdays = addunit(numdays, 'day');
var numhours = (a-a%hourlen)/hourlen; a = a-numhours*hourlen; var shours = addunit(numhours,'hour');
var nummins = (a-a%minlen)/minlen; a = a-nummins*minlen; var smins = addunit(nummins, 'minute');
var numsecs = (a-a%seclen)/seclen; a = a-numsecs*seclen; var ssecs = addunit(numsecs, 'second');
if (aa > 4*weeklen) { return sweeks; }
if (aa > weeklen) { return sweeks + ' ' + sdays; }
if (aa > daylen) { return sdays + ' ' + shours; }
if (aa > 6*hourlen) { return shours; }
if (aa > hourlen) { return shours + ' ' + smins; }
if (aa > 10*minlen) { return smins; }
if (aa > minlen) { return smins + ' ' + ssecs; }
return ssecs;
}
function addunit(num,str) { return '' + num + ' ' + ((num!=1) ? popupString(str+'s') : popupString(str)) ;}
function runPopupFilters(list, data, download) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (list[i] && typeof list[i] == 'function') {
var s=list[i](data, download, download.owner.article);
if (s) { ret.push(s); }
}
}
return ret;
}
function getPageInfo(data, download) {
if (!data || data.length === 0) { return popupString('Empty page'); }
var popupFilters=getValueOf('popupFilters') || [];
var extraPopupFilters = getValueOf('extraPopupFilters') || [];
var pageInfoArray = runPopupFilters(popupFilters.concat(extraPopupFilters), data, download);
var pageInfo=pageInfoArray.join(', ');
if (pageInfo !== '' ) { pageInfo = upcaseFirst(pageInfo); }
return pageInfo;
}
// this could be improved!
function countLinks(wikiText) { return wikiText.split('[[').length - 1; }
// if N = # matches, n = # brackets, then
// String.parenSplit(regex) intersperses the N+1 split elements
// with Nn other elements. So total length is
// L= N+1 + Nn = N(n+1)+1. So N=(L-1)/(n+1).
function countImages(wikiText) {
return (wikiText.parenSplit(pg.re.image).length - 1) / (pg.re.imageBracketCount + 1);
}
function countCategories(wikiText) {
return (wikiText.parenSplit(pg.re.category).length - 1) / (pg.re.categoryBracketCount + 1);
}
function popupFilterStubDetect(data, download, article) {
var counts=stubCount(data, article);
if (counts.real) { return popupString('stub'); }
if (counts.sect) { return popupString('section stub'); }
return '';
}
function popupFilterDisambigDetect(data, download, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return ''; }
return (isDisambig(data, article)) ? popupString('disambig') : '';
}
function formatBytes(num) {
return (num > 949) ? (Math.round(num/100)/10+popupString('kB')) : (num +' ' + popupString('bytes')) ;
}
//</NOLITE>
// ENDFILE: pageinfo.js
// STARTFILE: titles.js
/**
@fileoverview Defines the {@link Title} class, and associated crufty functions.
<code>Title</code> deals with article titles and their various
forms. {@link Stringwrapper} is the parent class of
<code>Title</code>, which exists simply to make things a little
neater.
*/
/**
Creates a new Stringwrapper.
@constructor
@class the Stringwrapper class. This base class is not really
useful on its own; it just wraps various common string operations.
*/
function Stringwrapper() {
/**
Wrapper for this.toString().indexOf()
@param {String} x
@type integer
*/
this.indexOf=function(x){return this.toString().indexOf(x);};
/**
Returns this.value.
@type String
*/
this.toString=function(){return this.value;};
/**
Wrapper for {@link String#parenSplit} applied to this.toString()
@param {RegExp} x
@type Array
*/
this.parenSplit=function(x){return this.toString().parenSplit(x);};
/**
Wrapper for this.toString().substring()
@param {String} x
@param {String} y (optional)
@type String
*/
this.substring=function(x,y){
if (typeof y=='undefined') { return this.toString().substring(x); }
return this.toString().substring(x,y);
};
/**
Wrapper for this.toString().split()
@param {String} x
@type Array
*/
this.split=function(x){return this.toString().split(x);};
/**
Wrapper for this.toString().replace()
@param {String} x
@param {String} y
@type String
*/
this.replace=function(x,y){ return this.toString().replace(x,y); };
}
/**
Creates a new <code>Title</code>.
@constructor
@class The Title class. Holds article titles and converts them into
various forms. Also deals with anchors, by which we mean the bits
of the article URL after a # character, representing locations
within an article.
@param {String} value The initial value to assign to the
article. This must be the canonical title (see {@link
Title#value}. Omit this in the constructor and use another function
to set the title if this is unavailable.
*/
function Title(val) {
/**
The canonical article title. This must be in UTF-8 with no
entities, escaping or nasties. Also, underscores should be
replaced with spaces.
@type String
@private
*/
this.value=null;
/**
The canonical form of the anchor. This should be exactly as
it appears in the URL, i.e. with the .C3.0A bits in.
@type String
*/
this.anchor='';
this.setUtf(val);
}
Title.prototype=new Stringwrapper();
/**
Returns the canonical representation of the article title, optionally without anchor.
@param {boolean} omitAnchor
@fixme Decide specs for anchor
@return String The article title and the anchor.
*/
Title.prototype.toString=function(omitAnchor) {
return this.value + ( (!omitAnchor && this.anchor) ? '#' + this.anchorString() : '' );
};
Title.prototype.anchorString=function() {
if (!this.anchor) { return ''; }
var split=this.anchor.parenSplit(/((?:[.][0-9A-F]{2})+)/);
var len=split.length;
for (var j=1; j<len; j+=2) {
// FIXME s/decodeURI/decodeURIComponent/g ?
split[j]=decodeURIComponent(split[j].split('.').join('%')).split('_').join(' ');
}
return split.join('');
};
Title.prototype.urlAnchor=function() {
var split=this.anchor.parenSplit('/((?:[%][0-9A-F]{2})+)/');
var len=split.length;
for (var j=1; j<len; j+=2) {
split[j]=split[j].split('%').join('.');
}
return split.join('');
};
Title.prototype.anchorFromUtf=function(str) {
this.anchor=encodeURIComponent(str.split(' ').join('_'))
.split('%3A').join(':').split("'").join('%27').split('%').join('.');
};
Title.fromURL=function(h) {
return new Title().fromURL(h);
};
Title.prototype.fromURL=function(h) {
if (typeof h != 'string') {
this.value=null;
return this;
}
// NOTE : playing with decodeURI, encodeURI, escape, unescape,
// we seem to be able to replicate the IE borked encoding
// IE doesn't do this new-fangled utf-8 thing.
// and it's worse than that.
// IE seems to treat the query string differently to the rest of the url
// the query is treated as bona-fide utf8, but the first bit of the url is pissed around with
// we fix up & for all browsers, just in case.
var splitted=h.split('?');
splitted[0]=splitted[0].split('&').join('%26');
h=splitted.join('?');
var contribs=pg.re.contribs.exec(h);
if (contribs) {
if (contribs[1]=='title=') { contribs[3]=contribs[3].split('+').join(' '); }
var u=new Title(contribs[3]);
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + u.stripNamespace()));
return this;
}
var email=pg.re.email.exec(h);
if (email) {
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(email[3]).stripNamespace()));
return this;
}
var backlinks=pg.re.backlinks.exec(h);
if (backlinks) {
this.setUtf(this.decodeNasties(new Title(backlinks[3])));
return this;
}
//A dummy title object for a Special:Diff link.
var specialdiff=pg.re.specialdiff.exec(h);
if (specialdiff) {
this.setUtf(this.decodeNasties(new Title(mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Diff')));
return this;
}
// no more special cases to check --
// hopefully it's not a disguised user-related or specially treated special page
var m=pg.re.main.exec(h);
if(m === null) { this.value=null; }
else {
var fromBotInterface = /[?](.+[&])?title=/.test(h);
if (fromBotInterface) {
m[2]=m[2].split('+').join('_');
}
var extracted = m[2] + (m[3] ? '#' + m[3] : '');
if (pg.flag.isSafari && /%25[0-9A-Fa-f]{2}/.test(extracted)) {
// Fix Safari issue
// Safari sometimes encodes % as %25 in UTF-8 encoded strings like %E5%A3 -> %25E5%25A3.
this.setUtf(decodeURIComponent(unescape(extracted)));
} else {
this.setUtf(this.decodeNasties(extracted));
}
}
return this;
};
Title.prototype.decodeNasties=function(txt) {
var ret= this.decodeEscapes(decodeURI(txt));
ret = ret.replace(/[_ ]*$/, '');
return ret;
};
Title.prototype.decodeEscapes=function(txt) {
var split=txt.parenSplit(/((?:[%][0-9A-Fa-f]{2})+)/);
var len=split.length;
for (var i=1; i<len; i=i+2) {
// FIXME is decodeURIComponent better?
split[i]=unescape(split[i]);
}
return split.join('');
};
Title.fromAnchor=function(a) {
return new Title().fromAnchor(a);
};
Title.prototype.fromAnchor=function(a) {
if (!a) { this.value=null; return this; }
return this.fromURL(a.href);
};
Title.fromWikiText=function(txt) {
return new Title().fromWikiText(txt);
};
Title.prototype.fromWikiText=function(txt) {
// FIXME - testing needed
txt=myDecodeURI(txt);
this.setUtf(txt);
return this;
};
Title.prototype.hintValue=function(){
if(!this.value) { return ''; }
return safeDecodeURI(this.value);
};
//<NOLITE>
Title.prototype.toUserName=function(withNs) {
if (this.namespaceId() != pg.nsUserId && this.namespaceId() != pg.nsUsertalkId) {
this.value=null;
return;
}
this.value = (withNs ? mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' : '') + this.stripNamespace().split('/')[0];
};
Title.prototype.userName=function(withNs) {
var t=(new Title(this.value));
t.toUserName(withNs);
if (t.value) { return t; }
return null;
};
Title.prototype.toTalkPage=function() {
// convert article to a talk page, or if we can't, return null
// In other words: return null if this ALREADY IS a talk page
// and return the corresponding talk page otherwise
//
// Per https://www.mediawiki.org/wiki/Manual:Namespace#Subject_and_talk_namespaces
// * All discussion namespaces have odd-integer indices
// * The discussion namespace index for a specific namespace with index n is n + 1
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId>=0 && namespaceId % 2 === 0) //non-special and subject namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId+1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
//</NOLITE>
// Return canonical, localized namespace
Title.prototype.namespace=function() {
return mw.config.get('wgFormattedNamespaces')[this.namespaceId()];
};
Title.prototype.namespaceId=function() {
var n=this.value.indexOf(':');
if (n<0) { return 0; } //mainspace
var namespaceId = mw.config.get('wgNamespaceIds')[this.value.substring(0,n).split(' ').join('_').toLowerCase()];
if (typeof namespaceId=='undefined') return 0; //mainspace
return namespaceId;
};
//<NOLITE>
Title.prototype.talkPage=function() {
var t=new Title(this.value);
t.toTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.isTalkPage=function() {
if (this.talkPage()===null) { return true; }
return false;
};
Title.prototype.toArticleFromTalkPage=function() {
//largely copy/paste from toTalkPage above.
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId >= 0 && namespaceId % 2 == 1) //non-special and talk namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId-1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
Title.prototype.articleFromTalkPage=function() {
var t=new Title(this.value);
t.toArticleFromTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.articleFromTalkOrArticle=function() {
var t=new Title(this.value);
if ( t.toArticleFromTalkPage() ) { return t; }
return this;
};
Title.prototype.isIpUser=function() {
return pg.re.ipUser.test(this.userName());
};
//</NOLITE>
Title.prototype.stripNamespace=function(){ // returns a string, not a Title
var n=this.value.indexOf(':');
if (n<0) { return this.value; }
var namespaceId = this.namespaceId();
if (namespaceId === pg.nsMainspaceId) return this.value;
return this.value.substring(n+1);
};
Title.prototype.setUtf=function(value){
if (!value) { this.value=''; return; }
var anch=value.indexOf('#');
if(anch < 0) { this.value=value.split('_').join(' '); this.anchor=''; return; }
this.value=value.substring(0,anch).split('_').join(' ');
this.anchor=value.substring(anch+1);
this.ns=null; // wait until namespace() is called
};
Title.prototype.setUrl=function(urlfrag) {
var anch=urlfrag.indexOf('#');
this.value=safeDecodeURI(urlfrag.substring(0,anch));
this.anchor=this.value.substring(anch+1);
};
Title.prototype.append=function(x){
this.setUtf(this.value + x);
};
Title.prototype.urlString=function(x) {
if(!x) { x={}; }
var v=this.toString(true);
if (!x.omitAnchor && this.anchor) { v+= '#' + this.urlAnchor(); }
if (!x.keepSpaces) { v=v.split(' ').join('_'); }
return encodeURI(v).split('&').join('%26').split('?').join('%3F').split('+').join('%2B');
};
Title.prototype.removeAnchor=function() {
return new Title(this.toString(true));
};
Title.prototype.toUrl=function() {
return pg.wiki.titlebase + this.urlString();
};
function parseParams(url) {
var specialDiff = pg.re.specialdiff.exec(url);
if (specialDiff)
{
var split= specialDiff[1].split('/');
if (split.length==1) return {oldid:split[0], diff: 'prev'};
else if (split.length==2) return {oldid: split[0], diff: split[1]};
}
var ret={};
if (url.indexOf('?')==-1) { return ret; }
url = url.split('#')[0];
var s=url.split('?').slice(1).join();
var t=s.split('&');
for (var i=0; i<t.length; ++i) {
var z=t[i].split('=');
z.push(null);
ret[z[0]]=z[1];
}
//Diff revision with no oldid is interpreted as a diff to the previous revision by MediaWiki
if (ret.diff && typeof(ret.oldid)==='undefined')
{
ret.oldid = "prev";
}
//Documentation seems to say something different, but oldid can also accept prev/next, and Echo is emitting such URLs. Simple fixup during parameter decoding:
if (ret.oldid && (ret.oldid==='prev' || ret.oldid==='next' || ret.oldid==='cur'))
{
var helper = ret.diff;
ret.diff = ret.oldid;
ret.oldid = helper;
}
return ret;
}
// (a) myDecodeURI (first standard decodeURI, then pg.re.urlNoPopup)
// (b) change spaces to underscores
// (c) encodeURI (just the straight one, no pg.re.urlNoPopup)
function myDecodeURI (str) {
var ret;
// FIXME decodeURIComponent??
try { ret=decodeURI(str.toString()); }
catch (summat) { return str; }
for (var i=0; i<pg.misc.decodeExtras.length; ++i) {
var from=pg.misc.decodeExtras[i].from;
var to=pg.misc.decodeExtras[i].to;
ret=ret.split(from).join(to);
}
return ret;
}
function safeDecodeURI(str) { var ret=myDecodeURI(str); return ret || str; }
///////////
// TESTS //
///////////
//<NOLITE>
function isDisambig(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
return ! article.isTalkPage() && pg.re.disambig.test(data);
}
function stubCount(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
var sectStub=0;
var realStub=0;
if (pg.re.stub.test(data)) {
var s=data.parenSplit(pg.re.stub);
for (var i=1; i<s.length; i=i+2) {
if (s[i]) { ++sectStub; }
else { ++realStub; }
}
}
return { real: realStub, sect: sectStub };
}
function isValidImageName(str){ // extend as needed...
return ( str.indexOf('{') == -1 );
}
function isInStrippableNamespace(article) {
// Does the namespace allow subpages
// Note, would be better if we had access to wgNamespacesWithSubpages
return ( article.namespaceId() !== 0 );
}
function isInMainNamespace(article) { return article.namespaceId() === 0; }
function anchorContainsImage(a) {
// iterate over children of anchor a
// see if any are images
if (a === null) { return false; }
var kids=a.childNodes;
for (var i=0; i<kids.length; ++i) { if (kids[i].nodeName=='IMG') { return true; } }
return false;
}
//</NOLITE>
function isPopupLink(a) {
// NB for performance reasons, TOC links generally return true
// they should be stripped out later
if (!markNopopupSpanLinks.done) { markNopopupSpanLinks(); }
if (a.inNopopupSpan) { return false; }
// FIXME is this faster inline?
if (a.onmousedown || a.getAttribute('nopopup')) { return false; }
var h=a.href;
if (h === document.location.href+'#') { return false; }
if (!pg.re.basenames.test(h)) { return false; }
if (!pg.re.urlNoPopup.test(h)) { return true; }
return (
(pg.re.email.test(h) || pg.re.contribs.test(h) || pg.re.backlinks.test(h) || pg.re.specialdiff.test(h)) &&
h.indexOf('&limit=') == -1 );
}
function markNopopupSpanLinks() {
if( !getValueOf('popupOnlyArticleLinks'))
fixVectorMenuPopups();
var s = $('.nopopups').toArray();
for (var i=0; i<s.length; ++i) {
var as=s[i].getElementsByTagName('a');
for (var j=0; j<as.length; ++j) {
as[j].inNopopupSpan=true;
}
}
markNopopupSpanLinks.done=true;
}
function fixVectorMenuPopups() {
$('div.vectorMenu h3:first a:first').prop('inNopopupSpan', true);
}
// ENDFILE: titles.js
// STARTFILE: getpage.js
//////////////////////////////////////////////////
// Wiki-specific downloading
//
// Schematic for a getWiki call
//
// getPageWithCaching
// |
// false | true
// getPage<-[findPictureInCache]->-onComplete(a fake download)
// \.
// (async)->addPageToCache(download)->-onComplete(download)
// check cache to see if page exists
function getPageWithCaching(url, onComplete, owner) {
log('getPageWithCaching, url='+url);
var i=findInPageCache(url);
var d;
if (i > -1) {
d=fakeDownload(url, owner.idNumber, onComplete,
pg.cache.pages[i].data, pg.cache.pages[i].lastModified,
owner);
} else {
d=getPage(url, onComplete, owner);
if (d && owner && owner.addDownload) {
owner.addDownload(d);
d.owner=owner;
}
}
}
function getPage(url, onComplete, owner) {
log('getPage');
var callback= function (d) { if (!d.aborted) {addPageToCache(d); onComplete(d);} };
return startDownload(url, owner.idNumber, callback);
}
function findInPageCache(url) {
for (var i=0; i<pg.cache.pages.length; ++i) {
if (url==pg.cache.pages[i].url) { return i; }
}
return -1;
}
function addPageToCache(download) {
log('addPageToCache '+download.url);
var page = {url: download.url, data: download.data, lastModified: download.lastModified};
return pg.cache.pages.push(page);
}
// ENDFILE: getpage.js
// STARTFILE: parensplit.js
//////////////////////////////////////////////////
// parenSplit
// String.prototype.parenSplit should do what ECMAscript says String.prototype.split does,
// interspersing paren matches (regex capturing groups) between the split elements.
// i.e. 'abc'.split(/(b)/)) should return ['a','b','c'], not ['a','c']
if (String('abc'.split(/(b)/))!='a,b,c') {
// broken String.split, e.g. konq, IE < 10
String.prototype.parenSplit=function (re) {
re=nonGlobalRegex(re);
var s=this;
var m=re.exec(s);
var ret=[];
while (m && s) {
// without the following loop, we have
// 'ab'.parenSplit(/a|(b)/) != 'ab'.split(/a|(b)/)
for(var i=0; i<m.length; ++i) {
if (typeof m[i]=='undefined') m[i]='';
}
ret.push(s.substring(0,m.index));
ret = ret.concat(m.slice(1));
s=s.substring(m.index + m[0].length);
m=re.exec(s);
}
ret.push(s);
return ret;
};
} else {
String.prototype.parenSplit=function (re) { return this.split(re); };
String.prototype.parenSplit.isNative=true;
}
function nonGlobalRegex(re) {
var s=re.toString();
var flags='';
for (var j=s.length; s.charAt(j) != '/'; --j) {
if (s.charAt(j) != 'g') { flags += s.charAt(j); }
}
var t=s.substring(1,j);
return RegExp(t,flags);
}
// ENDFILE: parensplit.js
// STARTFILE: tools.js
// IE madness with encoding
// ========================
//
// suppose throughout that the page is in utf8, like wikipedia
//
// if a is an anchor DOM element and a.href should consist of
//
// http://host.name.here/wiki/foo?bar=baz
//
// then IE gives foo as "latin1-encoded" utf8; we have foo = decode_utf8(decodeURI(foo_ie))
// but IE gives bar=baz correctly as plain utf8
//
// ---------------------------------
//
// IE's xmlhttp doesn't understand utf8 urls. Have to use encodeURI here.
//
// ---------------------------------
//
// summat else
// Source: http://aktuell.de.selfhtml.org/artikel/javascript/utf8b64/utf8.htm
//<NOLITE>
function getJsObj(json) {
try {
var json_ret = JSON.parse(json);
if( json_ret.warnings ) {
for( var w=0; w < json_ret.warnings.length; w++ ) {
if( json_ret.warnings[w]['*'] ) {
log( json_ret.warnings[w]['*'] );
} else {
log( json_ret.warnings[w]['warnings'] );
}
}
} else if ( json_ret.error ) {
errlog( json_ret.error.code + ': ' + json_ret.error.info );
}
return json_ret;
} catch (someError) {
errlog('Something went wrong with getJsObj, json='+json);
return 1;
}
}
function anyChild(obj) {
for (var p in obj) {
return obj[p];
}
return null;
}
//</NOLITE>
function upcaseFirst(str) {
if (typeof str != typeof '' || str === '') return '';
return str.charAt(0).toUpperCase() + str.substring(1);
}
function findInArray(arr, foo) {
if (!arr || !arr.length) { return -1; }
var len=arr.length;
for (var i=0; i<len; ++i) { if (arr[i]==foo) { return i; } }
return -1;
}
/* eslint-disable no-unused-vars */
function nextOne (array, value) {
// NB if the array has two consecutive entries equal
// then this will loop on successive calls
var i=findInArray(array, value);
if (i<0) { return null; }
return array[i+1];
}
/* eslint-enable no-unused-vars */
function literalizeRegex(str){
return mw.util.escapeRegExp(str);
}
String.prototype.entify=function() {
//var shy='­';
return this.split('&').join('&').split('<').join('<').split('>').join('>'/*+shy*/).split('"').join('"');
};
// Array filter function
function removeNulls(val) { return val !== null; }
function joinPath(list) {
return list.filter(removeNulls).join('/');
}
function simplePrintf(str, subs) {
if (!str || !subs) { return str; }
var ret=[];
var s=str.parenSplit(/(%s|\$[0-9]+)/);
var i=0;
do {
ret.push(s.shift());
if ( !s.length ) { break; }
var cmd=s.shift();
if (cmd == '%s') {
if ( i < subs.length ) { ret.push(subs[i]); } else { ret.push(cmd); }
++i;
} else {
var j=parseInt( cmd.replace('$', ''), 10 ) - 1;
if ( j > -1 && j < subs.length ) { ret.push(subs[j]); } else { ret.push(cmd); }
}
} while (s.length > 0);
return ret.join('');
}
/* eslint-disable no-unused-vars */
function isString(x) { return (typeof x === 'string' || x instanceof String); }
function isNumber(x) { return (typeof x === 'number' || x instanceof Number); }
function isRegExp(x) { return x instanceof RegExp; }
function isArray (x) { return x instanceof Array; }
function isObject(x) { return x instanceof Object; }
function isFunction(x) {
return !isRegExp(x) && (typeof x === 'function' || x instanceof Function);
}
/* eslint-enable no-unused-vars */
function repeatString(s,mult) {
var ret='';
for (var i=0; i<mult; ++i) { ret += s; }
return ret;
}
function zeroFill(s, min) {
min = min || 2;
var t=s.toString();
return repeatString('0', min - t.length) + t;
}
function map(f, o) {
if (isArray(o)) { return map_array(f,o); }
return map_object(f,o);
}
function map_array(f,o) {
var ret=[];
for (var i=0; i<o.length; ++i) {
ret.push(f(o[i]));
}
return ret;
}
function map_object(f,o) {
var ret={};
for (var i in o) { ret[o]=f(o[i]); }
return ret;
}
pg.escapeQuotesHTML = function ( text ) {
return text
.replace(/&/g, "&")
.replace(/"/g, """)
.replace(/</g, "<")
.replace(/>/g, ">");
};
// ENDFILE: tools.js
// STARTFILE: dab.js
//<NOLITE>
//////////////////////////////////////////////////
// Dab-fixing code
//
function retargetDab(newTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) {
log('retargetDab: newTarget='+newTarget + ' oldTarget=' + oldTarget);
return changeLinkTargetLink(
{newTarget: newTarget,
text: newTarget.split(' ').join(' '),
hint: tprintf('disambigHint', [newTarget]),
summary: simplePrintf(
getValueOf('popupFixDabsSummary'), [friendlyCurrentArticleName, newTarget ]),
clickButton: getValueOf('popupDabsAutoClick'), minor: true, oldTarget: oldTarget,
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit});
}
function listLinks(wikitext, oldTarget, titleToEdit) {
// mediawiki strips trailing spaces, so we do the same
// testcase: https://en.wikipedia.org/w/index.php?title=Radial&oldid=97365633
var reg=RegExp('\\[\\[([^|]*?) *(\\||\\]\\])', 'gi');
var ret=[];
var splitted=wikitext.parenSplit(reg);
// ^[a-z]+ should match interwiki links, hopefully (case-insensitive)
// and ^[a-z]* should match those and [[:Category...]] style links too
var omitRegex=RegExp('^[a-z]*:|^[Ss]pecial:|^[Ii]mage|^[Cc]ategory');
var friendlyCurrentArticleName= oldTarget.toString();
var wikPos = getValueOf('popupDabWiktionary');
for (var i=1; i<splitted.length; i=i+3) {
if (typeof splitted[i] == typeof 'string' && splitted[i].length>0 && !omitRegex.test(splitted[i])) {
ret.push( retargetDab(splitted[i], oldTarget, friendlyCurrentArticleName, titleToEdit) );
} /* if */
} /* for loop */
ret = rmDupesFromSortedList(ret.sort());
if (wikPos) {
var wikTarget='wiktionary:' +
friendlyCurrentArticleName.replace( RegExp('^(.+)\\s+[(][^)]+[)]\\s*$'), '$1' );
var meth;
if (wikPos.toLowerCase() == 'first') { meth = 'unshift'; }
else { meth = 'push'; }
ret[meth]( retargetDab(wikTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) );
}
ret.push(changeLinkTargetLink(
{ newTarget: null,
text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this disambig page from this article"),
clickButton: getValueOf('popupDabsAutoClick'), oldTarget: oldTarget,
summary: simplePrintf(getValueOf('popupRmDabLinkSummary'), [friendlyCurrentArticleName]),
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit
}));
return ret;
}
function rmDupesFromSortedList(list) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (ret.length === 0 || list[i]!=ret[ret.length-1]) { ret.push(list[i]); }
}
return ret;
}
function makeFixDab(data, navpop) {
// grab title from parent popup if there is one; default exists in changeLinkTargetLink
var titleToEdit=(navpop.parentPopup && navpop.parentPopup.article.toString());
var list=listLinks(data, navpop.originalArticle, titleToEdit);
if (list.length === 0) { log('listLinks returned empty list'); return null; }
var html='<hr />' + popupString('Click to disambiguate this link to:') + '<br>';
html+=list.join(', ');
return html;
}
function makeFixDabs(wikiText, navpop) {
if (getValueOf('popupFixDabs') && isDisambig(wikiText, navpop.article) &&
Title.fromURL(location.href).namespaceId() != pg.nsSpecialId &&
navpop.article.talkPage() ) {
setPopupHTML(makeFixDab(wikiText, navpop), 'popupFixDab', navpop.idNumber);
}
}
function popupRedlinkHTML(article) {
return changeLinkTargetLink(
{ newTarget: null, text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this page from this article"),
clickButton: getValueOf('popupRedlinkAutoClick'),
oldTarget: article.toString(),
summary: simplePrintf(getValueOf('popupRedlinkSummary'), [article.toString()])});
}
//</NOLITE>
// ENDFILE: dab.js
// STARTFILE: htmloutput.js
// this has to use a timer loop as we don't know if the DOM element exists when we want to set the text
function setPopupHTML (str, elementId, popupId, onSuccess, append) {
if (typeof popupId === 'undefined') {
//console.error('popupId is not defined in setPopupHTML, html='+str.substring(0,100));
popupId = pg.idNumber;
}
var popupElement=document.getElementById(elementId+popupId);
if (popupElement) {
if (!append) { popupElement.innerHTML=''; }
if (isString(str)) {
popupElement.innerHTML+=str;
} else {
popupElement.appendChild(str);
}
if (onSuccess) { onSuccess(); }
setTimeout(checkPopupPosition, 100);
return true;
} else {
// call this function again in a little while...
setTimeout(function(){
setPopupHTML(str,elementId,popupId,onSuccess);
}, 600);
}
return null;
}
//<NOLITE>
function setPopupTrailer(str,id) {return setPopupHTML(str, 'popupData', id);}
//</NOLITE>
// args.navpopup is mandatory
// optional: args.redir, args.redirTarget
// FIXME: ye gods, this is ugly stuff
function fillEmptySpans(args) {
// if redir is present and true then redirTarget is mandatory
var redir=true;
var rcid;
if (typeof args != 'object' || typeof args.redir == 'undefined' || !args.redir) { redir=false; }
var a=args.navpopup.parentAnchor;
var article, hint=null, oldid=null, params={};
if (redir && typeof args.redirTarget == typeof {}) {
article=args.redirTarget;
//hint=article.hintValue();
} else {
article=(new Title()).fromAnchor(a);
hint=a.originalTitle || article.hintValue();
params=parseParams(a.href);
oldid=(getValueOf('popupHistoricalLinks')) ? params.oldid : null;
rcid=params.rcid;
}
var x={ article:article, hint: hint, oldid: oldid, rcid: rcid, navpop:args.navpopup, params:params };
var structure=pg.structures[getValueOf('popupStructure')];
if (typeof structure != 'object') {
setPopupHTML('popupError', 'Unknown structure (this should never happen): '+
pg.option.popupStructure, args.navpopup.idNumber);
return;
}
var spans=flatten(pg.misc.layout);
var numspans = spans.length;
var redirs=pg.misc.redirSpans;
for (var i=0; i<numspans; ++i) {
var found = redirs && (redirs.indexOf( spans[i] ) !== -1);
//log('redir='+redir+', found='+found+', spans[i]='+spans[i]);
if ( (found && !redir) || (!found && redir) ) {
//log('skipping this set of the loop');
continue;
}
var structurefn=structure[spans[i]];
var setfn = setPopupHTML;
if (getValueOf('popupActiveNavlinks') &&
(spans[i].indexOf('popupTopLinks')===0 || spans[i].indexOf('popupRedirTopLinks')===0)
) {
setfn = setPopupTipsAndHTML;
}
switch (typeof structurefn) {
case 'function':
log('running '+spans[i]+'({article:'+x.article+', hint:'+x.hint+', oldid: '+x.oldid+'})');
setfn(structurefn(x), spans[i], args.navpopup.idNumber);
break;
case 'string':
setfn(structurefn, spans[i], args.navpopup.idNumber);
break;
default:
errlog('unknown thing with label '+spans[i] + ' (span index was ' + i + ')');
break;
}
}
}
// flatten an array
function flatten(list, start) {
var ret=[];
if (typeof start == 'undefined') { start=0; }
for (var i=start; i<list.length; ++i) {
if (typeof list[i] == typeof []) {
return ret.concat(flatten(list[i])).concat(flatten(list, i+1));
}
else { ret.push(list[i]); }
}
return ret;
}
// Generate html for whole popup
function popupHTML (a) {
getValueOf('popupStructure');
var structure=pg.structures[pg.option.popupStructure];
if (typeof structure != 'object') {
//return 'Unknown structure: '+pg.option.popupStructure;
// override user choice
pg.option.popupStructure=pg.optionDefault.popupStructure;
return popupHTML(a);
}
if (typeof structure.popupLayout != 'function') { return 'Bad layout'; }
pg.misc.layout=structure.popupLayout();
if (typeof structure.popupRedirSpans === 'function') { pg.misc.redirSpans=structure.popupRedirSpans(); }
else { pg.misc.redirSpans=[]; }
return makeEmptySpans(pg.misc.layout, a.navpopup);
}
function makeEmptySpans (list, navpop) {
var ret='';
for (var i=0; i<list.length; ++i) {
if (typeof list[i] == typeof '') {
ret += emptySpanHTML(list[i], navpop.idNumber, 'div');
} else if (typeof list[i] == typeof [] && list[i].length > 0 ) {
ret = ret.parenSplit(RegExp('(</[^>]*?>$)')).join(makeEmptySpans(list[i], navpop));
} else if (typeof list[i] == typeof {} && list[i].nodeType ) {
ret += emptySpanHTML(list[i].name, navpop.idNumber, list[i].nodeType);
}
}
return ret;
}
function emptySpanHTML(name, id, tag, classname) {
tag = tag || 'span';
if (!classname) { classname = emptySpanHTML.classAliases[name]; }
classname = classname || name;
if (name == getValueOf('popupDragHandle')) { classname += ' popupDragHandle'; }
return simplePrintf('<%s id="%s" class="%s"></%s>', [tag, name + id, classname, tag]);
}
emptySpanHTML.classAliases={ 'popupSecondPreview': 'popupPreview' };
// generate html for popup image
// <a id="popupImageLinkn"><img id="popupImagen">
// where n=idNumber
function imageHTML(article, idNumber) {
return simplePrintf('<a id="popupImageLink$1">' +
'<img align="right" valign="top" id="popupImg$1" style="display: none;"></img>' +
'</a>', [ idNumber ]);
}
function popTipsSoonFn(id, when, popData) {
if (!when) { when=250; }
var popTips=function(){ setupTooltips(document.getElementById(id), false, true, popData); };
return function() { setTimeout( popTips, when, popData ); };
}
function setPopupTipsAndHTML(html, divname, idnumber, popData) {
setPopupHTML(html, divname, idnumber,
getValueOf('popupSubpopups') ?
popTipsSoonFn(divname + idnumber, null, popData) :
null);
}
// ENDFILE: htmloutput.js
// STARTFILE: mouseout.js
//////////////////////////////////////////////////
// fuzzy checks
function fuzzyCursorOffMenus(x,y, fuzz, parent) {
if (!parent) { return null; }
var uls=parent.getElementsByTagName('ul');
for (var i=0; i<uls.length; ++i) {
if (uls[i].className=='popup_menu') {
if (uls[i].offsetWidth > 0) return false;
} // else {document.title+='.';}
}
return true;
}
function checkPopupPosition () { // stop the popup running off the right of the screen
// FIXME avoid pg.current.link
if (pg.current.link && pg.current.link.navpopup)
pg.current.link.navpopup.limitHorizontalPosition();
}
function mouseOutWikiLink () {
//console ('mouseOutWikiLink');
var a=this;
removeModifierKeyHandler(a);
if (a.navpopup === null || typeof a.navpopup === 'undefined') return;
if ( ! a.navpopup.isVisible() ) {
a.navpopup.banish();
return;
}
restoreTitle(a);
Navpopup.tracker.addHook(posCheckerHook(a.navpopup));
}
function posCheckerHook(navpop) {
return function() {
if (!navpop.isVisible()) { return true; /* remove this hook */ }
if (Navpopup.tracker.dirty) {
return false;
}
var x=Navpopup.tracker.x, y=Navpopup.tracker.y;
var mouseOverNavpop = navpop.isWithin(x,y,navpop.fuzz, navpop.mainDiv) ||
!fuzzyCursorOffMenus(x,y,navpop.fuzz, navpop.mainDiv);
// FIXME it'd be prettier to do this internal to the Navpopup objects
var t=getValueOf('popupHideDelay');
if (t) { t = t * 1000; }
if (!t) {
if(!mouseOverNavpop) {
if(navpop.parentAnchor) {
restoreTitle( navpop.parentAnchor );
}
navpop.banish();
return true; /* remove this hook */
}
return false;
}
// we have a hide delay set
var d=+(new Date());
if ( !navpop.mouseLeavingTime ) {
navpop.mouseLeavingTime = d;
return false;
}
if ( mouseOverNavpop ) {
navpop.mouseLeavingTime=null;
return false;
}
if (d - navpop.mouseLeavingTime > t) {
navpop.mouseLeavingTime=null;
navpop.banish(); return true; /* remove this hook */
}
return false;
};
}
function runStopPopupTimer(navpop) {
// at this point, we should have left the link but remain within the popup
// so we call this function again until we leave the popup.
if (!navpop.stopPopupTimer) {
navpop.stopPopupTimer=setInterval(posCheckerHook(navpop), 500);
navpop.addHook(function(){clearInterval(navpop.stopPopupTimer);},
'hide', 'before');
}
}
// ENDFILE: mouseout.js
// STARTFILE: previewmaker.js
/**
@fileoverview
Defines the {@link Previewmaker} object, which generates short previews from wiki markup.
*/
/**
Creates a new Previewmaker
@constructor
@class The Previewmaker class. Use an instance of this to generate short previews from Wikitext.
@param {String} wikiText The Wikitext source of the page we wish to preview.
@param {String} baseUrl The url we should prepend when creating relative urls.
@param {Navpopup} owner The navpop associated to this preview generator
*/
function Previewmaker(wikiText, baseUrl, owner) {
/** The wikitext which is manipulated to generate the preview. */
this.originalData=wikiText;
this.baseUrl=baseUrl;
this.owner=owner;
this.maxCharacters=getValueOf('popupMaxPreviewCharacters');
this.maxSentences=getValueOf('popupMaxPreviewSentences');
this.setData();
}
Previewmaker.prototype.setData=function() {
var maxSize=Math.max(10000, 2*this.maxCharacters);
this.data=this.originalData.substring(0,maxSize);
};
/** Remove HTML comments
@private
*/
Previewmaker.prototype.killComments = function () {
// this also kills one trailing newline, eg [[diamyo]]
this.data=this.data.replace(RegExp('^<!--[^$]*?-->\\n|\\n<!--[^$]*?-->(?=\\n)|<!--[^$]*?-->', 'g'), '');
};
/**
@private
*/
Previewmaker.prototype.killDivs = function () {
// say goodbye, divs (can be nested, so use * not *?)
this.data=this.data.replace(RegExp('< *div[^>]* *>[\\s\\S]*?< */ *div *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killGalleries = function () {
this.data=this.data.replace(RegExp('< *gallery[^>]* *>[\\s\\S]*?< */ *gallery *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.kill = function(opening, closing, subopening, subclosing, repl) {
var oldk=this.data;
var k=this.killStuff(this.data, opening, closing, subopening, subclosing, repl);
while (k.length < oldk.length) {
oldk=k;
k=this.killStuff(k, opening, closing, subopening, subclosing, repl);
}
this.data=k;
};
/**
@private
*/
Previewmaker.prototype.killStuff = function (txt, opening, closing, subopening, subclosing, repl) {
var op=this.makeRegexp(opening);
var cl=this.makeRegexp(closing, '^');
var sb=subopening ? this.makeRegexp(subopening, '^') : null;
var sc=subclosing ? this.makeRegexp(subclosing, '^') : cl;
if (!op || !cl) {
alert('Navigation Popups error: op or cl is null! something is wrong.');
return;
}
if (!op.test(txt)) { return txt; }
var ret='';
var opResult = op.exec(txt);
ret = txt.substring(0,opResult.index);
txt=txt.substring(opResult.index+opResult[0].length);
var depth = 1;
while (txt.length > 0) {
var removal=0;
if (depth==1 && cl.test(txt)) {
depth--;
removal=cl.exec(txt)[0].length;
} else if (depth > 1 && sc.test(txt)) {
depth--;
removal=sc.exec(txt)[0].length;
}else if (sb && sb.test(txt)) {
depth++;
removal=sb.exec(txt)[0].length;
}
if ( !removal ) { removal = 1; }
txt=txt.substring(removal);
if (depth === 0) { break; }
}
return ret + (repl || '') + txt;
};
/**
@private
*/
Previewmaker.prototype.makeRegexp = function (x, prefix, suffix) {
prefix = prefix || '';
suffix = suffix || '';
var reStr='';
var flags='';
if (isString(x)) {
reStr=prefix + literalizeRegex(x) + suffix;
} else if (isRegExp(x)) {
var s=x.toString().substring(1);
var sp=s.split('/');
flags=sp[sp.length-1];
sp[sp.length-1]='';
s=sp.join('/');
s=s.substring(0,s.length-1);
reStr= prefix + s + suffix;
} else {
log ('makeRegexp failed');
}
log ('makeRegexp: got reStr=' + reStr + ', flags=' + flags);
return RegExp(reStr, flags);
};
/**
@private
*/
Previewmaker.prototype.killBoxTemplates = function () {
// taxobox removal... in fact, there's a saudiprincebox_begin, so let's be more general
// also, have float_begin, ... float_end
this.kill(RegExp('[{][{][^{}\\s|]*?(float|box)[_ ](begin|start)', 'i'), /[}][}]\s*/, '{{');
// infoboxes etc
// from User:Zyxw/popups.js: kill frames too
this.kill(RegExp('[{][{][^{}\\s|]*?(infobox|elementbox|frame)[_ ]', 'i'), /[}][}]\s*/, '{{');
};
/**
@private
*/
Previewmaker.prototype.killTemplates = function () {
// the plus sign is to avoid transcluding [[Template:', ']]
this.kill('{'+'{', '}'+'}', '{', '}', ' ');
};
/**
@private
*/
Previewmaker.prototype.killTables = function () {
// tables are bad, too
// this can be slow, but it's an inprovement over a browser hang
// torture test: [[Comparison_of_Intel_Central_Processing_Units]]
this.kill('{|', /[|]}\s*/, '{|');
this.kill(/<table.*?>/i, /<\/table.*?>/i, /<table.*?>/i);
// remove lines starting with a pipe for the hell of it (?)
this.data=this.data.replace(RegExp('^[|].*$', 'mg'), '');
};
/**
@private
*/
Previewmaker.prototype.killImages = function () {
var forbiddenNamespaceAliases = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=pg.nsImageId && _namespaceId!=pg.nsCategoryId) return;
forbiddenNamespaceAliases.push(_localizedNamespaceLc.split(' ').join('[ _]')); //todo: escape regexp fragments!
});
// images and categories are a nono
this.kill(RegExp('[[][[]\\s*(' + forbiddenNamespaceAliases.join('|') + ')\\s*:', 'i'),
/\]\]\s*/, '[', ']');
};
/**
@private
*/
Previewmaker.prototype.killHTML = function () {
// kill <ref ...>...</ref>
this.kill(/<ref\b[^/>]*?>/i, /<\/ref>/i);
// let's also delete entire lines starting with <. it's worth a try.
this.data=this.data.replace(RegExp('(^|\\n) *<.*', 'g'), '\n');
// and those pesky html tags, but not <nowiki> or <blockquote>
var splitted=this.data.parenSplit(/(<[\w\W]*?(?:>|$|(?=<)))/);
var len=splitted.length;
for (var i=1; i<len; i=i+2) {
switch (splitted[i]) {
case '<nowiki>':
case '</nowiki>':
case '<blockquote>':
case '</blockquote>':
break;
default:
splitted[i]='';
}
}
this.data=splitted.join('');
};
/**
@private
*/
Previewmaker.prototype.killChunks = function() { // heuristics alert
// chunks of italic text? you crazy, man?
var italicChunkRegex=new RegExp
("((^|\\n)\\s*:*\\s*''[^']([^']|'''|'[^']){20}(.|\\n[^\\n])*''[.!?\\s]*\\n)+", 'g');
// keep stuff separated, though, so stick in \n (fixes [[Union Jack]]?
this.data=this.data.replace(italicChunkRegex, '\n');
};
/**
@private
*/
Previewmaker.prototype.mopup = function () {
// we simply *can't* be doing with horizontal rules right now
this.data=this.data.replace(RegExp('^-{4,}','mg'),'');
// no indented lines
this.data=this.data.replace(RegExp('(^|\\n) *:[^\\n]*','g'), '');
// replace __TOC__, __NOTOC__ and whatever else there is
// this'll probably do
this.data=this.data.replace(RegExp('^__[A-Z_]*__ *$', 'gmi'),'');
};
/**
@private
*/
Previewmaker.prototype.firstBit = function () {
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d=this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data=this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data=this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data=this.data.replace(RegExp('^[\\s\\n]*'), '');
var stuff=(RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) { d = stuff[0]; }
if (!getValueOf('popupPreviewFirstParOnly')) { d = this.data; }
/// now put \n\n after sections so that bullets and numbered lists work
d=d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// Split sentences. Superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["'+"'"+']*\\s)','g'));
// leading space is bad, mmkay?
d[0]=d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds=RegExp('([^.][a-z][.] *[a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|op|cit|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
this.fullLength=d.join('').length;
var n=this.maxSentences;
var dd=this.firstSentences(d,n);
do {
dd=this.firstSentences(d,n); --n;
} while ( dd.length > this.maxCharacters && n !== 0 );
this.data = dd;
};
/**
@private
*/
Previewmaker.prototype.fixSentenceEnds = function(strs, reg) {
// take an array of strings, strs
// join strs[i] to strs[i+1] & strs[i+2] if strs[i] matches regex reg
for (var i=0; i<strs.length-2; ++i) {
if (reg.test(strs[i])) {
var a=[];
for (var j=0; j<strs.length; ++j) {
if (j<i) a[j]=strs[j];
if (j==i) a[i]=strs[i]+strs[i+1]+strs[i+2];
if (j>i+2) a[j-2]=strs[j];
}
return this.fixSentenceEnds(a,reg);
}
}
return strs;
};
/**
@private
*/
Previewmaker.prototype.firstSentences = function(strs, howmany) {
var t=strs.slice(0, 2*howmany);
return t.join('');
};
/**
@private
*/
Previewmaker.prototype.killBadWhitespace = function() {
// also cleans up isolated '''', eg [[Suntory Sungoliath]]
this.data=this.data.replace(RegExp('^ *\'+ *$', 'gm'), '');
};
/**
Runs the various methods to generate the preview.
The preview is stored in the <code>html</html> field.
@private
*/
Previewmaker.prototype.makePreview = function() {
if (this.owner.article.namespaceId()!=pg.nsTemplateId &&
this.owner.article.namespaceId()!=pg.nsImageId ) {
this.killComments();
this.killDivs();
this.killGalleries();
this.killBoxTemplates();
if (getValueOf('popupPreviewKillTemplates')) {
this.killTemplates();
} else {
this.killMultilineTemplates();
}
this.killTables();
this.killImages();
this.killHTML();
this.killChunks();
this.mopup();
this.firstBit();
this.killBadWhitespace();
}
else
{
this.killHTML();
}
this.html=wiki2html(this.data, this.baseUrl); // needs livepreview
this.fixHTML();
this.stripLongTemplates();
};
/**
@private
*/
Previewmaker.prototype.esWiki2HtmlPart = function(data) {
var reLinks = /(?:\[\[([^|\]]*)(?:\|([^|\]]*))*]]([a-z]*))/gi; //match a wikilink
reLinks.lastIndex = 0; //reset regex
var match;
var result = "";
var postfixIndex = 0;
while ((match = reLinks.exec(data))) //match all wikilinks
{
//FIXME: the way that link is built here isn't perfect. It is clickable, but popups preview won't recognize it in some cases.
result += pg.escapeQuotesHTML(data.substring(postfixIndex, match.index)) +
'<a href="'+Insta.conf.paths.articles+pg.escapeQuotesHTML(match[1])+'">'+pg.escapeQuotesHTML((match[2]?match[2]:match[1])+match[3])+"</a>";
postfixIndex = reLinks.lastIndex;
}
//append the rest
result += pg.escapeQuotesHTML(data.substring(postfixIndex));
return result;
};
Previewmaker.prototype.editSummaryPreview=function() {
var reAes = /\/\* *(.*?) *\*\//g; //match the first section marker
reAes.lastIndex = 0; //reset regex
var match;
match = reAes.exec(this.data);
if (match)
{
//we have a section link. Split it, process it, combine it.
var prefix = this.data.substring(0,match.index-1);
var section = match[1];
var postfix = this.data.substring(reAes.lastIndex);
var start = "<span class='autocomment'>";
var end = "</span>";
if (prefix.length>0) start = this.esWiki2HtmlPart(prefix) + " " + start + "- ";
if (postfix.length>0) end = ": " + end + this.esWiki2HtmlPart(postfix);
var t=new Title().fromURL(this.baseUrl);
t.anchorFromUtf(section);
var sectionLink = Insta.conf.paths.articles + pg.escapeQuotesHTML(t.toString(true)) + '#' + pg.escapeQuotesHTML(t.anchor);
return start + '<a href="'+sectionLink+'">→</a> '+pg.escapeQuotesHTML(section) + end;
}
//else there's no section link, htmlify the whole thing.
return this.esWiki2HtmlPart(this.data);
};
//<NOLITE>
/** Test function for debugging preview problems one step at a time.
*/
/*eslint-disable */
function previewSteps(txt) {
try {
txt=txt || document.editform.wpTextbox1.value;
} catch (err) {
if (pg.cache.pages.length > 0) {
txt=pg.cache.pages[pg.cache.pages.length-1].data;
} else {
alert('provide text or use an edit page');
}
}
txt=txt.substring(0,10000);
var base=pg.wiki.articlebase + Title.fromURL(document.location.href).urlString();
var p=new Previewmaker(txt, base, pg.current.link.navpopup);
if (this.owner.article.namespaceId() != pg.nsTemplateId) {
p.killComments(); if (!confirm('done killComments(). Continue?\n---\n' + p.data)) { return; }
p.killDivs(); if (!confirm('done killDivs(). Continue?\n---\n' + p.data)) { return; }
p.killGalleries(); if (!confirm('done killGalleries(). Continue?\n---\n' + p.data)) { return; }
p.killBoxTemplates(); if (!confirm('done killBoxTemplates(). Continue?\n---\n' + p.data)) { return; }
if (getValueOf('popupPreviewKillTemplates')) {
p.killTemplates(); if (!confirm('done killTemplates(). Continue?\n---\n' + p.data)) { return; }
} else {
p.killMultilineTemplates(); if (!confirm('done killMultilineTemplates(). Continue?\n---\n' + p.data)) { return; }
}
p.killTables(); if (!confirm('done killTables(). Continue?\n---\n' + p.data)) { return; }
p.killImages(); if (!confirm('done killImages(). Continue?\n---\n' + p.data)) { return; }
p.killHTML(); if (!confirm('done killHTML(). Continue?\n---\n' + p.data)) { return; }
p.killChunks(); if (!confirm('done killChunks(). Continue?\n---\n' + p.data)) { return; }
p.mopup(); if (!confirm('done mopup(). Continue?\n---\n' + p.data)) { return; }
p.firstBit(); if (!confirm('done firstBit(). Continue?\n---\n' + p.data)) { return; }
p.killBadWhitespace(); if (!confirm('done killBadWhitespace(). Continue?\n---\n' + p.data)) { return; }
}
p.html=wiki2html(p.data, base); // needs livepreview
p.fixHTML(); if (!confirm('done fixHTML(). Continue?\n---\n' + p.html)) { return; }
p.stripLongTemplates(); if (!confirm('done stripLongTemplates(). Continue?\n---\n' + p.html)) { return; }
alert('finished preview - end result follows.\n---\n' + p.html);
}
/*eslint-enable */
//</NOLITE>
/**
Works around livepreview bugs.
@private
*/
Previewmaker.prototype.fixHTML = function() {
if(!this.html) return;
var ret = this.html;
// fix question marks in wiki links
// maybe this'll break some stuff :-(
ret=ret.replace(RegExp('(<a href="' + pg.wiki.articlePath + '/[^"]*)[?](.*?")', 'g'), '$1%3F$2');
ret=ret.replace(RegExp('(<a href=\'' + pg.wiki.articlePath + '/[^\']*)[?](.*?\')', 'g'), '$1%3F$2');
// FIXME fix up % too
this.html=ret;
};
/**
Generates the preview and displays it in the current popup.
Does nothing if the generated preview is invalid or consists of whitespace only.
Also activates wikilinks in the preview for subpopups if the popupSubpopups option is true.
*/
Previewmaker.prototype.showPreview = function () {
this.makePreview();
if (typeof this.html != typeof '') return;
if (RegExp('^\\s*$').test(this.html)) return;
setPopupHTML('<hr />', 'popupPrePreviewSep', this.owner.idNumber);
setPopupTipsAndHTML(this.html, 'popupPreview', this.owner.idNumber, { owner: this.owner });
var more = (this.fullLength > this.data.length) ? this.moreLink() : '';
setPopupHTML(more, 'popupPreviewMore', this.owner.idNumber);
};
/**
@private
*/
Previewmaker.prototype.moreLink=function() {
var a=document.createElement('a');
a.className='popupMoreLink';
a.innerHTML=popupString('more...');
var savedThis=this;
a.onclick=function() {
savedThis.maxCharacters+=2000;
savedThis.maxSentences+=20;
savedThis.setData();
savedThis.showPreview();
};
return a;
};
/**
@private
*/
Previewmaker.prototype.stripLongTemplates = function() {
// operates on the HTML!
this.html=this.html.replace(RegExp('^.{0,1000}[{][{][^}]*?(<(p|br)( /)?>\\s*){2,}([^{}]*?[}][}])?', 'gi'), '');
this.html=this.html.split('\n').join(' '); // workaround for <pre> templates
this.html=this.html.replace(RegExp('[{][{][^}]*<pre>[^}]*[}][}]','gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killMultilineTemplates = function() {
this.kill('{{{', '}}}');
this.kill(RegExp('\\s*[{][{][^{}]*\\n'), '}}', '{{');
};
// ENDFILE: previewmaker.js
// STARTFILE: querypreview.js
function loadAPIPreview(queryType, article, navpop) {
var art=new Title(article).urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
var htmlGenerator=function(/*a, d*/){alert('invalid html generator');};
var usernameart = '';
switch (queryType) {
case 'history':
url += 'titles=' + art + '&prop=revisions&rvlimit=' +
getValueOf('popupHistoryPreviewLimit');
htmlGenerator=APIhistoryPreviewHTML;
break;
case 'category':
url += 'list=categorymembers&cmtitle=' + art;
htmlGenerator=APIcategoryPreviewHTML;
break;
case 'userinfo':
var username = new Title( article ).userName();
usernameart = encodeURIComponent( username );
if (pg.re.ipUser.test(username)) {
url += 'list=blocks&bkprop=range|restrictions&bkip=' + usernameart;
} else {
url += 'list=users|usercontribs&usprop=blockinfo|groups|editcount|registration|gender&ususers=' + usernameart + "&meta=globaluserinfo&guiprop=groups|unattached&guiuser="+ usernameart + "&uclimit=1&ucprop=timestamp&ucuser=" + usernameart;
}
htmlGenerator=APIuserInfoPreviewHTML;
break;
case 'contribs':
usernameart = encodeURIComponent( new Title( article ).userName() );
url += 'list=usercontribs&ucuser=' + usernameart +
'&uclimit=' + getValueOf('popupContribsPreviewLimit');
htmlGenerator=APIcontribsPreviewHTML;
break;
case 'imagepagepreview':
var trail='';
if (getValueOf('popupImageLinks')) { trail = '&list=imageusage&iutitle=' + art; }
url += 'titles=' + art + '&prop=revisions|imageinfo&rvprop=content' + trail;
htmlGenerator=APIimagepagePreviewHTML;
break;
case 'backlinks':
url += 'list=backlinks&bltitle=' + art;
htmlGenerator=APIbacklinksPreviewHTML;
break;
case 'revision':
if (article.oldid) {
url += 'revids=' + article.oldid;
} else {
url += 'titles=' + article.removeAnchor().urlString();
}
url += '&prop=revisions|pageprops|info|images|categories&rvprop=ids|timestamp|flags|comment|user|content&cllimit=max&imlimit=max';
htmlGenerator=APIrevisionPreviewHTML;
break;
}
pendingNavpopTask(navpop);
var callback=function(d){
log( "callback of API functions was hit" );
showAPIPreview(queryType, htmlGenerator(article,d,navpop), navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_'+queryType+'_QUERY_DATA'); }
}
function linkList(list) {
list.sort(function(x,y) { return (x==y ? 0 : (x<y ? -1 : 1)); });
var buf=[];
for (var i=0; i<list.length; ++i) {
buf.push(wikiLink({article: new Title(list[i]),
text: list[i].split(' ').join(' '),
action: 'view'}));
}
return buf.join(', ');
}
function getTimeOffset() {
var tz = mw.user.options.get('timecorrection');
if(tz) {
if( tz.indexOf('|') > -1 ) {
// New format
return parseInt(tz.split('|')[1],10);
} else if ( tz.indexOf(':') > -1 ) {
// Old format
return( parseInt(tz,10)*60 + parseInt(tz.split(':')[1],10) );
}
}
return 0;
}
/*
* Creates a HTML table that's shown in the history and user-contribs popups.
* @param {Object[]} h - a list of revisions, returned from the API
* @param {boolean} reallyContribs - true only if we're displaying user contributions
*/
function editPreviewTable(article, h, reallyContribs, timeOffset) {
var html=['<table>'];
var day=null;
var curart=article;
var page=null;
var makeFirstColumnLinks;
if(reallyContribs) {
// We're showing user contributions, so make (diff | hist) links
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&diff=prev' +
'&oldid=' + currentRevision.revid + '">' + popupString('diff') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&action=history">' +
popupString('hist') + '</a>';
result += ')';
return result;
};
} else {
// It's a regular history page, so make (cur | last) links
var firstRevid = h[0].revid;
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=' + firstRevid + '&oldid=' + currentRevision.revid + '">' + popupString('cur') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=prev&oldid=' + currentRevision.revid + '">' + popupString('last') + '</a>';
result += ')';
return result;
};
}
for (var i=0; i<h.length; ++i) {
if (reallyContribs) {
page = h[i].title;
curart = new Title(page);
}
var minor = h[i].minor ? '<b>m </b>' : '';
var editDate = adjustDate(getDateFromTimestamp(h[i].timestamp), timeOffset);
var thisDay = dayFormat(editDate);
var thisTime = timeFormat(editDate);
if (thisDay == day) {
thisDay = '';
} else {
day = thisDay;
}
if (thisDay) {
html.push( '<tr><td colspan=3><span class="popup_history_date">' +
thisDay+'</span></td></tr>' );
}
html.push('<tr class="popup_history_row_' + ( (i%2) ? 'odd' : 'even') + '">');
html.push('<td>' + makeFirstColumnLinks(h[i]) + '</td>');
html.push('<td>' +
'<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&oldid=' + h[i].revid + '">' + thisTime + '</a></td>');
var col3url='', col3txt='';
if (!reallyContribs) {
var user=h[i].user;
if( !h[i].userhidden ) {
if( pg.re.ipUser.test(user) ) {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Contributions&target=' + new Title(user).urlString();
} else {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(user).urlString();
}
col3txt=pg.escapeQuotesHTML(user);
} else {
col3url=getValueOf('popupRevDelUrl');
col3txt=pg.escapeQuotesHTML( popupString('revdel'));
}
} else {
col3url=pg.wiki.titlebase + curart.urlString();
col3txt=pg.escapeQuotesHTML(page);
}
html.push('<td>' + (reallyContribs ? minor : '') +
'<a href="' + col3url + '">' + col3txt + '</a></td>');
var comment='';
var c=h[i].comment || h[i].content;
if (c) {
comment=new Previewmaker(c, new Title(curart).toUrl()).editSummaryPreview();
} else if ( h[i].commenthidden ) {
comment=popupString('revdel');
}
html.push('<td>' + (!reallyContribs ? minor : '') + comment + '</td>');
html.push('</tr>');
html=[html.join('')];
}
html.push('</table>');
return html.join('');
}
function getDateFromTimestamp(t) {
var s=t.split(/[^0-9]/);
switch(s.length) {
case 0: return null;
case 1: return new Date(s[0]);
case 2: return new Date(s[0], s[1]-1);
case 3: return new Date(s[0], s[1]-1, s[2]);
case 4: return new Date(s[0], s[1]-1, s[2], s[3]);
case 5: return new Date(s[0], s[1]-1, s[2], s[3], s[4]);
case 6: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5]);
default: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5], s[6]);
}
}
function adjustDate(d, offset) {
// offset is in minutes
var o=offset * 60 * 1000;
return new Date( +d + o);
}
function dayFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCFullYear(), editDate.getUTCMonth()+1, editDate.getUTCDate()]).join('-'); }
return map(zeroFill, [editDate.getFullYear(), editDate.getMonth()+1, editDate.getDate()]).join('-');
}
function timeFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCHours(), editDate.getUTCMinutes(), editDate.getUTCSeconds()]).join(':'); }
return map(zeroFill, [editDate.getHours(), editDate.getMinutes(), editDate.getSeconds()]).join(':');
}
function showAPIPreview(queryType, html, id, navpop, download) {
// DJ: done
var target='popupPreview';
completedNavpopTask(navpop);
switch (queryType) {
case 'imagelinks':
case 'category':
target='popupPostPreview'; break;
case 'userinfo':
target='popupUserData'; break;
case 'revision':
insertPreview(download);
return;
}
setPopupTipsAndHTML(html, target, id);
}
function APIrevisionPreviewHTML(article, download) {
try{
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
if( page.missing ) {
// TODO we need to fix this proper later on
download.owner = null;
return;
}
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
download.data = content;
download.lastModified = new Date(page.revisions[0].timestamp);
}
} catch(someError) {
return 'Revision preview failed :(';
}
}
function APIbacklinksPreviewHTML(article, download/*, navpop*/ ) {
try {
var jsObj=getJsObj(download.data);
var list=jsObj.query.backlinks;
var html=[];
if (!list) { return popupString('No backlinks found'); }
for ( var i=0; i < list.length; i++ ) {
var t=new Title(list[i].title);
html.push('<a href="' + pg.wiki.titlebase + t.urlString() + '">' + t + '</a>');
}
html=html.join(', ');
if (jsObj['continue'] && jsObj['continue'].blcontinue) {
html += popupString(' and more');
}
return html;
} catch (someError) {
return 'backlinksPreviewHTML went wonky';
}
}
pg.fn.APIsharedImagePagePreviewHTML = function APIsharedImagePagePreviewHTML(obj) {
log( "APIsharedImagePagePreviewHTML" );
var popupid = obj.requestid;
if( obj.query && obj.query.pages )
{
var page=anyChild(obj.query.pages );
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
/* Not entirely safe, but the best we can do */
var p=new Previewmaker(content, pg.current.link.navpopup.article, pg.current.link.navpopup);
p.makePreview();
setPopupHTML( p.html, "popupSecondPreview", popupid );
}
}
};
function APIimagepagePreviewHTML(article, download, navpop) {
try {
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
var content= (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
var ret='';
var alt='';
try{alt=navpop.parentAnchor.childNodes[0].alt;} catch(e){}
if (alt) {
ret = ret + '<hr /><b>' + popupString('Alt text:') + '</b> ' + pg.escapeQuotesHTML(alt);
}
if (typeof content === 'string') {
var p=prepPreviewmaker(content, article, navpop);
p.makePreview();
if (p.html) { ret += '<hr />' + p.html; }
if (getValueOf('popupSummaryData')) {
var info=getPageInfo(content, download);
log(info);
setPopupTrailer(info, navpop.idNumber);
}
}
if (page && page.imagerepository == "shared" ) {
var art=new Title(article);
var encart = encodeURIComponent( "File:" + art.stripNamespace() );
var shared_url = pg.wiki.apicommonsbase + '?format=json&formatversion=2' +
'&callback=pg.fn.APIsharedImagePagePreviewHTML' +
'&requestid=' + navpop.idNumber +
'&action=query&prop=revisions&rvprop=content&titles=' + encart;
ret = ret +'<hr />' + popupString( 'Image from Commons') +
': <a href="' + pg.wiki.commonsbase + '?title=' + encart + '">' +
popupString( 'Description page') + '</a>';
mw.loader.load( shared_url );
}
showAPIPreview('imagelinks', APIimagelinksPreviewHTML(article,download), navpop.idNumber, download);
return ret;
} catch (someError) {
return 'API imagepage preview failed :(';
}
}
function APIimagelinksPreviewHTML(article, download) {
try {
var jsobj=getJsObj(download.data);
var list=jsobj.query.imageusage;
if (list) {
var ret=[];
for (var i=0; i < list.length; i++) {
ret.push(list[i].title);
}
if (ret.length === 0) { return popupString('No image links found'); }
return '<h2>' + popupString('File links') + '</h2>' + linkList(ret);
} else {
return popupString('No image links found');
}
} catch(someError) {
return 'Image links preview generation failed :(';
}
}
function APIcategoryPreviewHTML(article, download) {
try{
var jsobj=getJsObj(download.data);
var list=jsobj.query.categorymembers;
var ret=[];
for (var p=0; p < list.length; p++) {
ret.push(list[p].title);
}
if (ret.length === 0) { return popupString('Empty category'); }
ret = '<h2>' + tprintf('Category members (%s shown)', [ret.length]) + '</h2>' +linkList(ret);
if (jsobj['continue'] && jsobj['continue'].cmcontinue) {
ret += popupString(' and more');
}
return ret;
} catch(someError) {
return 'Category preview failed :(';
}
}
function APIuserInfoPreviewHTML(article, download) {
var ret=[];
var queryobj = {};
try{
queryobj=getJsObj(download.data).query;
} catch(someError) { return 'Userinfo preview failed :('; }
var user=anyChild(queryobj.users);
if (user) {
var globaluserinfo=queryobj.globaluserinfo;
if (user.invalid === '') {
ret.push( popupString( 'Invalid user') );
} else if (user.missing === '') {
ret.push( popupString( 'Not a registered username') );
}
if( user.blockedby ) {
if( user.blockpartial ) {
ret.push('<b>' + popupString('Has blocks') + '</b>');
} else {
ret.push('<b>' + popupString('BLOCKED') + '</b>');
}
}
if( globaluserinfo && ( 'locked' in globaluserinfo || 'hidden' in globaluserinfo ) ) {
var lockedSulAccountIsAttachedToThis = true;
for( var i=0; globaluserinfo.unattached && i < globaluserinfo.unattached.length; i++) {
if ( globaluserinfo.unattached[i].wiki === mw.config.get('wgDBname') ) {
lockedSulAccountIsAttachedToThis=false;
break;
}
}
if (lockedSulAccountIsAttachedToThis) {
if ( 'locked' in globaluserinfo ) ret.push('<b><i>' + popupString('LOCKED') + '</i></b>');
if ( 'hidden' in globaluserinfo ) ret.push('<b><i>' + popupString('HIDDEN') + '</i></b>');
}
}
if( getValueOf('popupShowGender') && user.gender ) {
switch( user.gender ) {
case "male": ret.push( popupString( "\u2642" ) ); break;
case "female": ret.push( popupString( "\u2640" ) ); break;
}
}
if( user.groups ) {
for( var j=0; j < user.groups.length; j++) {
var currentGroup = user.groups[j];
if( ["*", "user", "autoconfirmed", "extendedconfirmed"].indexOf( currentGroup ) === -1 ) {
ret.push( pg.escapeQuotesHTML(user.groups[j]) );
}
}
}
if( globaluserinfo && globaluserinfo.groups ) {
for( var k=0; k < globaluserinfo.groups.length; k++) {
ret.push( '<i>'+pg.escapeQuotesHTML(globaluserinfo.groups[k])+'</i>' );
}
}
if( user.registration )
ret.push( pg.escapeQuotesHTML((user.editcount?user.editcount:'0') + popupString(' edits since: ') + (user.registration?dayFormat(getDateFromTimestamp(user.registration)):'')) );
}
if (queryobj.usercontribs && queryobj.usercontribs.length) {
ret.push( popupString('last edit on ') + dayFormat(getDateFromTimestamp(queryobj.usercontribs[0].timestamp)) );
}
if (queryobj.blocks) {
ret.push( popupString( 'IP user') ); //we only request list=blocks for IPs
for (var l=0; l<queryobj.blocks.length; l++) {
console.log(queryobj);
var rbstr = queryobj.blocks[l].rangestart === queryobj.blocks[l].rangeend ? 'BLOCK' : 'RANGEBLOCK';
rbstr = (!Array.isArray(queryobj.blocks[l].restrictions) ? 'Has ' + rbstr.toLowerCase() + 's' : rbstr + 'ED');
ret.push('<b>' + popupString(rbstr) + '</b>' );
}
}
ret = '<hr />' + ret.join( ', ' );
return ret;
}
function APIcontribsPreviewHTML(article, download, navpop) {
return APIhistoryPreviewHTML(article, download, navpop, true);
}
function APIhistoryPreviewHTML(article, download, navpop, reallyContribs) {
try {
var jsobj=getJsObj(download.data);
var edits = [];
if( reallyContribs ) {
edits=jsobj.query.usercontribs;
} else {
edits=anyChild(jsobj.query.pages).revisions;
}
var ret=editPreviewTable(article, edits, reallyContribs, getTimeOffset());
return ret;
} catch (someError) {
return 'History preview failed :-(';
}
}
//</NOLITE>
// ENDFILE: querypreview.js
// STARTFILE: debug.js
////////////////////////////////////////////////////////////////////
// Debugging functions
////////////////////////////////////////////////////////////////////
function setupDebugging() {
//<NOLITE>
if (window.popupDebug) { // popupDebug is set from .version
window.log=function(x) { //if(gMsg!='')gMsg += '\n'; gMsg+=time() + ' ' + x; };
window.console.log(x);
};
window.errlog=function(x) {
window.console.error(x);
};
log('Initializing logger');
} else {
//</NOLITE>
window.log = function() {};
window.errlog = function() {};
//<NOLITE>
}
//</NOLITE>
}
// ENDFILE: debug.js
// STARTFILE: images.js
// load image of type Title.
function loadImage(image, navpop) {
if (typeof image.stripNamespace != 'function') { alert('loadImages bad'); }
// API call to retrieve image info.
if ( !getValueOf('popupImages') ) return;
if ( !isValidImageName(image) ) return false;
var art=image.urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query';
url += '&prop=imageinfo&iiprop=url|mime&iiurlwidth=' + getValueOf('popupImageSizeLarge');
url += '&titles=' + art;
pendingNavpopTask(navpop);
var callback=function(d){
popupsInsertImage(navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'after', 'DOWNLOAD_IMAGE_QUERY_DATA'); }
}
function popupsInsertImage(id, navpop, download) {
log( "popupsInsertImage");
var imageinfo;
try {
var jsObj=getJsObj(download.data);
var imagepage=anyChild(jsObj.query.pages);
if (typeof imagepage.imageinfo === 'undefined') return;
imageinfo = imagepage.imageinfo[0];
} catch (someError) {
log( "popupsInsertImage failed :(" );
return;
}
var popupImage = document.getElementById("popupImg"+id);
if (!popupImage) {
log( "could not find insertion point for image");
return;
}
popupImage.width=getValueOf('popupImageSize');
popupImage.style.display='inline';
// Set the source for the image.
if( imageinfo.thumburl )
popupImage.src=imageinfo.thumburl;
else if( imageinfo.mime.indexOf("image") === 0 ){
popupImage.src=imageinfo.url;
log( "a thumb could not be found, using original image" );
} else log( "fullsize imagethumb, but not sure if it's an image");
var a=document.getElementById("popupImageLink"+id);
if (a === null) { return null; }
// Determine the action of the surrouding imagelink.
switch (getValueOf('popupThumbAction')) {
case 'imagepage':
if (pg.current.article.namespaceId()!=pg.nsImageId) {
a.href=imageinfo.descriptionurl;
// FIXME: unreliable pg.idNumber
popTipsSoonFn('popupImage' + id)();
break;
}
/* falls through */
case 'sizetoggle':
a.onclick=toggleSize;
a.title=popupString('Toggle image size');
return;
case 'linkfull':
a.href = imageinfo.url;
a.title=popupString('Open full-size image');
return;
}
}
// Toggles the image between inline small and navpop fullwidth.
// It's the same image, no actual sizechange occurs, only display width.
function toggleSize() {
var imgContainer=this;
if (!imgContainer) {
alert('imgContainer is null :/');
return;
}
var img=imgContainer.firstChild;
if (!img) {
alert('img is null :/');
return;
}
if (!img.style.width || img.style.width==='') {
img.style.width='100%';
} else {
img.style.width='';
}
}
// Returns one title of an image from wikiText.
function getValidImageFromWikiText(wikiText) {
// nb in pg.re.image we're interested in the second bracketed expression
// this may change if the regex changes :-(
//var match=pg.re.image.exec(wikiText);
var matched=null;
var match;
// strip html comments, used by evil bots :-(
var t = removeMatchesUnless(wikiText, RegExp('(<!--[\\s\\S]*?-->)'), 1,
RegExp('^<!--[^[]*popup', 'i'));
while ( ( match = pg.re.image.exec(t) ) ) {
// now find a sane image name - exclude templates by seeking {
var m = match[2] || match[6];
if ( isValidImageName(m) ) {
matched=m;
break;
}
}
pg.re.image.lastIndex=0;
if (!matched) { return null; }
return mw.config.get('wgFormattedNamespaces')[pg.nsImageId]+':'+upcaseFirst(matched);
}
function removeMatchesUnless(str, re1, parencount, re2) {
var split=str.parenSplit(re1);
var c=parencount + 1;
for (var i=0; i<split.length; ++i) {
if ( i%c === 0 || re2.test(split[i]) ) { continue; }
split[i]='';
}
return split.join('');
}
//</NOLITE>
// ENDFILE: images.js
// STARTFILE: namespaces.js
// Set up namespaces and other non-strings.js localization
// (currently that means redirs too)
function setNamespaces() {
pg.nsSpecialId = -1;
pg.nsMainspaceId = 0;
pg.nsImageId = 6;
pg.nsUserId = 2;
pg.nsUsertalkId = 3;
pg.nsCategoryId = 14;
pg.nsTemplateId = 10;
}
function setRedirs() {
var r='redirect';
var R='REDIRECT';
var redirLists={
//<NOLITE>
'ar': [ R, 'تحويل' ],
'be': [ r, 'перанакіраваньне' ],
'bg': [ r, 'пренасочване', 'виж' ],
'bs': [ r, 'Preusmjeri', 'preusmjeri', 'PREUSMJERI' ],
'cs': [ R, 'PŘESMĚRUJ' ],
'cy': [ r, 'ail-cyfeirio' ],
'de': [ R, 'WEITERLEITUNG' ],
'el': [ R, 'ΑΝΑΚΑΤΕΥΘΥΝΣΗ'],
'eo': [ R, 'ALIDIREKTU', 'ALIDIREKTI' ],
'es': [ R, 'REDIRECCIÓN' ],
'et': [ r, 'suuna' ],
'ga': [ r, 'athsheoladh' ],
'gl': [ r, 'REDIRECCIÓN', 'REDIRECIONAMENTO'],
'he': [ R, 'הפניה' ],
'hu': [ R, 'ÁTIRÁNYÍTÁS' ],
'is': [ r, 'tilvísun', 'TILVÍSUN' ],
'it': [ R, 'RINVIA', 'Rinvia'],
'ja': [ R, '転送' ],
'mk': [ r, 'пренасочување', 'види' ],
'nds': [ r, 'wiederleiden' ],
'nl': [ R, 'DOORVERWIJZING' ],
'nn': [ r, 'omdiriger' ],
'pl': [ R, 'PATRZ', 'PRZEKIERUJ', 'TAM' ],
'pt': [ R, 'redir' ],
'ru': [ R, 'ПЕРЕНАПРАВЛЕНИЕ', 'ПЕРЕНАПР' ],
'sk': [ r, 'presmeruj' ],
'sr': [ r, 'Преусмери', 'преусмери', 'ПРЕУСМЕРИ', 'Preusmeri', 'preusmeri', 'PREUSMERI' ],
'tt': [ R, 'yünältü', 'перенаправление', 'перенапр' ],
'uk': [ R, 'ПЕРЕНАПРАВЛЕННЯ', 'ПЕРЕНАПР' ],
'vi': [ r, 'đổi' ],
'zh': [ R, '重定向'] // no comma
//</NOLITE>
};
var redirList=redirLists[ pg.wiki.lang ] || [r, R];
// Mediawiki is very tolerant about what comes after the #redirect at the start
pg.re.redirect=RegExp('^\\s*[#](' + redirList.join('|') + ').*?\\[{2}([^\\|\\]]*)(|[^\\]]*)?\\]{2}\\s*(.*)', 'i');
}
function setInterwiki() {
if (pg.wiki.wikimedia) {
// From https://meta.wikimedia.org/wiki/List_of_Wikipedias
pg.wiki.interwiki='aa|ab|ace|af|ak|als|am|an|ang|ar|arc|arz|as|ast|av|ay|az|ba|bar|bat-smg|bcl|be|be-x-old|bg|bh|bi|bjn|bm|bn|bo|bpy|br|bs|bug|bxr|ca|cbk-zam|cdo|ce|ceb|ch|cho|chr|chy|ckb|co|cr|crh|cs|csb|cu|cv|cy|da|de|diq|dsb|dv|dz|ee|el|eml|en|eo|es|et|eu|ext|fa|ff|fi|fiu-vro|fj|fo|fr|frp|frr|fur|fy|ga|gag|gan|gd|gl|glk|gn|got|gu|gv|ha|hak|haw|he|hi|hif|ho|hr|hsb|ht|hu|hy|hz|ia|id|ie|ig|ii|ik|ilo|io|is|it|iu|ja|jbo|jv|ka|kaa|kab|kbd|kg|ki|kj|kk|kl|km|kn|ko|koi|kr|krc|ks|ksh|ku|kv|kw|ky|la|lad|lb|lbe|lg|li|lij|lmo|ln|lo|lt|ltg|lv|map-bms|mdf|mg|mh|mhr|mi|mk|ml|mn|mo|mr|mrj|ms|mt|mus|mwl|my|myv|mzn|na|nah|nap|nds|nds-nl|ne|new|ng|nl|nn|no|nov|nrm|nv|ny|oc|om|or|os|pa|pag|pam|pap|pcd|pdc|pfl|pi|pih|pl|pms|pnb|pnt|ps|pt|qu|rm|rmy|rn|ro|roa-rup|roa-tara|ru|rue|rw|sa|sah|sc|scn|sco|sd|se|sg|sh|si|simple|sk|sl|sm|sn|so|sq|sr|srn|ss|st|stq|su|sv|sw|szl|ta|te|tet|tg|th|ti|tk|tl|tn|to|tpi|tr|ts|tt|tum|tw|ty|udm|ug|uk|ur|uz|ve|vec|vi|vls|vo|wa|war|wo|wuu|xal|xh|yi|yo|za|zea|zh|zh-classical|zh-min-nan|zh-yue|zu';
pg.re.interwiki=RegExp('^'+pg.wiki.interwiki+':');
} else {
pg.wiki.interwiki=null;
pg.re.interwiki=RegExp('^$');
}
}
// return a regexp pattern matching all variants to write the given namespace
function nsRe(namespaceId) {
var imageNamespaceVariants = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=namespaceId) return;
_localizedNamespaceLc = upcaseFirst(_localizedNamespaceLc);
imageNamespaceVariants.push(mw.util.escapeRegExp(_localizedNamespaceLc).split(' ').join('[ _]'));
imageNamespaceVariants.push(mw.util.escapeRegExp(encodeURI(_localizedNamespaceLc)));
});
return '(?:' + imageNamespaceVariants.join('|') + ')';
}
function nsReImage() {
return nsRe(pg.nsImageId);
}
// ENDFILE: namespaces.js
// STARTFILE: selpop.js
//<NOLITE>
function getEditboxSelection() {
// see http://www.webgurusforum.com/8/12/0
var editbox;
try {
editbox=document.editform.wpTextbox1;
} catch (dang) { return; }
// IE, Opera
if (document.selection) { return document.selection.createRange().text; }
// Mozilla
var selStart = editbox.selectionStart;
var selEnd = editbox.selectionEnd;
return (editbox.value).substring(selStart, selEnd);
}
function doSelectionPopup() {
// popup if the selection looks like [[foo|anything afterwards at all
// or [[foo|bar]]text without ']]'
// or [[foo|bar]]
var sel=getEditboxSelection();
var open=sel.indexOf('[[');
var pipe=sel.indexOf('|');
var close=sel.indexOf(']]');
if (open == -1 || ( pipe == -1 && close == -1) ) { return; }
if (pipe != -1 && open > pipe || close != -1 && open > close) { return; }
if (getValueOf('popupOnEditSelection')=='boxpreview') {
return doSeparateSelectionPopup(sel);
}
var article=new Title(sel.substring(open+2, (pipe < 0) ? close : pipe)).urlString();
if (close > 0 && sel.substring(close+2).indexOf('[[') >= 0) {
return;
}
var a=document.createElement('a');
a.href=pg.wiki.titlebase + article;
mouseOverWikiLink2(a);
if (a.navpopup) {
a.navpopup.addHook(function(){runStopPopupTimer(a.navpopup);}, 'unhide', 'after');
}
}
function doSeparateSelectionPopup(str) {
var div=document.getElementById('selectionPreview');
if (!div) {
div = document.createElement('div');
div.id='selectionPreview';
try {
var box=document.editform.wpTextbox1;
box.parentNode.insertBefore(div, box);
} catch (error) {
return;
}
}
div.innerHTML=wiki2html(str);
div.ranSetupTooltipsAlready = false;
popTipsSoonFn('selectionPreview')();
}
//</NOLITE>
// ENDFILE: selpop.js
// STARTFILE: navpopup.js
/**
@fileoverview Defines two classes: {@link Navpopup} and {@link Mousetracker}.
<code>Navpopup</code> describes popups: when they appear, where, what
they look like and so on.
<code>Mousetracker</code> "captures" the mouse using
<code>document.onmousemove</code>.
*/
/**
Creates a new Mousetracker.
@constructor
@class The Mousetracker class. This monitors mouse movements and manages associated hooks.
*/
function Mousetracker() {
/**
Interval to regularly run the hooks anyway, in milliseconds.
@type Integer
*/
this.loopDelay=400;
/**
Timer for the loop.
@type Timer
*/
this.timer=null;
/**
Flag - are we switched on?
@type Boolean
*/
this.active=false;
/**
Flag - are we probably inaccurate, i.e. not reflecting the actual mouse position?
*/
this.dirty=true;
/**
Array of hook functions.
@private
@type Array
*/
this.hooks=[];
}
/**
Adds a hook, to be called when we get events.
@param {Function} f A function which is called as
<code>f(x,y)</code>. It should return <code>true</code> when it
wants to be removed, and <code>false</code> otherwise.
*/
Mousetracker.prototype.addHook = function (f) {
this.hooks.push(f);
};
/**
Runs hooks, passing them the x
and y coords of the mouse. Hook functions that return true are
passed to {@link Mousetracker#removeHooks} for removal.
@private
*/
Mousetracker.prototype.runHooks = function () {
if (!this.hooks || !this.hooks.length) { return; }
//log('Mousetracker.runHooks; we got some hooks to run');
var remove=false;
var removeObj={};
// this method gets called a LOT -
// pre-cache some variables
var x=this.x, y=this.y, len = this.hooks.length;
for (var i=0; i<len; ++i) {
//~ run the hook function, and remove it if it returns true
if (this.hooks[i](x, y)===true) {
remove=true;
removeObj[i]=true;
}
}
if (remove) { this.removeHooks(removeObj); }
};
/**
Removes hooks.
@private
@param {Object} removeObj An object whose keys are the index
numbers of functions for removal, with values that evaluate to true
*/
Mousetracker.prototype.removeHooks = function(removeObj) {
var newHooks=[];
var len = this.hooks.length;
for (var i=0; i<len; ++i) {
if (! removeObj[i]) { newHooks.push(this.hooks[i]); }
}
this.hooks=newHooks;
};
/**
Event handler for mouse wiggles.
We simply grab the event, set x and y and run the hooks.
This makes the cpu all hot and bothered :-(
@private
@param {Event} e Mousemove event
*/
Mousetracker.prototype.track=function (e) {
//~ Apparently this is needed in IE.
e = e || window.event;
var x, y;
if (e) {
if (e.pageX) { x=e.pageX; y=e.pageY; }
else if (typeof e.clientX!='undefined') {
var left, top, docElt = document.documentElement;
if (docElt) { left=docElt.scrollLeft; }
left = left || document.body.scrollLeft || document.scrollLeft || 0;
if (docElt) { top=docElt.scrollTop; }
top = top || document.body.scrollTop || document.scrollTop || 0;
x=e.clientX + left;
y=e.clientY + top;
} else { return; }
this.setPosition(x,y);
}
};
/**
Sets the x and y coordinates stored and takes appropriate action,
running hooks as appropriate.
@param {Integer} x, y Screen coordinates to set
*/
Mousetracker.prototype.setPosition=function(x,y) {
this.x = x;
this.y = y;
if (this.dirty || this.hooks.length === 0) { this.dirty=false; return; }
if (typeof this.lastHook_x != 'number') { this.lastHook_x = -100; this.lastHook_y=-100; }
var diff = (this.lastHook_x - x)*(this.lastHook_y - y);
diff = (diff >= 0) ? diff : -diff;
if ( diff > 1 ) {
this.lastHook_x=x;
this.lastHook_y=y;
if (this.dirty) { this.dirty = false; }
else { this.runHooks(); }
}
};
/**
Sets things in motion, unless they are already that is, registering an event handler on <code>document.onmousemove</code>.
A half-hearted attempt is made to preserve the old event handler if there is one.
*/
Mousetracker.prototype.enable = function () {
if (this.active) { return; }
this.active=true;
//~ Save the current handler for mousemove events. This isn't too
//~ robust, of course.
this.savedHandler=document.onmousemove;
//~ Gotta save @tt{this} again for the closure, and use apply for
//~ the member function.
var savedThis=this;
document.onmousemove=function (e) {savedThis.track.apply(savedThis, [e]);};
if (this.loopDelay) { this.timer = setInterval(function() { //log('loop delay in mousetracker is working');
savedThis.runHooks();}, this.loopDelay); }
};
/**
Disables the tracker, removing the event handler.
*/
Mousetracker.prototype.disable = function () {
if (!this.active) { return; }
if (typeof this.savedHandler === 'function') {
document.onmousemove=this.savedHandler;
} else { delete document.onmousemove; }
if (this.timer) { clearInterval(this.timer); }
this.active=false;
};
/**
Creates a new Navpopup.
Gets a UID for the popup and
@param init Contructor object. If <code>init.draggable</code> is true or absent, the popup becomes draggable.
@constructor
@class The Navpopup class. This generates popup hints, and does some management of them.
*/
function Navpopup(/*init*/) {
//alert('new Navpopup(init)');
/** UID for each Navpopup instance.
Read-only.
@type integer
*/
this.uid=Navpopup.uid++;
/**
Read-only flag for current visibility of the popup.
@type boolean
@private
*/
this.visible=false;
/** Flag to be set when we want to cancel a previous request to
show the popup in a little while.
@private
@type boolean
*/
this.noshow=false;
/** Categorised list of hooks.
@see #runHooks
@see #addHook
@private
@type Object
*/
this.hooks={
'create': [],
'unhide': [],
'hide': []
};
/** list of unique IDs of hook functions, to avoid duplicates
@private
*/
this.hookIds={};
/** List of downloads associated with the popup.
@private
@type Array
*/
this.downloads=[];
/** Number of uncompleted downloads.
@type integer
*/
this.pending=null;
/** Tolerance in pixels when detecting whether the mouse has left the popup.
@type integer
*/
this.fuzz=5;
/** Flag to toggle running {@link #limitHorizontalPosition} to regulate the popup's position.
@type boolean
*/
this.constrained=true;
/** The popup width in pixels.
@private
@type integer
*/
this.width=0;
/** The popup width in pixels.
@private
@type integer
*/
this.height=0;
/** The main content DIV element.
@type HTMLDivElement
*/
this.mainDiv=null;
this.createMainDiv();
// if (!init || typeof init.popups_draggable=='undefined' || init.popups_draggable) {
// this.makeDraggable(true);
// }
}
/**
A UID for each Navpopup. This constructor property is just a counter.
@type integer
@private
*/
Navpopup.uid=0;
/**
Retrieves the {@link #visible} attribute, indicating whether the popup is currently visible.
@type boolean
*/
Navpopup.prototype.isVisible=function() {
return this.visible;
};
/**
Repositions popup using CSS style.
@private
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@param {boolean} noLimitHor Don't call {@link #limitHorizontalPosition}
*/
Navpopup.prototype.reposition= function (x,y, noLimitHor) {
log ('reposition('+x+','+y+','+noLimitHor+')');
if (typeof x != 'undefined' && x !== null) { this.left=x; }
if (typeof y != 'undefined' && y !== null) { this.top=y; }
if (typeof this.left != 'undefined' && typeof this.top != 'undefined') {
this.mainDiv.style.left=this.left + 'px';
this.mainDiv.style.top=this.top + 'px';
}
if (!noLimitHor) { this.limitHorizontalPosition(); }
//console.log('navpop'+this.uid+' - (left,top)=(' + this.left + ',' + this.top + '), css=('
//+ this.mainDiv.style.left + ',' + this.mainDiv.style.top + ')');
};
/**
Prevents popups from being in silly locations. Hopefully.
Should not be run if {@link #constrained} is true.
@private
*/
Navpopup.prototype.limitHorizontalPosition=function() {
if (!this.constrained || this.tooWide) { return; }
this.updateDimensions();
var x=this.left;
var w=this.width;
var cWidth=document.body.clientWidth;
// log('limitHorizontalPosition: x='+x+
// ', this.left=' + this.left +
// ', this.width=' + this.width +
// ', cWidth=' + cWidth);
if ( (x+w) >= cWidth ||
( x > 0 &&
this.maxWidth &&
this.width < this.maxWidth &&
this.height > this.width &&
x > cWidth - this.maxWidth ) ) {
// This is a very nasty hack. There has to be a better way!
// We find the "natural" width of the div by positioning it at the far left
// then reset it so that it should be flush right (well, nearly)
this.mainDiv.style.left='-10000px';
this.mainDiv.style.width = this.maxWidth + 'px';
var naturalWidth=parseInt(this.mainDiv.offsetWidth, 10);
var newLeft=cWidth - naturalWidth - 1;
if (newLeft < 0) { newLeft = 0; this.tooWide=true; } // still unstable for really wide popups?
log ('limitHorizontalPosition: moving to ('+newLeft + ','+ this.top+');' + ' naturalWidth=' + naturalWidth + ', clientWidth=' + cWidth);
this.reposition(newLeft, null, true);
}
};
/**
Counter indicating the z-order of the "highest" popup.
We start the z-index at 1000 so that popups are above everything
else on the screen.
@private
@type integer
*/
Navpopup.highest=1000;
/**
Brings popup to the top of the z-order.
We increment the {@link #highest} property of the contructor here.
@private
*/
Navpopup.prototype.raise = function () {
this.mainDiv.style.zIndex=Navpopup.highest + 1;
++Navpopup.highest;
};
/**
Shows the popup provided {@link #noshow} is not true.
Updates the position, brings the popup to the top of the z-order and unhides it.
*/
Navpopup.prototype.show = function () {
//document.title+='s';
if (this.noshow) { return; }
//document.title+='t';
this.reposition();
this.raise();
this.unhide();
};
/**
Checks to see if the mouse pointer has
stabilised (checking every <code>time</code>/2 milliseconds) and runs the
{@link #show} method if it has.
@param {integer} time The minimum time (ms) before the popup may be shown.
*/
Navpopup.prototype.showSoonIfStable = function (time) {
log ('showSoonIfStable, time='+time);
if (this.visible) { return; }
this.noshow = false;
//~ initialize these variables so that we never run @tt{show} after
//~ just half the time
this.stable_x = -10000; this.stable_y = -10000;
var stableShow = function() {
log('stableShow called');
var new_x = Navpopup.tracker.x, new_y = Navpopup.tracker.y;
var dx = savedThis.stable_x - new_x, dy = savedThis.stable_y - new_y;
var fuzz2 = 0; // savedThis.fuzz * savedThis.fuzz;
//document.title += '[' + [savedThis.stable_x,new_x, savedThis.stable_y,new_y, dx, dy, fuzz2].join(',') + '] ';
if ( dx * dx <= fuzz2 && dy * dy <= fuzz2 ) {
log ('mouse is stable');
clearInterval(savedThis.showSoonStableTimer);
savedThis.reposition.apply(savedThis, [new_x + 2, new_y + 2]);
savedThis.show.apply(savedThis, []);
savedThis.limitHorizontalPosition.apply(savedThis, []);
return;
}
savedThis.stable_x = new_x; savedThis.stable_y = new_y;
};
var savedThis = this;
this.showSoonStableTimer = setInterval(stableShow, time/2);
};
/**
Sets the {@link #noshow} flag and hides the popup. This should be called
when the mouse leaves the link before
(or after) it's actually been displayed.
*/
Navpopup.prototype.banish = function () {
log ('banish called');
// hide and prevent showing with showSoon in the future
this.noshow=true;
if (this.showSoonStableTimer) {
log('clearing showSoonStableTimer');
clearInterval(this.showSoonStableTimer);
}
this.hide();
};
/**
Runs hooks added with {@link #addHook}.
@private
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
*/
Navpopup.prototype.runHooks = function (key, when) {
if (!this.hooks[key]) { return; }
var keyHooks=this.hooks[key];
var len=keyHooks.length;
for (var i=0; i< len; ++i) {
if (keyHooks[i] && keyHooks[i].when == when) {
if (keyHooks[i].hook.apply(this, [])) {
// remove the hook
if (keyHooks[i].hookId) {
delete this.hookIds[keyHooks[i].hookId];
}
keyHooks[i]=null;
}
}
}
};
/**
Adds a hook to the popup. Hook functions are run with <code>this</code> set to refer to the Navpopup instance, and no arguments.
@param {Function} hook The hook function. Functions that return true are deleted.
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
@param {String} uid A truthy string identifying the hook function; if it matches another hook in this position, it won't be added again.
*/
Navpopup.prototype.addHook = function ( hook, key, when, uid ) {
when = when || 'after';
if (!this.hooks[key]) { return; }
// if uid is specified, don't add duplicates
var hookId=null;
if (uid) {
hookId=[key,when,uid].join('|');
if (this.hookIds[hookId]) {
return;
}
this.hookIds[hookId]=true;
}
this.hooks[key].push( {hook: hook, when: when, hookId: hookId} );
};
/**
Creates the main DIV element, which contains all the actual popup content.
Runs hooks with key 'create'.
@private
*/
Navpopup.prototype.createMainDiv = function () {
if (this.mainDiv) { return; }
this.runHooks('create', 'before');
var mainDiv=document.createElement('div');
var savedThis=this;
mainDiv.onclick=function(e) {savedThis.onclickHandler(e);};
mainDiv.className=(this.className) ? this.className : 'navpopup_maindiv';
mainDiv.id=mainDiv.className + this.uid;
mainDiv.style.position='absolute';
mainDiv.style.minWidth = '350px';
mainDiv.style.display='none';
mainDiv.className='navpopup';
// easy access to javascript object through DOM functions
mainDiv.navpopup=this;
this.mainDiv=mainDiv;
document.body.appendChild(mainDiv);
this.runHooks('create', 'after');
};
/**
Calls the {@link #raise} method.
@private
*/
Navpopup.prototype.onclickHandler=function(/*e*/) {
this.raise();
};
/**
Makes the popup draggable, using a {@link Drag} object.
@private
*/
Navpopup.prototype.makeDraggable=function(handleName) {
if (!this.mainDiv) { this.createMainDiv(); }
var drag=new Drag();
if (!handleName) {
drag.startCondition=function(e) {
try { if (!e.shiftKey) { return false; } } catch (err) { return false; }
return true;
};
}
var dragHandle;
if (handleName) dragHandle = document.getElementById(handleName);
if (!dragHandle) dragHandle = this.mainDiv;
var np=this;
drag.endHook=function(x,y) {
Navpopup.tracker.dirty=true;
np.reposition(x,y);
};
drag.init(dragHandle,this.mainDiv);
};
/** Hides the popup using CSS. Runs hooks with key 'hide'.
Sets {@link #visible} appropriately. {@link #banish} should be called externally instead of this method.
@private
*/
Navpopup.prototype.hide = function () {
this.runHooks('hide', 'before');
this.abortDownloads();
if (typeof this.visible != 'undefined' && this.visible) {
this.mainDiv.style.display='none';
this.visible=false;
}
this.runHooks('hide', 'after');
};
/** Shows the popup using CSS. Runs hooks with key 'unhide'.
Sets {@link #visible} appropriately. {@link #show} should be called externally instead of this method.
@private
*/
Navpopup.prototype.unhide = function () {
this.runHooks('unhide', 'before');
if (typeof this.visible != 'undefined' && !this.visible) {
this.mainDiv.style.display='inline';
this.visible=true;
}
this.runHooks('unhide', 'after');
};
/**
Sets the <code>innerHTML</code> attribute of the main div containing the popup content.
@param {String} html The HTML to set.
*/
Navpopup.prototype.setInnerHTML = function (html) {
this.mainDiv.innerHTML = html;
};
/**
Updates the {@link #width} and {@link #height} attributes with the CSS properties.
@private
*/
Navpopup.prototype.updateDimensions = function () {
this.width=parseInt(this.mainDiv.offsetWidth, 10);
this.height=parseInt(this.mainDiv.offsetHeight, 10);
};
/**
Checks if the point (x,y) is within {@link #fuzz} of the
{@link #mainDiv}.
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@type boolean
*/
Navpopup.prototype.isWithin = function(x,y) {
//~ If we're not even visible, no point should be considered as
//~ being within the popup.
if (!this.visible) { return false; }
this.updateDimensions();
var fuzz=this.fuzz || 0;
//~ Use a simple box metric here.
return (x+fuzz >= this.left && x-fuzz <= this.left + this.width &&
y+fuzz >= this.top && y-fuzz <= this.top + this.height);
};
/**
Adds a download to {@link #downloads}.
@param {Downloader} download
*/
Navpopup.prototype.addDownload=function(download) {
if (!download) { return; }
this.downloads.push(download);
};
/**
Aborts the downloads listed in {@link #downloads}.
@see Downloader#abort
*/
Navpopup.prototype.abortDownloads=function() {
for(var i=0; i<this.downloads.length; ++i) {
var d=this.downloads[i];
if (d && d.abort) { d.abort(); }
}
this.downloads=[];
};
/**
A {@link Mousetracker} instance which is a property of the constructor (pseudo-global).
*/
Navpopup.tracker=new Mousetracker();
// ENDFILE: navpopup.js
// STARTFILE: diff.js
//<NOLITE>
/*
* Javascript Diff Algorithm
* By John Resig (http://ejohn.org/) and [[:en:User:Lupin]]
*
* More Info:
* http://ejohn.org/projects/javascript-diff-algorithm/
*/
function delFmt(x) {
if (!x.length) { return ''; }
return "<del class='popupDiff'>" + x.join('') +"</del>";
}
function insFmt(x) {
if (!x.length) { return ''; }
return "<ins class='popupDiff'>" + x.join('') +"</ins>";
}
function countCrossings(a, b, i, eject) {
// count the crossings on the edge starting at b[i]
if (!b[i].row && b[i].row !== 0) { return -1; }
var count=0;
for (var j=0; j<a.length; ++j) {
if (!a[j].row && a[j].row !== 0) { continue; }
if ( (j-b[i].row)*(i-a[j].row) > 0) {
if(eject) { return true; }
count++;
}
}
return count;
}
function shortenDiffString(str, context) {
var re=RegExp('(<del[\\s\\S]*?</del>|<ins[\\s\\S]*?</ins>)');
var splitted=str.parenSplit(re);
var ret=[''];
for (var i=0; i<splitted.length; i+=2) {
if (splitted[i].length < 2*context) {
ret[ret.length-1] += splitted[i];
if (i+1<splitted.length) { ret[ret.length-1] += splitted[i+1]; }
continue;
}
else {
if (i > 0) { ret[ret.length-1] += splitted[i].substring(0,context); }
if (i+1 < splitted.length) {
ret.push(splitted[i].substring(splitted[i].length-context) +
splitted[i+1]);
}
}
}
while (ret.length > 0 && !ret[0]) { ret = ret.slice(1); }
return ret;
}
function diffString( o, n, simpleSplit ) {
var splitRe=RegExp('([[]{2}|[\\]]{2}|[{]{2,3}|[}]{2,3}|[|]|=|<|>|[*:]+|\\s|\\b)');
// We need to split the strings o and n first, and entify() the parts
// individually, so that the HTML entities are never cut apart. (AxelBoldt)
var out, i, oSplitted, nSplitted;
if (simpleSplit) {
oSplitted=o.split(/\b/);
nSplitted=n.split(/\b/);
} else {
oSplitted=o.parenSplit(splitRe);
nSplitted=n.parenSplit(splitRe);
}
for (i=0; i<oSplitted.length; ++i) {oSplitted[i]=oSplitted[i].entify();}
for (i=0; i<nSplitted.length; ++i) {nSplitted[i]=nSplitted[i].entify();}
out = diff (oSplitted, nSplitted);
var str = "";
var acc=[]; // accumulator for prettier output
// crossing pairings -- eg 'A B' vs 'B A' -- cause problems, so let's iron them out
// this doesn't always do things optimally but it should be fast enough
var maxOutputPair=0;
for (i=0; i<out.n.length; ++i) {
if ( out.n[i].paired ) {
if( maxOutputPair > out.n[i].row ) {
// tangle - delete pairing
out.o[ out.n[i].row ]=out.o[ out.n[i].row ].text;
out.n[i]=out.n[i].text;
}
if (maxOutputPair < out.n[i].row) { maxOutputPair = out.n[i].row; }
}
}
// output the stuff preceding the first paired old line
for (i=0; i<out.o.length && !out.o[i].paired; ++i) { acc.push( out.o[i] ); }
str += delFmt(acc); acc=[];
// main loop
for ( i = 0; i < out.n.length; ++i ) {
// output unpaired new "lines"
while ( i < out.n.length && !out.n[i].paired ) { acc.push( out.n[i++] ); }
str += insFmt(acc); acc=[];
if ( i < out.n.length ) { // this new "line" is paired with the (out.n[i].row)th old "line"
str += out.n[i].text;
// output unpaired old rows starting after this new line's partner
var m = out.n[i].row + 1;
while ( m < out.o.length && !out.o[m].paired ) { acc.push ( out.o[m++] ); }
str += delFmt(acc); acc=[];
}
}
return str;
}
// see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Object
// FIXME: use obj.hasOwnProperty instead of this kludge!
var jsReservedProperties=RegExp('^(constructor|prototype|__((define|lookup)[GS]etter)__' +
'|eval|hasOwnProperty|propertyIsEnumerable' +
'|to(Source|String|LocaleString)|(un)?watch|valueOf)$');
function diffBugAlert(word) {
if (!diffBugAlert.list[word]) {
diffBugAlert.list[word]=1;
alert('Bad word: '+word+'\n\nPlease report this bug.');
}
}
diffBugAlert.list={};
function makeDiffHashtable(src) {
var ret={};
for ( var i = 0; i < src.length; i++ ) {
if ( jsReservedProperties.test(src[i]) ) { src[i] += '<!-- -->'; }
if ( !ret[ src[i] ] ) { ret[ src[i] ] = []; }
try { ret[ src[i] ].push( i ); } catch (err) { diffBugAlert(src[i]); }
}
return ret;
}
function diff( o, n ) {
// pass 1: make hashtable ns with new rows as keys
var ns = makeDiffHashtable(n);
// pass 2: make hashtable os with old rows as keys
var os = makeDiffHashtable(o);
// pass 3: pair unique new rows and matching unique old rows
var i;
for ( i in ns ) {
if ( ns[i].length == 1 && os[i] && os[i].length == 1 ) {
n[ ns[i][0] ] = { text: n[ ns[i][0] ], row: os[i][0], paired: true };
o[ os[i][0] ] = { text: o[ os[i][0] ], row: ns[i][0], paired: true };
}
}
// pass 4: pair matching rows immediately following paired rows (not necessarily unique)
for ( i = 0; i < n.length - 1; i++ ) {
if ( n[i].paired && ! n[i+1].paired && n[i].row + 1 < o.length && ! o[ n[i].row + 1 ].paired &&
n[i+1] == o[ n[i].row + 1 ] ) {
n[i+1] = { text: n[i+1], row: n[i].row + 1, paired: true };
o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1, paired: true };
}
}
// pass 5: pair matching rows immediately preceding paired rows (not necessarily unique)
for ( i = n.length - 1; i > 0; i-- ) {
if ( n[i].paired && ! n[i-1].paired && n[i].row > 0 && ! o[ n[i].row - 1 ].paired &&
n[i-1] == o[ n[i].row - 1 ] ) {
n[i-1] = { text: n[i-1], row: n[i].row - 1, paired: true };
o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1, paired: true };
}
}
return { o: o, n: n };
}
//</NOLITE>
// ENDFILE: diff.js
// STARTFILE: init.js
function setSiteInfo() {
if (window.popupLocalDebug) {
pg.wiki.hostname = 'en.wikipedia.org';
} else {
pg.wiki.hostname = location.hostname; // use in preference to location.hostname for flexibility (?)
}
pg.wiki.wikimedia=RegExp('(wiki([pm]edia|source|books|news|quote|versity)|wiktionary|mediawiki)[.]org').test(pg.wiki.hostname);
pg.wiki.wikia=RegExp('[.]wikia[.]com$', 'i').test(pg.wiki.hostname);
pg.wiki.isLocal=RegExp('^localhost').test(pg.wiki.hostname);
pg.wiki.commons=( pg.wiki.wikimedia && pg.wiki.hostname != 'commons.wikimedia.org') ? 'commons.wikimedia.org' : null;
pg.wiki.lang = mw.config.get('wgContentLanguage');
var port = location.port ? ':' + location.port : '';
pg.wiki.sitebase = pg.wiki.hostname + port;
}
function setUserInfo() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
list: 'users',
ususers: mw.config.get('wgUserName'),
usprop: 'rights'
};
pg.user.canReview = false;
if (getValueOf('popupReview')) {
api.get(params).done(function(data){
var rights = data.query.users[0].rights;
pg.user.canReview = rights.indexOf('review') !== -1; // TODO: Should it be a getValueOf('ReviewRight') ?
});
}
}
function setTitleBase() {
var protocol = ( window.popupLocalDebug ? 'http:' : location.protocol );
pg.wiki.articlePath = mw.config.get('wgArticlePath').replace(/\/\$1/, ""); // as in http://some.thing.com/wiki/Article
pg.wiki.botInterfacePath = mw.config.get('wgScript');
pg.wiki.APIPath = mw.config.get('wgScriptPath') +"/api.php";
// default mediawiki setting is paths like http://some.thing.com/articlePath/index.php?title=foo
var titletail = pg.wiki.botInterfacePath + '?title=';
//var titletail2 = joinPath([pg.wiki.botInterfacePath, 'wiki.phtml?title=']);
// other sites may need to add code here to set titletail depending on how their urls work
pg.wiki.titlebase = protocol + '//' + pg.wiki.sitebase + titletail;
//pg.wiki.titlebase2 = protocol + '//' + joinPath([pg.wiki.sitebase, titletail2]);
pg.wiki.wikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.botInterfacePath;
pg.wiki.apiwikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.APIPath;
pg.wiki.articlebase = protocol + '//' + pg.wiki.sitebase + pg.wiki.articlePath;
pg.wiki.commonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.botInterfacePath;
pg.wiki.apicommonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.APIPath;
pg.re.basenames = RegExp( '^(' +
map( literalizeRegex, [ pg.wiki.titlebase, //pg.wiki.titlebase2,
pg.wiki.articlebase ]).join('|') + ')' );
}
//////////////////////////////////////////////////
// Global regexps
function setMainRegex() {
var reStart='[^:]*://';
var preTitles = literalizeRegex( mw.config.get('wgScriptPath') ) + '/(?:index[.]php|wiki[.]phtml)[?]title=';
preTitles += '|' + literalizeRegex( pg.wiki.articlePath + '/' );
var reEnd='(' + preTitles + ')([^&?#]*)[^#]*(?:#(.+))?';
pg.re.main = RegExp(reStart + literalizeRegex(pg.wiki.sitebase) + reEnd);
}
function setRegexps() {
// TODO: We shoud use an api call to get the aliases for special pages, now it does not work for non-English wikipedias:
// E.g., https://ru.wikipedia.org/w/api.php?action=query&meta=siteinfo&siprop=specialpagealiases&formatversion=2
setMainRegex();
var sp=nsRe(pg.nsSpecialId);
pg.re.urlNoPopup=RegExp('((title=|/)' + sp + '(?:%3A|:)|section=[0-9]|^#$)') ;
pg.re.contribs =RegExp('(title=|/)' + sp + '(?:%3A|:)Contributions' + '(&target=|/|/' + nsRe(pg.nsUserId)+':)(.*)') ;
pg.re.email =RegExp('(title=|/)' + sp + '(?:%3A|:)EmailUser' + '(&target=|/|/(?:' + nsRe(pg.nsUserId)+':)?)(.*)') ;
pg.re.backlinks =RegExp('(title=|/)' + sp + '(?:%3A|:)WhatLinksHere' + '(&target=|/)([^&]*)');
pg.re.specialdiff=RegExp('/' + sp + '(?:%3A|:)Diff/([^?#]*)');
//<NOLITE>
var im=nsReImage();
// note: tries to get images in infobox templates too, e.g. movie pages, album pages etc
// (^|\[\[)image: *([^|\]]*[^|\] ]) *
// (^|\[\[)image: *([^|\]]*[^|\] ])([^0-9\]]*([0-9]+) *px)?
// $4 = 120 as in 120px
pg.re.image = RegExp('(^|\\[\\[)' + im + ': *([^|\\]]*[^|\\] ])' +
'([^0-9\\]]*([0-9]+) *px)?|(?:\\n *[|]?|[|]) *' +
'(' + getValueOf('popupImageVarsRegexp') + ')' +
' *= *(?:\\[\\[ *)?(?:' + im + ':)?' +
'([^|]*?)(?:\\]\\])? *[|]? *\\n', 'img') ;
pg.re.imageBracketCount = 6;
pg.re.category = RegExp('\\[\\[' +nsRe(pg.nsCategoryId) +
': *([^|\\]]*[^|\\] ]) *', 'i');
pg.re.categoryBracketCount = 1;
pg.re.ipUser=RegExp('^' +
// IPv6
'(?::(?::|(?::[0-9A-Fa-f]{1,4}){1,7})|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,6}::|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){7})' +
// IPv4
'|(((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}' +
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9]))$');
pg.re.stub= RegExp(getValueOf('popupStubRegexp'), 'im');
pg.re.disambig=RegExp(getValueOf('popupDabRegexp'), 'im');
//</NOLITE>
// FIXME replace with general parameter parsing function, this is daft
pg.re.oldid=RegExp('[?&]oldid=([^&]*)');
pg.re.diff=RegExp('[?&]diff=([^&]*)');
}
//////////////////////////////////////////////////
// miscellany
function setupCache() {
// page caching
pg.cache.pages = [];
}
function setMisc() {
pg.current.link=null;
pg.current.links=[];
pg.current.linksHash={};
setupCache();
pg.timer.checkPopupPosition=null;
pg.counter.loop=0;
// ids change with each popup: popupImage0, popupImage1 etc
pg.idNumber=0;
// for myDecodeURI
pg.misc.decodeExtras = [
{from: '%2C', to: ',' },
{from: '_', to: ' ' },
{from: '%24', to: '$'},
{from: '%26', to: '&' } // no ,
];
pg.misc.userAgent = 'Navigation popups/1.0 (' + mw.config.get( 'wgServerName' ) +')';
}
// We need a callback since this might end up asynchronous because of
// the mw.loader.using() call.
function setupPopups( callback ) {
if ( setupPopups.completed ) {
if ( typeof callback === 'function' ) {
callback();
}
return;
}
// These dependencies are also enforced from the gadget,
// but not everyone loads this as a gadget, so double check
mw.loader.using( ['mediawiki.util', 'mediawiki.user', 'user.options'] ).then( function() {
// NB translatable strings should be set up first (strings.js)
// basics
setupDebugging();
setSiteInfo();
setTitleBase();
setOptions(); // see options.js
setUserInfo();
// namespaces etc
setNamespaces();
setInterwiki();
// regexps
setRegexps();
setRedirs();
// other stuff
setMisc();
setupLivePreview();
// main deal here
setupTooltips();
log('In setupPopups(), just called setupTooltips()');
Navpopup.tracker.enable();
setupPopups.completed = true;
if ( typeof callback === 'function' ) {
callback();
}
});
}
// ENDFILE: init.js
// STARTFILE: navlinks.js
//<NOLITE>
//////////////////////////////////////////////////
// navlinks... let the fun begin
//
function defaultNavlinkSpec() {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
if (getValueOf('popupLastEditLink')) {
str += '*<<lastEdit|shortcut=/>>|<<lastContrib>>|<<sinceMe>>if(oldid){|<<oldEdit>>|<<diffCur>>}';
}
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>*<<userlog|shortcut=L|log>>';
str+='if(ipuser){*<<arin>>}if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>|<<blocklog|log>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' + editstr + '}';
var historystr='<<history|shortcut=h>>|<<editors|shortcut=E|>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str+='<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>*<<move|shortcut=m>>';
// admin links
str += 'if(admin){<br><<unprotect|unprotectShort>>|<<protect|shortcut=p>>|<<protectlog|log>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>|<<deletelog|log>>}';
return str;
}
function navLinksHTML (article, hint, params) { //oldid, rcid) {
var str = '<span class="popupNavLinks">' + defaultNavlinkSpec() + '</span>';
// BAM
return navlinkStringToHTML(str, article, params);
}
function expandConditionalNavlinkString(s,article,z,recursionCount) {
var oldid=z.oldid, rcid=z.rcid, diff=z.diff;
// nested conditionals (up to 10 deep) are ok, hopefully! (work from the inside out)
if (typeof recursionCount!=typeof 0) { recursionCount=0; }
var conditionalSplitRegex=RegExp(
//(1 if \\( (2 2) \\) {(3 3)} (4 else {(5 5)} 4)1)
'(;?\\s*if\\s*\\(\\s*([\\w]*)\\s*\\)\\s*\\{([^{}]*)\\}(\\s*else\\s*\\{([^{}]*?)\\}|))', 'i');
var splitted=s.parenSplit(conditionalSplitRegex);
// $1: whole conditional
// $2: test condition
// $3: true expansion
// $4: else clause (possibly empty)
// $5: false expansion (possibly null)
var numParens=5;
var ret = splitted[0];
for (var i=1; i<splitted.length; i=i+numParens+1) {
var testString=splitted[i+2-1];
var trueString=splitted[i+3-1];
var falseString=splitted[i+5-1];
if (typeof falseString=='undefined' || !falseString) { falseString=''; }
var testResult=null;
switch (testString) {
case 'user':
testResult=(article.userName())?true:false;
break;
case 'talk':
testResult=(article.talkPage())?false:true; // talkPage converts _articles_ to talkPages
break;
case 'admin':
testResult=getValueOf('popupAdminLinks')?true:false;
break;
case 'oldid':
testResult=(typeof oldid != 'undefined' && oldid)?true:false;
break;
case 'rcid':
testResult=(typeof rcid != 'undefined' && rcid)?true:false;
break;
case 'ipuser':
testResult=(article.isIpUser())?true:false;
break;
case 'mainspace_en':
testResult=isInMainNamespace(article) &&
pg.wiki.hostname=='en.wikipedia.org';
break;
case 'wikimedia':
testResult=(pg.wiki.wikimedia) ? true : false;
break;
case 'diff':
testResult=(typeof diff != 'undefined' && diff)?true:false;
break;
}
switch(testResult) {
case null: ret+=splitted[i]; break;
case true: ret+=trueString; break;
case false: ret+=falseString; break;
}
// append non-conditional string
ret += splitted[i+numParens];
}
if (conditionalSplitRegex.test(ret) && recursionCount < 10) {
return expandConditionalNavlinkString(ret,article,z,recursionCount+1);
}
return ret;
}
function navlinkStringToArray(s, article, params) {
s=expandConditionalNavlinkString(s,article,params);
var splitted=s.parenSplit(RegExp('<<(.*?)>>'));
var ret=[];
for (var i=0; i<splitted.length; ++i) {
if (i%2) { // i odd, so s is a tag
var t=new navlinkTag();
var ss=splitted[i].split('|');
t.id=ss[0];
for (var j=1; j<ss.length; ++j) {
var sss=ss[j].split('=');
if (sss.length>1) {
t[sss[0]]=sss[1];
}
else { // no assignment (no "="), so treat this as a title (overwriting the last one)
t.text=popupString(sss[0]);
}
}
t.article=article;
var oldid=params.oldid, rcid=params.rcid, diff=params.diff;
if (typeof oldid !== 'undefined' && oldid !== null) { t.oldid=oldid; }
if (typeof rcid !== 'undefined' && rcid !== null) { t.rcid=rcid; }
if (typeof diff !== 'undefined' && diff !== null) { t.diff=diff; }
if (!t.text && t.id !== 'mainlink') { t.text=popupString(t.id); }
ret.push(t);
}
else { // plain HTML
ret.push(splitted[i]);
}
}
return ret;
}
function navlinkSubstituteHTML(s) {
return s.split('*').join(getValueOf('popupNavLinkSeparator'))
.split('<menurow>').join('<li class="popup_menu_row">')
.split('</menurow>').join('</li>')
.split('<menu>').join('<ul class="popup_menu">')
.split('</menu>').join('</ul>');
}
function navlinkDepth(magic,s) {
return s.split('<' + magic + '>').length - s.split('</' + magic + '>').length;
}
// navlinkString: * becomes the separator
// <<foo|bar=baz|fubar>> becomes a foo-link with attribute bar='baz'
// and visible text 'fubar'
// if(test){...} and if(test){...}else{...} work too (nested ok)
function navlinkStringToHTML(s,article,params) {
//limitAlert(navlinkStringToHTML, 5, 'navlinkStringToHTML\n' + article + '\n' + (typeof article));
var p=navlinkStringToArray(s,article,params);
var html='';
var menudepth = 0; // nested menus not currently allowed, but doesn't do any harm to code for it
var menurowdepth = 0;
for (var i=0; i<p.length; ++i) {
if (typeof p[i] == typeof '') {
html+=navlinkSubstituteHTML(p[i]);
menudepth += navlinkDepth('menu', p[i]);
menurowdepth += navlinkDepth('menurow', p[i]);
// if (menudepth === 0) {
// tagType='span';
// } else if (menurowdepth === 0) {
// tagType='li';
// } else {
// tagType = null;
// }
} else if (typeof p[i].type != 'undefined' && p[i].type=='navlinkTag') {
if (menudepth > 0 && menurowdepth === 0) {
html += '<li class="popup_menu_item">' + p[i].html() + '</li>';
} else {
html+=p[i].html();
}
}
}
return html;
}
function navlinkTag() {
this.type='navlinkTag';
}
navlinkTag.prototype.html=function () {
this.getNewWin();
this.getPrintFunction();
var html='';
var opening, closing;
var tagType='span';
if (!tagType) {
opening = ''; closing = '';
} else {
opening = '<' + tagType + ' class="popup_' + this.id + '">';
closing = '</' + tagType + '>';
}
if (typeof this.print!='function') {
errlog ('Oh dear - invalid print function for a navlinkTag, id='+this.id);
} else {
html=this.print(this);
if (typeof html != typeof '') {html='';}
else if (typeof this.shortcut!='undefined') html=addPopupShortcut(html, this.shortcut);
}
return opening + html + closing;
};
navlinkTag.prototype.getNewWin=function() {
getValueOf('popupLinksNewWindow');
if (typeof pg.option.popupLinksNewWindow[this.id] === 'undefined') { this.newWin=null; }
this.newWin=pg.option.popupLinksNewWindow[this.id];
};
navlinkTag.prototype.getPrintFunction=function() { //think about this some more
// this.id and this.article should already be defined
if (typeof this.id!=typeof '' || typeof this.article!=typeof {} ) { return; }
this.noPopup=1;
switch (this.id) {
case 'contribs': case 'history': case 'whatLinksHere':
case 'userPage': case 'monobook': case 'userTalk':
case 'talk': case 'article': case 'lastEdit':
this.noPopup=null;
}
switch (this.id) {
case 'email': case 'contribs': case 'block': case 'unblock':
case 'userlog': case 'userSpace': case 'deletedContribs':
this.article=this.article.userName();
}
switch (this.id) {
case 'userTalk': case 'newUserTalk': case 'editUserTalk':
case 'userPage': case 'monobook': case 'editMonobook': case 'blocklog':
this.article=this.article.userName(true);
/* fall through */
case 'pagelog': case 'deletelog': case 'protectlog':
delete this.oldid;
}
if (this.id=='editMonobook' || this.id=='monobook') { this.article.append('/monobook.js'); }
if (this.id != 'mainlink') {
// FIXME anchor handling should be done differently with Title object
this.article=this.article.removeAnchor();
// if (typeof this.text=='undefined') this.text=popupString(this.id);
}
switch (this.id) {
case 'undelete': this.print=specialLink; this.specialpage='Undelete'; this.sep='/'; break;
case 'whatLinksHere': this.print=specialLink; this.specialpage='Whatlinkshere'; break;
case 'relatedChanges': this.print=specialLink; this.specialpage='Recentchangeslinked'; break;
case 'move': this.print=specialLink; this.specialpage='Movepage'; break;
case 'contribs': this.print=specialLink; this.specialpage='Contributions'; break;
case 'deletedContribs':this.print=specialLink; this.specialpage='Deletedcontributions'; break;
case 'email': this.print=specialLink; this.specialpage='EmailUser'; this.sep='/'; break;
case 'block': this.print=specialLink; this.specialpage='Blockip'; this.sep='&ip='; break;
case 'unblock': this.print=specialLink; this.specialpage='Ipblocklist'; this.sep='&action=unblock&ip='; break;
case 'userlog': this.print=specialLink; this.specialpage='Log'; this.sep='&user='; break;
case 'blocklog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=block&page='; break;
case 'pagelog': this.print=specialLink; this.specialpage='Log'; this.sep='&page='; break;
case 'protectlog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=protect&page='; break;
case 'deletelog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=delete&page='; break;
case 'userSpace': this.print=specialLink; this.specialpage='PrefixIndex'; this.sep='&namespace=2&prefix='; break;
case 'search': this.print=specialLink; this.specialpage='Search'; this.sep='&fulltext=Search&search='; break;
case 'thank': this.print=specialLink; this.specialpage='Thanks'; this.sep='/'; this.article.value = this.diff; break;
case 'unwatch': case 'watch':
this.print=magicWatchLink; this.action=this.id+'&autowatchlist=1&autoimpl=' + popupString('autoedit_version') + '&actoken='+autoClickToken(); break;
case 'history': case 'historyfeed':
case 'unprotect': case 'protect':
this.print=wikiLink; this.action=this.id; break;
case 'delete':
this.print=wikiLink; this.action='delete';
if (this.article.namespaceId()==pg.nsImageId) {
var img=this.article.stripNamespace();
this.action+='&image='+img;
}
break;
case 'markpatrolled':
case 'edit': // editOld should keep the oldid, but edit should not.
delete this.oldid;
/* fall through */
case 'view': case 'purge': case 'render':
this.print=wikiLink;
this.action=this.id; break;
case 'raw':
this.print=wikiLink; this.action='raw'; break;
case 'new':
this.print=wikiLink; this.action='edit§ion=new'; break;
case 'mainlink':
if (typeof this.text=='undefined') { this.text=this.article.toString().entify(); }
if (getValueOf('popupSimplifyMainLink') && isInStrippableNamespace(this.article)) {
// only show the /subpage part of the title text
var s=this.text.split('/'); this.text=s[s.length-1];
if (this.text==='' && s.length > 1) { this.text=s[s.length-2]; }
}
this.print=titledWikiLink;
if (typeof this.title==='undefined' && pg.current.link && typeof pg.current.link.href !== 'undefined') {
this.title=safeDecodeURI((pg.current.link.originalTitle)?pg.current.link.originalTitle:this.article);
if (typeof this.oldid !== 'undefined' && this.oldid) {
this.title=tprintf('Revision %s of %s', [this.oldid, this.title]);
}
}
this.action='view'; break;
case 'userPage':
case 'article':
case 'monobook':
case 'editMonobook':
case 'editArticle':
delete this.oldid;
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.article=this.article.articleFromTalkOrArticle();
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.print=wikiLink;
if (this.id.indexOf('edit')===0) {
this.action='edit';
} else { this.action='view';}
break;
case 'userTalk':
case 'talk':
this.article=this.article.talkPage();
delete this.oldid;
this.print=wikiLink;
this.action='view'; break;
case 'arin':
this.print=arinLink; break;
case 'count':
this.print=editCounterLink; break;
case 'google':
this.print=googleLink; break;
case 'editors':
this.print=editorListLink; break;
case 'globalsearch':
this.print=globalSearchLink; break;
case 'lastEdit':
this.print=titledDiffLink;
this.title=popupString('Show the last edit');
this.from='prev'; this.to='cur'; break;
case 'oldEdit':
this.print=titledDiffLink;
this.title=popupString('Show the edit made to get revision') + ' ' + this.oldid;
this.from='prev'; this.to=this.oldid; break;
case 'editOld':
this.print=wikiLink; this.action='edit'; break;
case 'undo':
this.print=wikiLink; this.action='edit&undo='; break;
case 'revert':
this.print=wikiLink; this.action='revert'; break;
case 'nullEdit':
this.print=wikiLink; this.action='nullEdit'; break;
case 'diffCur':
this.print=titledDiffLink;
this.title=tprintf('Show changes since revision %s', [this.oldid]);
this.from=this.oldid; this.to='cur'; break;
case 'editUserTalk':
case 'editTalk':
delete this.oldid;
this.article=this.article.talkPage();
this.action='edit'; this.print=wikiLink; break;
case 'newUserTalk':
case 'newTalk':
this.article=this.article.talkPage();
this.action='edit§ion=new'; this.print=wikiLink; break;
case 'lastContrib':
case 'sinceMe':
this.print=magicHistoryLink;
break;
case 'togglePreviews':
this.text=popupString(pg.option.simplePopups ? 'enable previews' : 'disable previews');
/* fall through */
case 'disablePopups': case 'purgePopups':
this.print=popupMenuLink;
break;
default:
this.print=function () {return 'Unknown navlink type: '+this.id+'';};
}
};
//
// end navlinks
//////////////////////////////////////////////////
//</NOLITE>
// ENDFILE: navlinks.js
// STARTFILE: shortcutkeys.js
//<NOLITE>
function popupHandleKeypress(evt) {
var keyCode = window.event ? window.event.keyCode : ( evt.keyCode ? evt.keyCode : evt.which);
if (!keyCode || !pg.current.link || !pg.current.link.navpopup) { return; }
if (keyCode==27) { // escape
killPopup();
return false; // swallow keypress
}
var letter=String.fromCharCode(keyCode);
var links=pg.current.link.navpopup.mainDiv.getElementsByTagName('A');
var startLink=0;
var i,j;
if (popupHandleKeypress.lastPopupLinkSelected) {
for (i=0; i<links.length; ++i) {
if (links[i]==popupHandleKeypress.lastPopupLinkSelected) { startLink=i; }
}
}
for (j=0; j<links.length; ++j) {
i=(startLink + j + 1) % links.length;
if (links[i].getAttribute('popupkey')==letter) {
if (evt && evt.preventDefault) evt.preventDefault();
links[i].focus();
popupHandleKeypress.lastPopupLinkSelected=links[i];
return false; // swallow keypress
}
}
// pass keypress on
if (document.oldPopupOnkeypress) { return document.oldPopupOnkeypress(evt); }
return true;
}
function addPopupShortcuts() {
if (document.onkeypress!=popupHandleKeypress) {
document.oldPopupOnkeypress=document.onkeypress;
}
document.onkeypress=popupHandleKeypress;
}
function rmPopupShortcuts() {
popupHandleKeypress.lastPopupLinkSelected=null;
try {
if (document.oldPopupOnkeypress && document.oldPopupOnkeypress==popupHandleKeypress) {
// panic
document.onkeypress=null; //function () {};
return;
}
document.onkeypress=document.oldPopupOnkeypress;
} catch (nasties) { /* IE goes here */ }
}
function addLinkProperty(html, property) {
// take "<a href=...>...</a> and add a property
// not sophisticated at all, easily broken
var i=html.indexOf('>');
if (i<0) { return html; }
return html.substring(0,i) + ' ' + property + html.substring(i);
}
function addPopupShortcut(html, key) {
if (!getValueOf('popupShortcutKeys')) { return html; }
var ret= addLinkProperty(html, 'popupkey="'+key+'"');
if (key==' ') { key=popupString('spacebar'); }
return ret.replace(RegExp('^(.*?)(title=")(.*?)(".*)$', 'i'),'$1$2$3 ['+key+']$4');
}
//</NOLITE>
// ENDFILE: shortcutkeys.js
// STARTFILE: diffpreview.js
//<NOLITE>
//lets jump through hoops to find the rev ids we need to retrieve
function loadDiff(article, oldid, diff, navpop) {
navpop.diffData={ oldRev: {}, newRev: {} };
mw.loader.using( 'mediawiki.api' ).then( function() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'compare',
prop: 'ids|title'
};
if(article.title){
params.fromtitle = article.title;
}
switch (diff) {
case 'cur':
switch ( oldid ) {
case null:
case '':
case 'prev':
// this can only work if we have the title
// cur -> prev
params.torelative = 'prev';
break;
default:
params.fromrev = oldid;
params.torelative = 'cur';
break;
}
break;
case 'prev':
if( oldid ) {
params.fromrev = oldid;
} else {
params.fromtitle;
}
params.torelative = 'prev';
break;
case 'next':
params.fromrev = oldid || 0;
params.torelative = 'next';
break;
default:
params.fromrev = oldid || 0;
params.torev = diff || 0;
break;
}
api.get( params ).then( function( data ) {
navpop.diffData.oldRev.revid = data.compare.fromrevid;
navpop.diffData.newRev.revid = data.compare.torevid;
addReviewLink(navpop, 'popupMiscTools');
var go = function() {
pendingNavpopTask(navpop);
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
url += 'revids=' + navpop.diffData.oldRev.revid + '|' + navpop.diffData.newRev.revid;
url += '&prop=revisions&rvprop=ids|timestamp|content';
getPageWithCaching(url, doneDiff, navpop);
return true; // remove hook once run
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_DIFFS'); }
} );
} );
}
// Put a "mark patrolled" link to an element target
// TODO: Allow patrol a revision, as well as a diff
function addReviewLink (navpop, target) {
if (! pg.user.canReview) return;
// If 'newRev' is older than 'oldRev' than it could be confusing, so we do not show the review link.
if (navpop.diffData.newRev.revid <= navpop.diffData.oldRev.revid) return;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
prop: 'info|flagged',
revids: navpop.diffData.oldRev.revid,
formatversion : 2
};
api.get (params).then(function(data){
var stable_revid = data.query.pages[0].flagged && data.query.pages[0].flagged.stable_revid || 0;
// The diff can be reviewed if the old version is the last reviewed version
// TODO: Other possible conditions that we may want to implement instead of this one:
// * old version is patrolled and the new version is not patrolled
// * old version is patrolled and the new version is more recent than the last reviewed version
if (stable_revid == navpop.diffData.oldRev.revid) {
var a = document.createElement('a');
a.innerHTML = popupString('mark patrolled');
a.title=popupString('markpatrolledHint');
a.onclick = function() {
var params = {
action: 'review',
revid: navpop.diffData.newRev.revid,
comment: tprintf('defaultpopupReviewedSummary', [navpop.diffData.oldRev.revid, navpop.diffData.newRev.revid])
};
api.postWithToken('csrf',params).done(function(){
a.style.display = "none";
// TODO: Update current page and other already constructed popups
} ).fail(function(){
alert(popupString('Could not marked this edit as patrolled'));
});
};
setPopupHTML(a, target, navpop.idNumber,null,true);
}
});
}
function doneDiff(download) {
if (!download.owner || !download.owner.diffData) { return; }
var navpop=download.owner;
completedNavpopTask(navpop);
var pages, revisions=[];
try{
// Process the downloads
pages = getJsObj(download.data).query.pages;
for(var i=0; i < pages.length; i++ ) {
revisions = revisions.concat(pages[i].revisions);
}
for(i=0; i< revisions.length; i++){
if(revisions[i].revid == navpop.diffData.oldRev.revid) {
navpop.diffData.oldRev.revision = revisions[i];
} else if (revisions[i].revid == navpop.diffData.newRev.revid) {
navpop.diffData.newRev.revision = revisions[i];
}
}
} catch(someError) {
errlog( 'Could not get diff' );
}
insertDiff(navpop);
}
function rmBoringLines(a,b,context) {
if (typeof context == 'undefined') { context=2; }
// this is fairly slow... i think it's quicker than doing a word-based diff from the off, though
var aa=[], aaa=[];
var bb=[], bbb=[];
var i, j;
// first, gather all disconnected nodes in a and all crossing nodes in a and b
for (i=0; i<a.length; ++i ) {
if(!a[i].paired) { aa[i]=1; }
else if (countCrossings(b,a,i, true)) {
aa[i]=1;
bb[ a[i].row ] = 1;
}
}
// pick up remaining disconnected nodes in b
for (i=0; i<b.length; ++i ) {
if (bb[i]==1) { continue; }
if(!b[i].paired) { bb[i]=1; }
}
// another pass to gather context: we want the neighbours of included nodes which are not yet included
// we have to add in partners of these nodes, but we don't want to add context for *those* nodes in the next pass
for (i=0; i<b.length; ++i) {
if ( bb[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(b.length, i+context); ++j) {
if ( !bb[j] ) { bb[j] = 1; aa[ b[j].row ] = 0.5; }
}
}
}
for (i=0; i<a.length; ++i) {
if ( aa[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(a.length, i+context); ++j) {
if ( !aa[j] ) { aa[j] = 1; bb[ a[j].row ] = 0.5; }
}
}
}
for (i=0; i<bb.length; ++i) {
if (bb[i] > 0) { // it's a row we need
if (b[i].paired) { bbb.push(b[i].text); } // joined; partner should be in aa
else {
bbb.push(b[i]);
}
}
}
for (i=0; i<aa.length; ++i) {
if (aa[i] > 0) { // it's a row we need
if (a[i].paired) { aaa.push(a[i].text); } // joined; partner should be in aa
else {
aaa.push(a[i]);
}
}
}
return { a: aaa, b: bbb};
}
function stripOuterCommonLines(a,b,context) {
var i=0;
while (i<a.length && i < b.length && a[i]==b[i]) { ++i; }
var j=a.length-1; var k=b.length-1;
while ( j>=0 && k>=0 && a[j]==b[k] ) { --j; --k; }
return { a: a.slice(Math.max(0,i - 1 - context), Math.min(a.length+1, j + context+1)),
b: b.slice(Math.max(0,i - 1 - context), Math.min(b.length+1, k + context+1)) };
}
function insertDiff(navpop) {
// for speed reasons, we first do a line-based diff, discard stuff that seems boring, then do a word-based diff
// FIXME: sometimes this gives misleading diffs as distant chunks are squashed together
var oldlines = navpop.diffData.oldRev.revision.content.split('\n');
var newlines = navpop.diffData.newRev.revision.content.split('\n');
var inner=stripOuterCommonLines(oldlines,newlines,getValueOf('popupDiffContextLines'));
oldlines=inner.a; newlines=inner.b;
var truncated=false;
getValueOf('popupDiffMaxLines');
if (oldlines.length > pg.option.popupDiffMaxLines || newlines.length > pg.option.popupDiffMaxLines) {
// truncate
truncated=true;
inner=stripOuterCommonLines(oldlines.slice(0,pg.option.popupDiffMaxLines),
newlines.slice(0,pg.option.popupDiffMaxLines),
pg.option.popupDiffContextLines);
oldlines=inner.a; newlines=inner.b;
}
var lineDiff=diff(oldlines, newlines);
var lines2=rmBoringLines(lineDiff.o, lineDiff.n);
var oldlines2=lines2.a; var newlines2=lines2.b;
var simpleSplit = !String.prototype.parenSplit.isNative;
var html='<hr />';
if (getValueOf('popupDiffDates')) {
html += diffDatesTable(navpop);
html += '<hr />';
}
html += shortenDiffString(
diffString(oldlines2.join('\n'), newlines2.join('\n'), simpleSplit),
getValueOf('popupDiffContextCharacters') ).join('<hr />');
setPopupTipsAndHTML(html.split('\n').join('<br>') +
(truncated ? '<hr /><b>'+popupString('Diff truncated for performance reasons')+'</b>' : '') ,
'popupPreview', navpop.idNumber);
}
function diffDatesTable( navpop ) {
var html='<table class="popup_diff_dates">';
html += diffDatesTableRow( navpop.diffData.newRev.revision, tprintf('New revision'));
html += diffDatesTableRow( navpop.diffData.oldRev.revision, tprintf('Old revision'));
html += '</table>';
return html;
}
function diffDatesTableRow( revision, label ) {
var txt='';
var lastModifiedDate = new Date(revision.timestamp);
var datePrint=getValueOf('popupDiffDatePrinter');
if (typeof lastModifiedDate[datePrint] == 'function') {
var d2 = adjustDate(lastModifiedDate, getTimeOffset());
txt = dayFormat(d2, true) + ' ' + timeFormat(d2, true);
} else {
txt = tprintf('Invalid %s %s', ['popupDiffDatePrinter', datePrint]);
}
var revlink = generalLink({url: mw.config.get('wgScript') + '?oldid='+revision.revid,
text: label, title: label});
return simplePrintf('<tr><td>%s</td><td>%s</td></tr>', [ revlink, txt ]);
}
//</NOLITE>
// ENDFILE: diffpreview.js
// STARTFILE: links.js
//<NOLITE>
/////////////////////
// LINK GENERATION //
/////////////////////
// titledDiffLink --> titledWikiLink --> generalLink
// wikiLink --> titledWikiLink --> generalLink
// editCounterLink --> generalLink
// TODO Make these functions return Element objects, not just raw HTML strings.
function titledDiffLink(l) { // article, text, title, from, to) {
return titledWikiLink({article: l.article, action: l.to + '&oldid=' + l.from,
newWin: l.newWin,
noPopup: l.noPopup,
text: l.text, title: l.title,
/* hack: no oldid here */
actionName: 'diff'});
}
function wikiLink(l) {
//{article:article, action:action, text:text, oldid, newid}) {
if (! (typeof l.article == typeof {} &&
typeof l.action == typeof '' &&
typeof l.text==typeof '')) return null;
if (typeof l.oldid == 'undefined') { l.oldid=null; }
var savedOldid = l.oldid;
if (!/^(edit|view|revert|render)$|^raw/.test(l.action)) { l.oldid=null; }
var hint=popupString(l.action + 'Hint'); // revertHint etc etc etc
var oldidData=[l.oldid, safeDecodeURI(l.article)];
var revisionString = tprintf('revision %s of %s', oldidData);
log('revisionString='+revisionString);
switch (l.action) {
case 'edit§ion=new': hint = popupString('newSectionHint'); break;
case 'edit&undo=':
if (l.diff && l.diff != 'prev' && savedOldid ) {
l.action += l.diff + '&undoafter=' + savedOldid;
} else if (savedOldid) {
l.action += savedOldid;
}
hint = popupString('undoHint');
break;
case 'raw&ctype=text/css': hint=popupString('rawHint'); break;
case 'revert':
var p=parseParams(pg.current.link.href);
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=' + revertSummary(l.oldid, p.diff);
if (p.diff=='prev') {
l.action += '&direction=prev';
revisionString = tprintf('the revision prior to revision %s of %s', oldidData);
}
if (getValueOf('popupRevertSummaryPrompt')) { l.action += '&autosummaryprompt=true'; }
if (getValueOf('popupMinorReverts')) { l.action += '&autominor=true'; }
log('revisionString is now '+revisionString);
break;
case 'nullEdit':
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=null';
break;
case 'historyfeed':
l.action='history&feed=rss';
break;
case 'markpatrolled':
l.action='markpatrolled&rcid='+l.rcid;
}
if (hint) {
if (l.oldid) {
hint = simplePrintf(hint, [revisionString]);
}
else {
hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
}
}
else {
hint = safeDecodeURI(l.article + '&action=' + l.action) + (l.oldid) ? '&oldid='+l.oldid : '';
}
return titledWikiLink({article: l.article, action: l.action, text: l.text, newWin:l.newWin,
title: hint, oldid: l.oldid, noPopup: l.noPopup, onclick: l.onclick});
}
function revertSummary(oldid, diff) {
var ret='';
if (diff == 'prev') {
ret=getValueOf('popupQueriedRevertToPreviousSummary');
} else { ret = getValueOf('popupQueriedRevertSummary'); }
return ret + '&autorv=' + oldid;
}
function titledWikiLink(l) {
// possible properties of argument:
// article, action, text, title, oldid, actionName, className, noPopup
// oldid = null is fine here
// article and action are mandatory args
if (typeof l.article == 'undefined' || typeof l.action=='undefined') {
errlog('got undefined article or action in titledWikiLink');
return null;
}
var base = pg.wiki.titlebase + l.article.urlString();
var url=base;
if (typeof l.actionName=='undefined' || !l.actionName) { l.actionName='action'; }
// no need to add &action=view, and this confuses anchors
if (l.action != 'view') { url = base + '&' + l.actionName + '=' + l.action; }
if (typeof l.oldid!='undefined' && l.oldid) { url+='&oldid='+l.oldid; }
var cssClass=pg.misc.defaultNavlinkClassname;
if (typeof l.className!='undefined' && l.className) { cssClass=l.className; }
return generalNavLink({url: url, newWin: l.newWin,
title: (typeof l.title != 'undefined') ? l.title : null,
text: (typeof l.text!='undefined')?l.text:null,
className: cssClass, noPopup:l.noPopup, onclick:l.onclick});
}
pg.fn.getLastContrib = function getLastContrib(wikipage, newWin) {
getHistoryInfo(wikipage, function(x) {
processLastContribInfo(x, {page: wikipage, newWin: newWin});
});
};
function processLastContribInfo(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: an odd thing happened. Please retry.'); return; }
if(!info.firstNewEditor) {
alert(tprintf('Only found one editor: %s made %s edits', [info.edits[0].editor,info.edits.length]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+info.firstNewEditor.oldid;
displayUrl(newUrl, stuff.newWin);
}
pg.fn.getDiffSinceMyEdit = function getDiffSinceMyEdit(wikipage, newWin) {
getHistoryInfo(wikipage, function(x){
processDiffSinceMyEdit(x, {page: wikipage, newWin: newWin});
});
};
function processDiffSinceMyEdit(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: something fishy happened. Please try again.'); return; }
var friendlyName=stuff.page.split('_').join(' ');
if(!info.myLastEdit) {
alert(tprintf('Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
[info.userName, getValueOf('popupHistoryLimit'), friendlyName]));
return;
}
if(info.myLastEdit.index === 0) {
alert(tprintf("%s seems to be the last editor to the page %s", [info.userName, friendlyName]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+ info.myLastEdit.oldid;
displayUrl(newUrl, stuff.newWin);
}
function displayUrl(url, newWin){
if(newWin) { window.open(url); }
else { document.location=url; }
}
pg.fn.purgePopups = function purgePopups() {
processAllPopups(true);
setupCache(); // deletes all cached items (not browser cached, though...)
pg.option={};
abortAllDownloads();
};
function processAllPopups(nullify, banish) {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
if (!pg.current.links[i].navpopup) { continue; }
if (nullify || banish) pg.current.links[i].navpopup.banish();
pg.current.links[i].simpleNoMore=false;
if (nullify) pg.current.links[i].navpopup=null;
}
}
pg.fn.disablePopups = function disablePopups(){
processAllPopups(false, true);
setupTooltips(null, true);
};
pg.fn.togglePreviews = function togglePreviews() {
processAllPopups(true, true);
pg.option.simplePopups=!pg.option.simplePopups;
abortAllDownloads();
};
function magicWatchLink(l) {
//Yuck!! Would require a thorough redesign to add this as a click event though ...
l.onclick = simplePrintf( 'pg.fn.modifyWatchlist(\'%s\',\'%s\');return false;', [l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), this.id] );
return wikiLink(l);
}
pg.fn.modifyWatchlist = function modifyWatchlist(title, action) {
var reqData = {
'action': 'watch',
'formatversion': 2,
'titles': title,
'uselang': mw.config.get('wgUserLanguage')
};
if ( action === 'unwatch' ) reqData.unwatch = true;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
// Load the Addedwatchtext or Removedwatchtext message and show it
var mwTitle = mw.Title.newFromText( title );
var messageName;
if ( mwTitle && mwTitle.getNamespaceId() > 0 && mwTitle.getNamespaceId() % 2 === 1 ) {
messageName = action === 'watch' ? 'addedwatchtext-talk' : 'removedwatchtext-talk';
} else {
messageName = action === 'watch' ? 'addedwatchtext' : 'removedwatchtext';
}
$.when(
api.postWithToken( 'watch', reqData ),
mw.loader.using( [ 'mediawiki.api', 'mediawiki.jqueryMsg' ] ).then( function () {
return api.loadMessagesIfMissing( [ messageName ] );
} )
).done( function () {
mw.notify( mw.message( messageName, title ).parseDom() );
} );
};
function magicHistoryLink(l) {
// FIXME use onclick change href trick to sort this out instead of window.open
var jsUrl='', title='', onClick='';
switch(l.id) {
case 'lastContrib':
onClick=simplePrintf('pg.fn.getLastContrib(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('lastContribHint');
break;
case 'sinceMe':
onClick=simplePrintf('pg.fn.getDiffSinceMyEdit(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('sinceMeHint');
break;
}
jsUrl = 'javascript:' + onClick; // jshint ignore:line
onClick += ';return false;';
return generalNavLink({url: jsUrl, newWin: false, // can't have new windows with JS links, I think
title: title, text: l.text, noPopup: l.noPopup, onclick: onClick });
}
function popupMenuLink(l) {
var jsUrl=simplePrintf('javascript:pg.fn.%s()', [l.id]); // jshint ignore:line
var title=popupString(simplePrintf('%sHint', [l.id]));
var onClick=simplePrintf('pg.fn.%s();return false;', [l.id]);
return generalNavLink({url: jsUrl, newWin:false, title:title, text:l.text, noPopup:l.noPopup, onclick: onClick});
}
function specialLink(l) {
// properties: article, specialpage, text, sep
if (typeof l.specialpage=='undefined'||!l.specialpage) return null;
var base = pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId]+':'+l.specialpage;
if (typeof l.sep == 'undefined' || l.sep === null) l.sep='&target=';
var article=l.article.urlString({keepSpaces: l.specialpage=='Search'});
var hint=popupString(l.specialpage+'Hint');
switch (l.specialpage) {
case 'Log':
switch (l.sep) {
case '&user=': hint=popupString('userLogHint'); break;
case '&type=block&page=': hint=popupString('blockLogHint'); break;
case '&page=': hint=popupString('pageLogHint'); break;
case '&type=protect&page=': hint=popupString('protectLogHint'); break;
case '&type=delete&page=': hint=popupString('deleteLogHint'); break;
default: log('Unknown log type, sep=' + l.sep); hint='Missing hint (FIXME)';
}
break;
case 'PrefixIndex': article += '/'; break;
}
if (hint) hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
else hint = safeDecodeURI(l.specialpage+':'+l.article) ;
var url = base + l.sep + article;
return generalNavLink({url: url, title: hint, text: l.text, newWin:l.newWin, noPopup:l.noPopup});
}
function generalLink(l) {
// l.url, l.text, l.title, l.newWin, l.className, l.noPopup, l.onclick
if (typeof l.url=='undefined') return null;
// only quotation marks in the url can screw us up now... I think
var url=l.url.split('"').join('%22');
var ret='<a href="' + url + '"';
if (typeof l.title!='undefined' && l.title) { ret += ' title="' + pg.escapeQuotesHTML(l.title) + '"'; }
if (typeof l.onclick!='undefined' && l.onclick) { ret += ' onclick="' + pg.escapeQuotesHTML(l.onclick) + '"'; }
if (l.noPopup) { ret += ' noPopup=1'; }
var newWin;
if (typeof l.newWin=='undefined' || l.newWin === null) { newWin=getValueOf('popupNewWindows'); }
else { newWin=l.newWin; }
if (newWin) { ret += ' target="_blank"'; }
if (typeof l.className!='undefined'&&l.className) { ret+=' class="'+l.className+'"'; }
ret += '>';
if (typeof l.text==typeof '') { ret+= l.text; }
ret +='</a>';
return ret;
}
function appendParamsToLink(linkstr, params) {
var sp=linkstr.parenSplit(RegExp('(href="[^"]+?)"', 'i'));
if (sp.length<2) return null;
var ret=sp.shift() + sp.shift();
ret += '&' + params + '"';
ret += sp.join('');
return ret;
}
function changeLinkTargetLink(x) { // newTarget, text, hint, summary, clickButton, minor, title (optional), alsoChangeLabel {
if (x.newTarget) {
log ('changeLinkTargetLink: newTarget=' + x.newTarget);
}
if (x.oldTarget !== decodeURIComponent( x.oldTarget ) ) {
log ('This might be an input problem: ' + x.oldTarget );
}
// FIXME: first character of page title as well as namespace should be case insensitive
// eg [[category:X1]] and [[Category:X1]] are equivalent
// this'll break if charAt(0) is nasty
var cA = mw.util.escapeRegExp(x.oldTarget);
var chs = cA.charAt(0).toUpperCase();
chs='['+chs + chs.toLowerCase()+']';
var currentArticleRegexBit=chs+cA.substring(1);
currentArticleRegexBit=currentArticleRegexBit
.split(RegExp('(?:[_ ]+|%20)', 'g')).join('(?:[_ ]+|%20)')
.split('\\(').join('(?:%28|\\()')
.split('\\)').join('(?:%29|\\))'); // why does this need to match encoded strings ? links in the document ?
// leading and trailing space should be ignored, and anchor bits optional:
currentArticleRegexBit = '\\s*(' + currentArticleRegexBit + '(?:#[^\\[\\|]*)?)\\s*';
// e.g. Computer (archaic) -> \s*([Cc]omputer[_ ](?:%2528|\()archaic(?:%2528|\)))\s*
// autoedit=s~\[\[([Cc]ad)\]\]~[[Computer-aided%20design|$1]]~g;s~\[\[([Cc]AD)[|]~[[Computer-aided%20design|~g
var title=x.title || mw.config.get('wgPageName').split('_').join(' ');
var lk=titledWikiLink({article: new Title(title), newWin:x.newWin,
action: 'edit',
text: x.text,
title: x.hint,
className: 'popup_change_title_link'
});
var cmd='';
if (x.newTarget) {
// escape '&' and other nasties
var t = x.newTarget;
var s = mw.util.escapeRegExp(x.newTarget);
if (x.alsoChangeLabel) {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + ']]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
} else {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + '|$1]]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
}
} else {
cmd += 's~\\[\\['+currentArticleRegexBit+'\\]\\]~$1~g;';
cmd += 's~\\[\\['+currentArticleRegexBit+'[|](.*?)\\]\\]~$2~g';
}
// Build query
cmd = 'autoedit=' + encodeURIComponent ( cmd );
cmd += '&autoclick='+ encodeURIComponent( x.clickButton ) + '&actoken=' + encodeURIComponent( autoClickToken() );
cmd += ( x.minor === null ) ? '' : '&autominor='+ encodeURIComponent( x.minor );
cmd += ( x.watch === null ) ? '' : '&autowatch='+ encodeURIComponent( x.watch );
cmd += '&autosummary='+encodeURIComponent(x.summary);
cmd += '&autoimpl='+encodeURIComponent( popupString('autoedit_version') );
return appendParamsToLink(lk, cmd);
}
function redirLink(redirMatch, article) {
// NB redirMatch is in wikiText
var ret='';
if (getValueOf('popupAppendRedirNavLinks') && getValueOf('popupNavLinks')) {
ret += '<hr />';
if (getValueOf('popupFixRedirs') && typeof autoEdit != 'undefined' && autoEdit) {
ret += popupString('Redirects to: (Fix ');
log('redirLink: newTarget=' + redirMatch);
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target'),
hint: popupString('Fix this redirect, changing just the link target'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages')
}), 'R');
ret += popupString(' or ');
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target & label'),
hint: popupString('Fix this redirect, changing the link target and label'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages'),
alsoChangeLabel: true
}), 'R');
ret += popupString(')');
}
else ret += popupString('Redirects') + popupString(' to ');
return ret;
}
else return '<br> ' + popupString('Redirects') + popupString(' to ') +
titledWikiLink({article: new Title().fromWikiText(redirMatch), action: 'view', /* FIXME: newWin */
text: safeDecodeURI(redirMatch), title: popupString('Bypass redirect')});
}
function arinLink(l) {
if (!saneLinkCheck(l)) { return null; }
if ( ! l.article.isIpUser() || ! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
return generalNavLink({url:'http://ws.arin.net/cgi-bin/whois.pl?queryinput=' + encodeURIComponent(uN), newWin:l.newWin,
title: tprintf('Look up %s in ARIN whois database', [uN]),
text: l.text, noPopup:1});
}
function toolDbName(cookieStyle) {
var ret = mw.config.get('wgDBname');
if (!cookieStyle) { ret+= '_p'; }
return ret;
}
function saneLinkCheck(l) {
if (typeof l.article != typeof {} || typeof l.text != typeof '') { return false; }
return true;
}
function editCounterLink(l) {
if(!saneLinkCheck(l)) return null;
if (! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
var tool=getValueOf('popupEditCounterTool');
var url;
var defaultToolUrl='//tools.wmflabs.org/supercount/index.php?user=$1&project=$2.$3';
switch(tool) {
case 'custom':
url=simplePrintf(getValueOf('popupEditCounterUrl'), [ encodeURIComponent(uN), toolDbName() ]);
break;
case 'soxred': // no longer available
case 'kate': // no longer available
case 'interiot':// no longer available
/* fall through */
case 'supercount':
default:
var theWiki=pg.wiki.hostname.split('.');
url=simplePrintf(defaultToolUrl, [ encodeURIComponent(uN), theWiki[0], theWiki[1] ]);
}
return generalNavLink({url:url, title: tprintf('editCounterLinkHint', [uN]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function globalSearchLink(l) {
if(!saneLinkCheck(l)) return null;
var base='http://vs.aka-online.de/cgi-bin/globalwpsearch.pl?timeout=120&search=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + article, newWin:l.newWin,
title: tprintf('globalSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function googleLink(l) {
if(!saneLinkCheck(l)) return null;
var base='https://www.google.com/search?q=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + '%22' + article + '%22', newWin:l.newWin,
title: tprintf('googleSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function editorListLink(l) {
if(!saneLinkCheck(l)) return null;
var article= l.article.articleFromTalkPage() || l.article;
var url='https://xtools.wmflabs.org/articleinfo/' +
encodeURI( pg.wiki.hostname ) + '/' +
article.urlString() +
'?uselang=' + mw.config.get('wgUserLanguage');
return generalNavLink({url:url,
title: tprintf('editorListHint', [article]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function generalNavLink(l) {
l.className = (l.className === null) ? 'popupNavLink' : l.className;
return generalLink(l);
}
//////////////////////////////////////////////////
// magic history links
//
function getHistoryInfo(wikipage, whatNext) {
log('getHistoryInfo');
getHistory(wikipage, whatNext ? function(d){whatNext(processHistory(d));} : processHistory);
}
// FIXME eliminate pg.idNumber ... how? :-(
function getHistory(wikipage, onComplete) {
log('getHistory');
var url = pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&prop=revisions&titles=' +
new Title(wikipage).urlString() + '&rvlimit=' + getValueOf('popupHistoryLimit');
log('getHistory: url='+url);
return startDownload(url, pg.idNumber+'history', onComplete);
}
function processHistory(download) {
var jsobj = getJsObj(download.data);
try {
var revisions = anyChild(jsobj.query.pages).revisions;
var edits=[];
for (var i=0; i<revisions.length; ++i) {
edits.push({ oldid: revisions[i].revid, editor: revisions[i].user });
}
log('processed ' + edits.length + ' edits');
return finishProcessHistory( edits, mw.config.get('wgUserName') );
} catch (someError) {
log('Something went wrong with JSON business');
return finishProcessHistory([]);
}
}
function finishProcessHistory(edits, userName) {
var histInfo={};
histInfo.edits=edits;
histInfo.userName=userName;
for (var i=0; i<edits.length; ++i) {
if (typeof histInfo.myLastEdit === 'undefined' && userName && edits[i].editor==userName) {
histInfo.myLastEdit={index: i, oldid: edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
if (typeof histInfo.firstNewEditor === 'undefined' && edits[i].editor != edits[0].editor) {
histInfo.firstNewEditor={index:i, oldid:edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
}
//pg.misc.historyInfo=histInfo;
return histInfo;
}
//</NOLITE>
// ENDFILE: links.js
// STARTFILE: options.js
//////////////////////////////////////////////////
// options
// check for existing value, else use default
function defaultize(x) {
if (pg.option[x]===null || typeof pg.option[x]=='undefined') {
if (typeof window[x] != 'undefined' ) pg.option[x]=window[x];
else pg.option[x]=pg.optionDefault[x];
}
}
function newOption(x, def) {
pg.optionDefault[x]=def;
}
function setDefault(x, def) {
return newOption(x, def);
}
function getValueOf(varName) {
defaultize(varName);
return pg.option[varName];
}
/*eslint-disable */
function useDefaultOptions() { // for testing
for (var p in pg.optionDefault) {
pg.option[p]=pg.optionDefault[p];
if (typeof window[p]!='undefined') { delete window[p]; }
}
}
/*eslint-enable */
function setOptions() {
// user-settable parameters and defaults
var userIsSysop = false;
if ( mw.config.get('wgUserGroups') ) {
for ( var g = 0; g < mw.config.get('wgUserGroups').length; ++g ) {
if ( mw.config.get('wgUserGroups')[g] == "sysop" )
userIsSysop = true;
}
}
// Basic options
newOption('popupDelay', 0.5);
newOption('popupHideDelay', 0.5);
newOption('simplePopups', false);
newOption('popupStructure', 'shortmenus'); // see later - default for popupStructure is 'original' if simplePopups is true
newOption('popupActionsMenu', true);
newOption('popupSetupMenu', true);
newOption('popupAdminLinks', userIsSysop);
newOption('popupShortcutKeys', false);
newOption('popupHistoricalLinks', true);
newOption('popupOnlyArticleLinks', true);
newOption('removeTitles', true);
newOption('popupMaxWidth', 350);
newOption('popupSimplifyMainLink', true);
newOption('popupAppendRedirNavLinks', true);
newOption('popupTocLinks', false);
newOption('popupSubpopups', true);
newOption('popupDragHandle', false /* 'popupTopLinks'*/);
newOption('popupLazyPreviews', true);
newOption('popupLazyDownloads', true);
newOption('popupAllDabsStubs', false);
newOption('popupDebugging', false);
newOption('popupActiveNavlinks', true);
newOption('popupModifier', false); // ctrl, shift, alt or meta
newOption('popupModifierAction', 'enable'); // or 'disable'
newOption('popupDraggable', true);
newOption('popupReview', false);
//<NOLITE>
// images
newOption('popupImages', true);
newOption('imagePopupsForImages', true);
newOption('popupNeverGetThumbs', false);
//newOption('popupImagesToggleSize', true);
newOption('popupThumbAction', 'imagepage'); //'sizetoggle');
newOption('popupImageSize', 60);
newOption('popupImageSizeLarge', 200);
// redirs, dabs, reversion
newOption('popupFixRedirs', false);
newOption('popupRedirAutoClick', 'wpDiff');
newOption('popupFixDabs', false);
newOption('popupDabsAutoClick', 'wpDiff');
newOption('popupRevertSummaryPrompt', false);
newOption('popupMinorReverts', false);
newOption('popupRedlinkRemoval', false);
newOption('popupRedlinkAutoClick', 'wpDiff');
newOption('popupWatchDisambiggedPages', null);
newOption('popupWatchRedirredPages', null);
newOption('popupDabWiktionary', 'last');
// navlinks
newOption('popupNavLinks', true);
newOption('popupNavLinkSeparator', ' ⋅ ');
newOption('popupLastEditLink', true);
newOption('popupEditCounterTool', 'supercount');
newOption('popupEditCounterUrl', '');
//</NOLITE>
// previews etc
newOption('popupPreviews', true);
newOption('popupSummaryData', true);
newOption('popupMaxPreviewSentences', 9);
newOption('popupMaxPreviewCharacters', 900);
newOption('popupLastModified', true);
newOption('popupPreviewKillTemplates', true);
newOption('popupPreviewRawTemplates', true);
newOption('popupPreviewFirstParOnly', false);
newOption('popupPreviewCutHeadings', false);
newOption('popupPreviewButton', false);
newOption('popupPreviewButtonEvent', 'click');
//<NOLITE>
// diffs
newOption('popupPreviewDiffs', true);
newOption('popupDiffMaxLines', 100);
newOption('popupDiffContextLines', 2);
newOption('popupDiffContextCharacters', 40);
newOption('popupDiffDates', true);
newOption('popupDiffDatePrinter', 'toLocaleString');
// edit summaries. God, these are ugly.
newOption('popupReviewedSummary', popupString('defaultpopupReviewedSummary') );
newOption('popupFixDabsSummary', popupString('defaultpopupFixDabsSummary') );
newOption('popupExtendedRevertSummary', popupString('defaultpopupExtendedRevertSummary') );
newOption('popupRevertSummary', popupString('defaultpopupRevertSummary') );
newOption('popupRevertToPreviousSummary', popupString('defaultpopupRevertToPreviousSummary') );
newOption('popupQueriedRevertSummary', popupString('defaultpopupQueriedRevertSummary') );
newOption('popupQueriedRevertToPreviousSummary', popupString('defaultpopupQueriedRevertToPreviousSummary') );
newOption('popupFixRedirsSummary', popupString('defaultpopupFixRedirsSummary') );
newOption('popupRedlinkSummary', popupString('defaultpopupRedlinkSummary') );
newOption('popupRmDabLinkSummary', popupString('defaultpopupRmDabLinkSummary') );
//</NOLITE>
// misc
newOption('popupHistoryLimit', 50);
//<NOLITE>
newOption('popupFilters', [popupFilterStubDetect, popupFilterDisambigDetect,
popupFilterPageSize, popupFilterCountLinks,
popupFilterCountImages, popupFilterCountCategories,
popupFilterLastModified]);
newOption('extraPopupFilters', []);
newOption('popupOnEditSelection', 'cursor');
newOption('popupPreviewHistory', true);
newOption('popupImageLinks', true);
newOption('popupCategoryMembers', true);
newOption('popupUserInfo', true);
newOption('popupHistoryPreviewLimit', 25);
newOption('popupContribsPreviewLimit',25);
newOption('popupRevDelUrl', '//en.wikipedia.org/wiki/Wikipedia:Revision_deletion');
newOption('popupShowGender', true);
//</NOLITE>
// new windows
newOption('popupNewWindows', false);
newOption('popupLinksNewWindow', {'lastContrib': true, 'sinceMe': true});
// regexps
newOption('popupDabRegexp', '(\\{\\{\\s*disambig(?!uation needed)|disambig(uation|)\\s*\\}\\}|disamb\\s*\\}\\}|dab\\s*\\}\\})|\\{\\{\\s*(((geo|hn|road?|school|number)dis)|[234][lc][acw]|(road|ship)index)(\\s*[|][^}]*)?\\s*[}][}]|is a .*disambiguation.*page');
newOption('popupAnchorRegexp', 'anchors?'); //how to identify an anchors template
newOption('popupStubRegexp', '(sect)?stub[}][}]|This .*-related article is a .*stub');
newOption('popupImageVarsRegexp', 'image|image_(?:file|skyline|name|flag|seal)|cover|badge|logo');
}
// ENDFILE: options.js
// STARTFILE: strings.js
//<NOLITE>
//////////////////////////////////////////////////
// Translatable strings
//////////////////////////////////////////////////
//
// See instructions at
// https://en.wikipedia.org/wiki/Wikipedia:Tools/Navigation_popups/Translation
pg.string = {
/////////////////////////////////////
// summary data, searching etc.
/////////////////////////////////////
'article': 'article',
'category': 'category',
'categories': 'categories',
'image': 'image',
'images': 'images',
'stub': 'stub',
'section stub': 'section stub',
'Empty page': 'Empty page',
'kB': 'kB',
'bytes': 'bytes',
'day': 'day',
'days': 'days',
'hour': 'hour',
'hours': 'hours',
'minute': 'minute',
'minutes': 'minutes',
'second': 'second',
'seconds': 'seconds',
'week': 'week',
'weeks': 'weeks',
'search': 'search',
'SearchHint': 'Find English Wikipedia articles containing %s',
'web': 'web',
'global': 'global',
'globalSearchHint': 'Search across Wikipedias in different languages for %s',
'googleSearchHint': 'Google for %s',
/////////////////////////////////////
// article-related actions and info
// (some actions also apply to user pages)
/////////////////////////////////////
'actions': 'actions', ///// view articles and view talk
'popupsMenu': 'popups',
'togglePreviewsHint': 'Toggle preview generation in popups on this page',
'enable previews': 'enable previews',
'disable previews': 'disable previews',
'toggle previews': 'toggle previews',
'show preview': 'show preview',
'reset': 'reset',
'more...': 'more...',
'disable': 'disable popups',
'disablePopupsHint': 'Disable popups on this page. Reload page to re-enable.',
'historyfeedHint': 'RSS feed of recent changes to this page',
'purgePopupsHint': 'Reset popups, clearing all cached popup data.',
'PopupsHint': 'Reset popups, clearing all cached popup data.',
'spacebar': 'space',
'view': 'view',
'view article': 'view article',
'viewHint': 'Go to %s',
'talk': 'talk',
'talk page': 'talk page',
'this revision': 'this revision',
'revision %s of %s': 'revision %s of %s',
'Revision %s of %s': 'Revision %s of %s',
'the revision prior to revision %s of %s': 'the revision prior to revision %s of %s',
'Toggle image size': 'Click to toggle image size',
'del': 'del', ///// delete, protect, move
'delete': 'delete',
'deleteHint': 'Delete %s',
'undeleteShort': 'un',
'UndeleteHint': 'Show the deletion history for %s',
'protect': 'protect',
'protectHint': 'Restrict editing rights to %s',
'unprotectShort': 'un',
'unprotectHint': 'Allow %s to be edited by anyone again',
'send thanks': 'send thanks',
'ThanksHint': 'Send a thank you notification to this user',
'move': 'move',
'move page': 'move page',
'MovepageHint': 'Change the title of %s',
'edit': 'edit', ///// edit articles and talk
'edit article': 'edit article',
'editHint': 'Change the content of %s',
'edit talk': 'edit talk',
'new': 'new',
'new topic': 'new topic',
'newSectionHint': 'Start a new section on %s',
'null edit': 'null edit',
'nullEditHint': 'Submit an edit to %s, making no changes ',
'hist': 'hist', ///// history, diffs, editors, related
'history': 'history',
'historyHint': 'List the changes made to %s',
'last': 'prev', // For labelling the previous revision in history pages; the key is "last" for backwards compatibility
'lastEdit': 'lastEdit',
'mark patrolled': 'mark patrolled',
'markpatrolledHint': 'Mark this edit as patrolled',
'Could not marked this edit as patrolled': 'Could not marked this edit as patrolled',
'show last edit': 'most recent edit',
'Show the last edit': 'Show the effects of the most recent change',
'lastContrib': 'lastContrib',
'last set of edits': 'latest edits',
'lastContribHint': 'Show the net effect of changes made by the last editor',
'cur': 'cur',
'diffCur': 'diffCur',
'Show changes since revision %s': 'Show changes since revision %s',
'%s old': '%s old', // as in 4 weeks old
'oldEdit': 'oldEdit',
'purge': 'purge',
'purgeHint': 'Demand a fresh copy of %s',
'raw': 'source',
'rawHint': 'Download the source of %s',
'render': 'simple',
'renderHint': 'Show a plain HTML version of %s',
'Show the edit made to get revision': 'Show the edit made to get revision',
'sinceMe': 'sinceMe',
'changes since mine': 'diff my edit',
'sinceMeHint': 'Show changes since my last edit',
'Couldn\'t find an edit by %s\nin the last %s edits to\n%s': 'Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
'eds': 'eds',
'editors': 'editors',
'editorListHint': 'List the users who have edited %s',
'related': 'related',
'relatedChanges': 'relatedChanges',
'related changes': 'related changes',
'RecentchangeslinkedHint': 'Show changes in articles related to %s',
'editOld': 'editOld', ///// edit old version, or revert
'rv': 'rv',
'revert': 'revert',
'revertHint': 'Revert to %s',
'defaultpopupReviewedSummary': 'Accepted by reviewing the [[Special:diff/%s/%s|difference]] between this version and previously accepted version using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRedlinkSummary': 'Removing link to empty page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixDabsSummary': 'Disambiguate [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixRedirsSummary': 'Redirect bypass from [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupExtendedRevertSummary': 'Revert to revision dated %s by %s, oldid %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertToPreviousSummary': 'Revert to the revision prior to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertSummary': 'Revert to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertToPreviousSummary': 'Revert to the revision prior to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertSummary': 'Revert to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRmDabLinkSummary': 'Remove link to dab page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'Redirects': 'Redirects', // as in Redirects to ...
' to ': ' to ', // as in Redirects to ...
'Bypass redirect': 'Bypass redirect',
'Fix this redirect': 'Fix this redirect',
'disambig': 'disambig', ///// add or remove dab etc.
'disambigHint': 'Disambiguate this link to [[%s]]',
'Click to disambiguate this link to:': 'Click to disambiguate this link to:',
'remove this link': 'remove this link',
'remove all links to this page from this article': 'remove all links to this page from this article',
'remove all links to this disambig page from this article': 'remove all links to this disambig page from this article',
'mainlink': 'mainlink', ///// links, watch, unwatch
'wikiLink': 'wikiLink',
'wikiLinks': 'wikiLinks',
'links here': 'links here',
'whatLinksHere': 'whatLinksHere',
'what links here': 'what links here',
'WhatlinkshereHint': 'List the pages that are hyperlinked to %s',
'unwatchShort': 'un',
'watchThingy': 'watch', // called watchThingy because {}.watch is a function
'watchHint': 'Add %s to my watchlist',
'unwatchHint': 'Remove %s from my watchlist',
'Only found one editor: %s made %s edits': 'Only found one editor: %s made %s edits',
'%s seems to be the last editor to the page %s': '%s seems to be the last editor to the page %s',
'rss': 'rss',
/////////////////////////////////////
// diff previews
/////////////////////////////////////
'Diff truncated for performance reasons': 'Diff truncated for performance reasons',
'Old revision': 'Old revision',
'New revision': 'New revision',
'Something went wrong :-(': 'Something went wrong :-(',
'Empty revision, maybe non-existent': 'Empty revision, maybe non-existent',
'Unknown date': 'Unknown date',
/////////////////////////////////////
// other special previews
/////////////////////////////////////
'Empty category': 'Empty category',
'Category members (%s shown)': 'Category members (%s shown)',
'No image links found': 'No image links found',
'File links': 'File links',
'No image found': 'No image found',
'Image from Commons': 'Image from Commons',
'Description page': 'Description page',
'Alt text:': 'Alt text:',
'revdel':'Hidden revision',
/////////////////////////////////////
// user-related actions and info
/////////////////////////////////////
'user': 'user', ///// user page, talk, email, space
'user page': 'user page',
'user talk': 'user talk',
'edit user talk': 'edit user talk',
'leave comment': 'leave comment',
'email': 'email',
'email user': 'email user',
'EmailuserHint': 'Send an email to %s',
'space': 'space', // short form for userSpace link
'PrefixIndexHint': 'Show pages in the userspace of %s',
'count': 'count', ///// contributions, log
'edit counter': 'edit counter',
'editCounterLinkHint': 'Count the contributions made by %s',
'contribs': 'contribs',
'contributions': 'contributions',
'deletedContribs': 'deleted contributions',
'DeletedcontributionsHint': 'List deleted edits made by %s',
'ContributionsHint': 'List the contributions made by %s',
'log': 'log',
'user log': 'user log',
'userLogHint': 'Show %s\'s user log',
'arin': 'ARIN lookup', ///// ARIN lookup, block user or IP
'Look up %s in ARIN whois database': 'Look up %s in the ARIN whois database',
'unblockShort': 'un',
'block': 'block',
'block user': 'block user',
'IpblocklistHint': 'Unblock %s',
'BlockipHint': 'Prevent %s from editing',
'block log': 'block log',
'blockLogHint': 'Show the block log for %s',
'protectLogHint': 'Show the protection log for %s',
'pageLogHint': 'Show the page log for %s',
'deleteLogHint': 'Show the deletion log for %s',
'Invalid %s %s': 'The option %s is invalid: %s',
'No backlinks found': 'No backlinks found',
' and more': ' and more',
'undo': 'undo',
'undoHint': 'undo this edit',
'Download preview data': 'Download preview data',
'Invalid or IP user': 'Invalid or IP user',
'Not a registered username': 'Not a registered username',
'BLOCKED': 'BLOCKED',
'Has blocks': 'Has blocks',
' edits since: ': ' edits since: ',
'last edit on ': 'last edit on ',
/////////////////////////////////////
// Autoediting
/////////////////////////////////////
'Enter a non-empty edit summary or press cancel to abort': 'Enter a non-empty edit summary or press cancel to abort',
'Failed to get revision information, please edit manually.\n\n': 'Failed to get revision information, please edit manually.\n\n',
'The %s button has been automatically clicked. Please wait for the next page to load.': 'The %s button has been automatically clicked. Please wait for the next page to load.',
'Could not find button %s. Please check the settings in your javascript file.': 'Could not find button %s. Please check the settings in your javascript file.',
/////////////////////////////////////
// Popups setup
/////////////////////////////////////
'Open full-size image': 'Open full-size image',
'zxy': 'zxy',
'autoedit_version': 'np20140416'
};
function popupString(str) {
if (typeof popupStrings != 'undefined' && popupStrings && popupStrings[str]) { return popupStrings[str]; }
if (pg.string[str]) { return pg.string[str]; }
return str;
}
function tprintf(str,subs) {
if (typeof subs != typeof []) { subs = [subs]; }
return simplePrintf(popupString(str), subs);
}
//</NOLITE>
// ENDFILE: strings.js
// STARTFILE: run.js
////////////////////////////////////////////////////////////////////
// Run things
////////////////////////////////////////////////////////////////////
// For some reason popups requires a fully loaded page jQuery.ready(...) causes problems for some.
// The old addOnloadHook did something similar to the below
if (document.readyState=="complete")
autoEdit(); //will setup popups
else
$( window ).on( 'load', autoEdit );
// Support for MediaWiki's live preview, VisualEditor's saves and Echo's flyout.
( function () {
var once = true;
function dynamicContentHandler( $content ) {
// Try to detect the hook fired on initial page load and disregard
// it, we already hook to onload (possibly to different parts of
// page - it's configurable) and running twice might be bad. Ugly…
if ( $content.attr( 'id' ) == 'mw-content-text' ) {
if ( once ) {
once = false;
return;
}
}
function registerHooksForVisibleNavpops () {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
var navpop = pg.current.links[i].navpopup;
if (!navpop || !navpop.isVisible()) { continue; }
Navpopup.tracker.addHook(posCheckerHook(navpop));
}
}
function doIt () {
registerHooksForVisibleNavpops();
$content.each( function () {
this.ranSetupTooltipsAlready = false;
setupTooltips( this );
} );
}
setupPopups( doIt );
}
// This hook is also fired after page load.
mw.hook( 'wikipage.content' ).add( dynamicContentHandler );
mw.hook( 'ext.echo.overlay.beforeShowingOverlay' ).add( function($overlay){
dynamicContentHandler( $overlay.find(".mw-echo-state") );
});
} )();
// Forked from User:Connel MacKenzie/mess-with-popups.js
Previewmaker.prototype.firstBit = function() {
try{
if (mw.config.get("wgNamespaceNumber") !== 0){
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d = this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data = this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data = this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data = this.data.replace(RegExp('^[\\s\\n]*'), '');
stuff = (RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) {
d = stuff[0];
}
if (!getValueOf('popupPreviewFirstParOnly')) {
d = this.data;
}
/// now put \n\n after sections so that bullets and numbered lists work
d = d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["' + "'" + ']*\\s)', 'g'));
// leading space is bad, mmkay?
d[0] = d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds = RegExp('([^.][a-z][.][a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
var maxChars=getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd;
do {
dd = this.firstSentences(d, n);
--n;
}
while (dd.length > maxChars && n > 0);
this.data = dd;
} else {
//TODO: Limit this override to urls that start with http://*.wiktionary.org/
//TODO: extend the above section to also preview Wikipedia links
var charsWikt = 0;
var linesWikt = this.data.split('\n');
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[in]transitive}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[un]countable}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{cattag\|(.*?)}}/g, "");
linesWikt[jWikt] = linesWikt[jWikt].replace(/[\{\}]/g, "'");
if (linesWikt[jWikt].search(/^[#]/) == -1) {
if (linesWikt[jWikt].search(/^==[A-Z]/) == -1) {
linesWikt[jWikt] = '';
}
}
charsWikt += linesWikt[jWikt].length;
}
if (charsWikt < 2) {
linesWikt[0] = "No properly formatted content";
}
var maxChars = getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd = '';
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
if (linesWikt[jWikt] !== '') {
dd += linesWikt[jWikt] + '\n';
}
if (dd.length < maxChars) {
this.data = dd;
}
}
}
}
catch (e) {
console.log("error from mess with popups: ", e);
}
};
});
m8wnoaql8ojr2k0cjm9nwb8hb7yib6n
2513948
2513945
2026-08-14T04:45:50Z
Hiyuune
50834
2513948
javascript
text/javascript
// {{documentation}}
// _________________________________________________________________________________________
// | |
// | === WARNING: GLOBAL GADGET FILE === |
// | Changes to this page affect many users. |
// | Please discuss changes on the [[Wiktionary:Grease Pit]] before editing. |
// |_________________________________________________________________________________________|
mw.loader.load('//en.wikipedia.org/w/index.php?action=raw&title=MediaWiki:Gadget-navpop.css&ctype=text/css', "text/css");
// Forked from [[:w:MediaWiki:Gadget-popups.js]]
$(function () {
//////////////////////////////////////////////////
// Globals
//
// Trying to shove as many of these as possible into the pg (popup globals) object
var pg = {
re: {}, // regexps
ns: {}, // namespaces
string: {}, // translatable strings
wiki: {}, // local site info
user: {}, // current user info
misc: {}, // YUCK PHOOEY
option: {}, // options, see newOption etc
optionDefault: {}, // default option values
flag: {}, // misc flags
cache: {}, // page and image cache
structures: {}, // navlink structures
timer: {}, // all sorts of timers (too damn many)
counter: {}, // .. and all sorts of counters
current: {}, // state info
fn: {}, // functions
endoflist: null
};
/* Bail if the gadget/script is being loaded twice */
if( window.pg ) {
return;
}
/* Export to global context */
window.pg = pg;
/// Local Variables: ///
/// mode:c ///
/// End: ///
// ENDFILE: main.js
// STARTFILE: actions.js
function setupTooltips(container, remove, force, popData) {
log('setupTooltips, container='+container+', remove='+remove);
if (!container) {
//<NOLITE>
// the main initial call
if (getValueOf('popupOnEditSelection') && document && document.editform && document.editform.wpTextbox1) {
document.editform.wpTextbox1.onmouseup=doSelectionPopup;
}
//</NOLITE>
// article/content is a structure-dependent thing
container = defaultPopupsContainer();
}
if (!remove && !force && container.ranSetupTooltipsAlready) { return; }
container.ranSetupTooltipsAlready = !remove;
var anchors;
anchors=container.getElementsByTagName('A');
setupTooltipsLoop(anchors, 0, 250, 100, remove, popData);
}
function defaultPopupsContainer() {
if (getValueOf('popupOnlyArticleLinks')) {
return document.getElementById('mw_content') ||
document.getElementById('content') ||
document.getElementById('article') || document;
}
return document;
}
function setupTooltipsLoop(anchors,begin,howmany,sleep, remove, popData) {
log(simplePrintf('setupTooltipsLoop(%s,%s,%s,%s,%s)', arguments));
var finish=begin+howmany;
var loopend = Math.min(finish, anchors.length);
var j=loopend - begin;
log ('setupTooltips: anchors.length=' + anchors.length + ', begin=' + begin +
', howmany=' + howmany + ', loopend=' + loopend + ', remove=' + remove);
var doTooltip= remove ? removeTooltip : addTooltip;
// try a faster (?) loop construct
if (j > 0) {
do {
var a=anchors[loopend - j];
if (typeof a==='undefined' || !a || !a.href) {
log('got null anchor at index ' + loopend - j);
continue;
}
doTooltip(a, popData);
} while (--j);
}
if (finish < anchors.length) {
setTimeout(function() {
setupTooltipsLoop(anchors,finish,howmany,sleep,remove,popData);},
sleep);
} else {
if ( !remove && ! getValueOf('popupTocLinks')) { rmTocTooltips(); }
pg.flag.finishedLoading=true;
}
}
// eliminate popups from the TOC
// This also kills any onclick stuff that used to be going on in the toc
function rmTocTooltips() {
var toc=document.getElementById('toc');
if (toc) {
var tocLinks=toc.getElementsByTagName('A');
var tocLen = tocLinks.length;
for (var j=0; j<tocLen; ++j) {
removeTooltip(tocLinks[j], true);
}
}
}
function addTooltip(a, popData) {
if ( !isPopupLink(a) ) { return; }
a.onmouseover=mouseOverWikiLink;
a.onmouseout= mouseOutWikiLink;
a.onmousedown = killPopup;
a.hasPopup = true;
a.popData = popData;
}
function removeTooltip(a) {
if ( !a.hasPopup ) { return; }
a.onmouseover = null;
a.onmouseout = null;
if (a.originalTitle) { a.title = a.originalTitle; }
a.hasPopup=false;
}
function removeTitle(a) {
if (!a.originalTitle) {
a.originalTitle=a.title;
}
a.title='';
}
function restoreTitle(a) {
if ( a.title || !a.originalTitle ) { return; }
a.title = a.originalTitle;
}
function registerHooks(np) {
var popupMaxWidth=getValueOf('popupMaxWidth');
if (typeof popupMaxWidth === 'number') {
var setMaxWidth = function () {
np.mainDiv.style.maxWidth = popupMaxWidth + 'px';
np.maxWidth = popupMaxWidth;
};
np.addHook(setMaxWidth, 'unhide', 'before');
}
//<NOLITE>
np.addHook(addPopupShortcuts, 'unhide', 'after');
np.addHook(rmPopupShortcuts, 'hide', 'before');
//</NOLITE>
}
function removeModifierKeyHandler(a) {
//remove listeners for modifier key if any that were added in mouseOverWikiLink
document.removeEventListener('keydown', a.modifierKeyHandler, false);
document.removeEventListener('keyup', a.modifierKeyHandler, false);
}
function mouseOverWikiLink(evt) {
if (!evt && window.event) {evt=window.event;}
// if the modifier is needed, listen for it,
// we will remove the listener when we mouseout of this link or kill popup.
if (getValueOf('popupModifier')) {
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
var key = action=='disable' ? 'keyup' : 'keydown';
var a = this;
a.modifierKeyHandler = function(evt) {
mouseOverWikiLink2(a, evt);
};
document.addEventListener(key, a.modifierKeyHandler, false);
}
return mouseOverWikiLink2(this, evt);
}
/**
* Gets the references list item that the provided footnote link targets. This
* is typically a li element within the ol.references element inside the reflist.
* @param {Element} a - A footnote link.
* @returns {Element|boolean} The targeted element, or false if one can't be found.
*/
function footnoteTarget(a) {
var aTitle=Title.fromAnchor(a);
// We want ".3A" rather than "%3A" or "?" here, so use the anchor property directly
var anch = aTitle.anchor;
if ( ! /^(cite_note-|_note-|endnote)/.test(anch) ) { return false; }
var lTitle=Title.fromURL(location.href);
if ( lTitle.toString(true) !== aTitle.toString(true) ) { return false; }
var el=document.getElementById(anch);
while ( el && typeof el.nodeName === 'string') {
var nt = el.nodeName.toLowerCase();
if ( nt === 'li' ) { return el; }
else if ( nt === 'body' ) { return false; }
else if ( el.parentNode ) { el=el.parentNode; }
else { return false; }
}
return false;
}
function footnotePreview(x, navpop) {
setPopupHTML('<hr />' + x.innerHTML, 'popupPreview', navpop.idNumber);
}
function modifierPressed(evt) {
var mod=getValueOf('popupModifier');
if (!mod) { return false; }
if (!evt && window.event) {evt=window.event;}
return ( evt && mod && evt[mod.toLowerCase() + 'Key'] );
}
// Checks if the correct modifier pressed/unpressed if needed
function isCorrectModifier(a,evt) {
if (!getValueOf('popupModifier')) { return true; }
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
return ( action == 'enable' && modifierPressed(evt) ||
action == 'disable' && !modifierPressed(evt) );
}
function mouseOverWikiLink2(a, evt) {
if (!isCorrectModifier(a,evt)) { return; }
if ( getValueOf('removeTitles') ) { removeTitle(a); }
if ( a==pg.current.link && a.navpopup && a.navpopup.isVisible() ) { return; }
pg.current.link=a;
if (getValueOf('simplePopups') && !pg.option.popupStructure) {
// reset *default value* of popupStructure
setDefault('popupStructure', 'original');
}
var article=(new Title()).fromAnchor(a);
// set global variable (ugh) to hold article (wikipage)
pg.current.article = article;
if (!a.navpopup) {
a.navpopup=newNavpopup(a, article);
pg.current.linksHash[a.href] = a.navpopup;
pg.current.links.push(a);
}
if (a.navpopup.pending === null || a.navpopup.pending !== 0) {
// either fresh popups or those with unfinshed business are redone from scratch
simplePopupContent(a, article);
}
a.navpopup.showSoonIfStable(a.navpopup.delay);
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=setInterval(checkPopupPosition, 600);
if(getValueOf('simplePopups')) {
if (getValueOf('popupPreviewButton') && !a.simpleNoMore) {
var d=document.createElement('div');
d.className='popupPreviewButtonDiv';
var s=document.createElement('span');
d.appendChild(s);
s.className='popupPreviewButton';
s['on' + getValueOf('popupPreviewButtonEvent')] = function() {
a.simpleNoMore=true;
d.style.display = "none";
nonsimplePopupContent(a,article);
};
s.innerHTML=popupString('show preview');
setPopupHTML(d, 'popupPreview', a.navpopup.idNumber);
}
}
if (a.navpopup.pending !== 0 ) {
nonsimplePopupContent(a, article);
}
}
// simplePopupContent: the content that do not require additional download
// (it is shown even when simplePopups is true)
function simplePopupContent(a, article) {
/* FIXME hack */ a.navpopup.hasPopupMenu=false;
a.navpopup.setInnerHTML(popupHTML(a));
fillEmptySpans({navpopup:a.navpopup});
if (getValueOf('popupDraggable'))
{
var dragHandle = getValueOf('popupDragHandle') || null;
if (dragHandle && dragHandle != 'all') {
dragHandle += a.navpopup.idNumber;
}
setTimeout(function(){a.navpopup.makeDraggable(dragHandle);}, 150);
}
//<NOLITE>
if (getValueOf('popupRedlinkRemoval') && a.className=='new') {
setPopupHTML('<br>'+popupRedlinkHTML(article), 'popupRedlink', a.navpopup.idNumber);
}
//</NOLITE>
}
function debugData(navpopup) {
if(getValueOf('popupDebugging') && navpopup.idNumber) {
setPopupHTML('idNumber='+navpopup.idNumber + ', pending=' + navpopup.pending,
'popupError', navpopup.idNumber);
}
}
function newNavpopup(a, article) {
var navpopup = new Navpopup();
navpopup.fuzz=5;
navpopup.delay=getValueOf('popupDelay')*1000;
// increment global counter now
navpopup.idNumber = ++pg.idNumber;
navpopup.parentAnchor = a;
navpopup.parentPopup = (a.popData && a.popData.owner);
navpopup.article = article;
registerHooks(navpopup);
return navpopup;
}
// Should we show nonsimple context?
// If simplePopups is set to true, then we do not show nonsimple context,
// but if a bottom "show preview" was clicked we do show nonsimple context
function shouldShowNonSimple(a) {
return !getValueOf('simplePopups') || a.simpleNoMore;
}
// Should we show nonsimple context govern by the option (e.g. popupUserInfo)?
// If the user explicitly asked for nonsimple context by setting the option to true,
// then we show it even in nonsimple mode.
function shouldShow(a,option) {
if (shouldShowNonSimple(a)) {
return getValueOf(option);
} else {
return (typeof window[option] != 'undefined' ) && window[option];
}
}
function nonsimplePopupContent(a, article) {
var diff=null, history=null;
var params=parseParams(a.href);
var oldid=(typeof params.oldid=='undefined' ? null : params.oldid);
//<NOLITE>
if(shouldShow(a,'popupPreviewDiffs')) {
diff=params.diff;
}
if(shouldShow(a,'popupPreviewHistory')) {
history=(params.action=='history');
}
//</NOLITE>
a.navpopup.pending=0;
var referenceElement = footnoteTarget(a);
if (referenceElement) {
footnotePreview(referenceElement, a.navpopup);
//<NOLITE>
} else if ( diff || diff === 0 ) {
loadDiff(article, oldid, diff, a.navpopup);
} else if ( history ) {
loadAPIPreview('history', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.contribs.test(a.href) ) {
loadAPIPreview('contribs', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.backlinks.test(a.href) ) {
loadAPIPreview('backlinks', article, a.navpopup);
} else if ( // FIXME should be able to get all preview combinations with options
article.namespaceId()==pg.nsImageId &&
( shouldShow(a,'imagePopupsForImages') || ! anchorContainsImage(a) )
) {
loadAPIPreview('imagepagepreview', article, a.navpopup);
loadImage(article, a.navpopup);
//</NOLITE>
} else {
if (article.namespaceId() == pg.nsCategoryId &&
shouldShow(a,'popupCategoryMembers')) {
loadAPIPreview('category', article, a.navpopup);
} else if ((article.namespaceId() == pg.nsUserId || article.namespaceId() == pg.nsUsertalkId) &&
shouldShow(a,'popupUserInfo')) {
loadAPIPreview('userinfo', article, a.navpopup);
}
if (shouldShowNonSimple(a)) startArticlePreview(article, oldid, a.navpopup);
}
}
function pendingNavpopTask(navpop) {
if (navpop && navpop.pending === null) { navpop.pending=0; }
++navpop.pending;
debugData(navpop);
}
function completedNavpopTask(navpop) {
if (navpop && navpop.pending) { --navpop.pending; }
debugData(navpop);
}
function startArticlePreview(article, oldid, navpop) {
navpop.redir=0;
loadPreview(article, oldid, navpop);
}
function loadPreview(article, oldid, navpop) {
if (!navpop.redir) { navpop.originalArticle=article; }
article.oldid = oldid;
loadAPIPreview('revision', article, navpop);
}
function loadPreviewFromRedir(redirMatch, navpop) {
// redirMatch is a regex match
var target = new Title().fromWikiText(redirMatch[2]);
// overwrite (or add) anchor from original target
// mediawiki does overwrite; eg User:Lupin/foo3#Done
if ( navpop.article.anchor ) { target.anchor = navpop.article.anchor; }
navpop.redir++;
navpop.redirTarget=target;
//<NOLITE>
var warnRedir = redirLink(target, navpop.article);
setPopupHTML(warnRedir, 'popupWarnRedir', navpop.idNumber);
//</NOLITE>
navpop.article=target;
fillEmptySpans({redir: true, redirTarget: target, navpopup:navpop});
return loadPreview(target, null, navpop);
}
function insertPreview(download) {
if (!download.owner) { return; }
var redirMatch = pg.re.redirect.exec(download.data);
if (download.owner.redir === 0 && redirMatch) {
loadPreviewFromRedir(redirMatch, download.owner);
return;
}
if (download.owner.visible || !getValueOf('popupLazyPreviews')) {
insertPreviewNow(download);
} else {
var id=(download.owner.redir) ? 'PREVIEW_REDIR_HOOK' : 'PREVIEW_HOOK';
download.owner.addHook( function(){insertPreviewNow(download); return true;},
'unhide', 'after', id );
}
}
function insertPreviewNow(download) {
if (!download.owner) { return; }
var wikiText=download.data;
var navpop=download.owner;
var art=navpop.redirTarget || navpop.originalArticle;
//<NOLITE>
makeFixDabs(wikiText, navpop);
if (getValueOf('popupSummaryData')) {
getPageInfo(wikiText, download);
setPopupTrailer(getPageInfo(wikiText, download), navpop.idNumber);
}
var imagePage='';
if (art.namespaceId()==pg.nsImageId) { imagePage=art.toString(); }
else { imagePage=getValidImageFromWikiText(wikiText); }
if(imagePage) { loadImage(Title.fromWikiText(imagePage), navpop); }
//</NOLITE>
if (getValueOf('popupPreviews')) { insertArticlePreview(download, art, navpop); }
}
function insertArticlePreview(download, art, navpop) {
if (download && typeof download.data == typeof ''){
if (art.namespaceId()==pg.nsTemplateId && getValueOf('popupPreviewRawTemplates')) {
// FIXME compare/consolidate with diff escaping code for wikitext
var h='<hr /><span style="font-family: monospace;">' + download.data.entify().split('\\n').join('<br />\\n') + '</span>';
setPopupHTML(h, 'popupPreview', navpop.idNumber);
}
else {
var p=prepPreviewmaker(download.data, art, navpop);
p.showPreview();
}
}
}
function prepPreviewmaker(data, article, navpop) {
// deal with tricksy anchors
var d=anchorize(data, article.anchorString());
var urlBase=joinPath([pg.wiki.articlebase, article.urlString()]);
var p=new Previewmaker(d, urlBase, navpop);
return p;
}
// Try to imitate the way mediawiki generates HTML anchors from section titles
function anchorize(d, anch) {
if (!anch) { return d; }
var anchRe=RegExp('(?:=+\\s*' + literalizeRegex(anch).replace(/[_ ]/g, '[_ ]') + '\\s*=+|\\{\\{\\s*'+getValueOf('popupAnchorRegexp')+'\\s*(?:\\|[^|}]*)*?\\s*'+literalizeRegex(anch)+'\\s*(?:\\|[^}]*)?}})');
var match=d.match(anchRe);
if(match && match.length > 0 && match[0]) { return d.substring(d.indexOf(match[0])); }
// now try to deal with == foo [[bar|baz]] boom == -> #foo_baz_boom
var lines=d.split('\n');
for (var i=0; i<lines.length; ++i) {
lines[i]=lines[i].replace(RegExp('[[]{2}([^|\\]]*?[|])?(.*?)[\\]]{2}', 'g'), '$2')
.replace(/'''([^'])/g, '$1').replace(RegExp("''([^'])", 'g'), '$1');
if (lines[i].match(anchRe)) {
return d.split('\n').slice(i).join('\n').replace(RegExp('^[^=]*'), '');
}
}
return d;
}
function killPopup() {
removeModifierKeyHandler(this);
if (getValueOf('popupShortcutKeys')) { rmPopupShortcuts(); }
if (!pg) { return; }
if (pg.current.link && pg.current.link.navpopup) { pg.current.link.navpopup.banish(); }
pg.current.link=null;
abortAllDownloads();
if (pg.timer.checkPopupPosition) {
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=null;
}
return true; // preserve default action
}
// ENDFILE: actions.js
// STARTFILE: domdrag.js
/**
@fileoverview
The {@link Drag} object, which enables objects to be dragged around.
<pre>
*************************************************
dom-drag.js
09.25.2001
www.youngpup.net
**************************************************
10.28.2001 - fixed minor bug where events
sometimes fired off the handle, not the root.
*************************************************
Pared down, some hooks added by User:Lupin
Copyright Aaron Boodman.
Saying stupid things daily since March 2001.
</pre>
*/
/**
Creates a new Drag object. This is used to make various DOM elements draggable.
@constructor
*/
function Drag () {
/**
Condition to determine whether or not to drag. This function should take one parameter, an Event.
To disable this, set it to <code>null</code>.
@type Function
*/
this.startCondition = null;
/**
Hook to be run when the drag finishes. This is passed the final coordinates of
the dragged object (two integers, x and y). To disables this, set it to <code>null</code>.
@type Function
*/
this.endHook = null;
}
/**
Gets an event in a cross-browser manner.
@param {Event} e
@private
*/
Drag.prototype.fixE = function(e) {
if (typeof e == 'undefined') { e = window.event; }
if (typeof e.layerX == 'undefined') { e.layerX = e.offsetX; }
if (typeof e.layerY == 'undefined') { e.layerY = e.offsetY; }
return e;
};
/**
Initialises the Drag instance by telling it which object you want to be draggable, and what you want to drag it by.
@param {DOMElement} o The "handle" by which <code>oRoot</code> is dragged.
@param {DOMElement} oRoot The object which moves when <code>o</code> is dragged, or <code>o</code> if omitted.
*/
Drag.prototype.init = function(o, oRoot) {
var dragObj = this;
this.obj = o;
o.onmousedown = function(e) { dragObj.start.apply( dragObj, [e]); };
o.dragging = false;
o.popups_draggable = true;
o.hmode = true;
o.vmode = true;
o.root = oRoot ? oRoot : o ;
if (isNaN(parseInt(o.root.style.left, 10))) { o.root.style.left = "0px"; }
if (isNaN(parseInt(o.root.style.top, 10))) { o.root.style.top = "0px"; }
o.root.onthisStart = function(){};
o.root.onthisEnd = function(){};
o.root.onthis = function(){};
};
/**
Starts the drag.
@private
@param {Event} e
*/
Drag.prototype.start = function(e) {
var o = this.obj; // = this;
e = this.fixE(e);
if (this.startCondition && !this.startCondition(e)) { return; }
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10);
o.root.onthisStart(x, y);
o.lastMouseX = e.clientX;
o.lastMouseY = e.clientY;
var dragObj = this;
o.onmousemoveDefault = document.onmousemove;
o.dragging = true;
document.onmousemove = function(e) { dragObj.drag.apply( dragObj, [e] ); };
document.onmouseup = function(e) { dragObj.end.apply( dragObj, [e] ); };
return false;
};
/**
Does the drag.
@param {Event} e
@private
*/
Drag.prototype.drag = function(e) {
e = this.fixE(e);
var o = this.obj;
var ey = e.clientY;
var ex = e.clientX;
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10 );
var nx, ny;
nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));
this.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
this.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
this.obj.lastMouseX = ex;
this.obj.lastMouseY = ey;
this.obj.root.onthis(nx, ny);
return false;
};
/**
Ends the drag.
@private
*/
Drag.prototype.end = function() {
document.onmousemove=this.obj.onmousemoveDefault;
document.onmouseup = null;
this.obj.dragging = false;
if (this.endHook) {
this.endHook( parseInt(this.obj.root.style[this.obj.hmode ? "left" : "right"], 10),
parseInt(this.obj.root.style[this.obj.vmode ? "top" : "bottom"], 10));
}
};
// ENDFILE: domdrag.js
// STARTFILE: structures.js
//<NOLITE>
pg.structures.original={};
pg.structures.original.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle',
'popupUserData', 'popupData', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.original.popupRedirSpans=function () {
return ['popupRedir', 'popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'];
};
pg.structures.original.popupTitle=function (x) {
log ('defaultstructure.popupTitle');
if (!getValueOf('popupNavLinks')) {
return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
}
return '';
};
pg.structures.original.popupTopLinks=function (x) {
log ('defaultstructure.popupTopLinks');
if (getValueOf('popupNavLinks')) { return navLinksHTML(x.article, x.hint, x.params); }
return '';
};
pg.structures.original.popupImage=function(x) {
log ('original.popupImage, x.article='+x.article+', x.navpop.idNumber='+x.navpop.idNumber);
return imageHTML(x.article, x.navpop.idNumber);
};
pg.structures.original.popupRedirTitle=pg.structures.original.popupTitle;
pg.structures.original.popupRedirTopLinks=pg.structures.original.popupTopLinks;
function copyStructure(oldStructure, newStructure) {
pg.structures[newStructure]={};
for (var prop in pg.structures[oldStructure]) {
pg.structures[newStructure][prop]=pg.structures[oldStructure][prop];
}
}
copyStructure('original', 'nostalgia');
pg.structures.nostalgia.popupTopLinks=function(x) {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>';
str+='if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' +
editstr + '}';
var historystr='<<history|shortcut=h>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str += '<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>';
str += 'if(admin){<br>}else{*}<<move|shortcut=m>>';
// admin links
str += 'if(admin){*<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>}';
return navlinkStringToHTML(str, x.article, x.params);
};
pg.structures.nostalgia.popupRedirTopLinks=pg.structures.nostalgia.popupTopLinks;
/** -- fancy -- **/
copyStructure('original', 'fancy');
pg.structures.fancy.popupTitle=function (x) {
return navlinkStringToHTML('<font size=+0><<mainlink>></font>',x.article,x.params);
};
pg.structures.fancy.popupTopLinks=function(x) {
var hist='<<history|shortcut=h|hist>>|<<lastEdit|shortcut=/|last>>|<<editors|shortcut=E|eds>>';
var watch='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
var move='<<move|shortcut=m|move>>';
return navlinkStringToHTML('if(talk){' +
'<<edit|shortcut=e>>|<<new|shortcut=+|+>>*' + hist + '*' +
'<<article|shortcut=a>>|<<editArticle|edit>>' + '*' + watch + '*' + move +
'}else{<<edit|shortcut=e>>*' + hist +
'*<<talk|shortcut=t|>>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>' +
'*' + watch + '*' + move+'}<br>', x.article, x.params);
};
pg.structures.fancy.popupOtherLinks=function(x) {
var admin='<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*<<undelete|undeleteShort>>|<<delete|shortcut=d|del>>';
var user='<<contribs|shortcut=c>>if(wikimedia){|<<count|shortcut=#|#>>}';
user+='if(ipuser){|<<arin>>}else{*<<email|shortcut=E|'+
popupString('email')+'>>}if(admin){*<<block|shortcut=b>>}';
var normal='<<whatLinksHere|shortcut=l|links here>>*<<relatedChanges|shortcut=r|related>>';
return navlinkStringToHTML('<br>if(user){' + user + '*}if(admin){'+admin+'if(user){<br>}else{*}}' + normal,
x.article, x.params);
};
pg.structures.fancy.popupRedirTitle=pg.structures.fancy.popupTitle;
pg.structures.fancy.popupRedirTopLinks=pg.structures.fancy.popupTopLinks;
pg.structures.fancy.popupRedirOtherLinks=pg.structures.fancy.popupOtherLinks;
/** -- fancy2 -- **/
// hack for User:MacGyverMagic
copyStructure('fancy', 'fancy2');
pg.structures.fancy2.popupTopLinks=function(x) { // hack out the <br> at the end and put one at the beginning
return '<br>'+pg.structures.fancy.popupTopLinks(x).replace(RegExp('<br>$','i'),'');
};
pg.structures.fancy2.popupLayout=function () { // move toplinks to after the title
return ['popupError', 'popupImage', 'popupTitle', 'popupUserData', 'popupData', 'popupTopLinks', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
/** -- menus -- **/
copyStructure('original', 'menus');
pg.structures.menus.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupUserData', 'popupData', 'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.menus.popupTopLinks = function (x, shorter) {
// FIXME maybe this stuff should be cached
var s=[];
var dropdiv='<div class="popup_drop">';
var enddiv='</div>';
var hist='<<history|shortcut=h>>';
if (!shorter) { hist = '<menurow>' + hist +
'|<<historyfeed|rss>>|<<editors|shortcut=E>></menurow>'; }
var lastedit='<<lastEdit|shortcut=/|show last edit>>';
var thank='if(diff){<<thank|send thanks>>}';
var jsHistory='<<lastContrib|last set of edits>><<sinceMe|changes since mine>>';
var linkshere='<<whatLinksHere|shortcut=l|what links here>>';
var related='<<relatedChanges|shortcut=r|related changes>>';
var search='<menurow><<search|shortcut=s>>if(wikimedia){|<<globalsearch|shortcut=g|global>>}' +
'|<<google|shortcut=G|web>></menurow>';
var watch='<menurow><<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>></menurow>';
var protect='<menurow><<unprotect|unprotectShort>>|' +
'<<protect|shortcut=p>>|<<protectlog|log>></menurow>';
var del='<menurow><<undelete|undeleteShort>>|<<delete|shortcut=d>>|' +
'<<deletelog|log>></menurow>';
var move='<<move|shortcut=m|move page>>';
var nullPurge='<menurow><<nullEdit|shortcut=n|null edit>>|<<purge|shortcut=P>></menurow>';
var viewOptions='<menurow><<view|shortcut=v>>|<<render|shortcut=S>>|<<raw>></menurow>';
var editRow='if(oldid){' +
'<menurow><<edit|shortcut=e>>|<<editOld|shortcut=e|this revision>></menurow>' +
'<menurow><<revert|shortcut=v>>|<<undo>></menurow>' + '}else{<<edit|shortcut=e>>}';
var markPatrolled='if(rcid){<<markpatrolled|mark patrolled>>}';
var newTopic='if(talk){<<new|shortcut=+|new topic>>}';
var protectDelete='if(admin){' + protect + del + '}';
if (getValueOf('popupActionsMenu')) {
s.push( '<<mainlink>>*' + dropdiv + menuTitle('actions'));
} else {
s.push( dropdiv + '<<mainlink>>');
}
s.push( '<menu>');
s.push( editRow + markPatrolled + newTopic + hist + lastedit + thank );
if (!shorter) { s.push(jsHistory); }
s.push( move + linkshere + related);
if (!shorter) { s.push(nullPurge + search); }
if (!shorter) { s.push(viewOptions); }
s.push('<hr />' + watch + protectDelete);
s.push('<hr />' +
'if(talk){<<article|shortcut=a|view article>><<editArticle|edit article>>}' +
'else{<<talk|shortcut=t|talk page>><<editTalk|edit talk>>' +
'<<newTalk|shortcut=+|new topic>>}</menu>' + enddiv);
// user menu starts here
var email='<<email|shortcut=E|email user>>';
var contribs= 'if(wikimedia){<menurow>}<<contribs|shortcut=c|contributions>>if(wikimedia){</menurow>}' +
'if(admin){<menurow><<deletedContribs>></menurow>}';
s.push('if(user){*' + dropdiv + menuTitle('user'));
s.push('<menu>');
s.push('<menurow><<userPage|shortcut=u|user page>>|<<userSpace|space>></menurow>');
s.push('<<userTalk|shortcut=t|user talk>><<editUserTalk|edit user talk>>' +
'<<newUserTalk|shortcut=+|leave comment>>');
if(!shorter) { s.push( 'if(ipuser){<<arin>>}else{' + email + '}' ); }
else { s.push( 'if(ipuser){}else{' + email + '}' ); }
s.push('<hr />' + contribs + '<<userlog|shortcut=L|user log>>');
s.push('if(wikimedia){<<count|shortcut=#|edit counter>>}');
s.push('if(admin){<menurow><<unblock|unblockShort>>|<<block|shortcut=b|block user>></menurow>}');
s.push('<<blocklog|shortcut=B|block log>>');
s.push('</menu>' + enddiv + '}');
// popups menu starts here
if (getValueOf('popupSetupMenu') && !x.navpop.hasPopupMenu /* FIXME: hack */) {
x.navpop.hasPopupMenu=true;
s.push('*' + dropdiv + menuTitle('popupsMenu') + '<menu>');
s.push('<<togglePreviews|toggle previews>>');
s.push('<<purgePopups|reset>>');
s.push('<<disablePopups|disable>>');
s.push('</menu>'+enddiv);
}
return navlinkStringToHTML(s.join(''), x.article, x.params);
};
function menuTitle(s) {
return '<a href="#" noPopup=1>' + popupString(s) + '</a>';
}
pg.structures.menus.popupRedirTitle=pg.structures.menus.popupTitle;
pg.structures.menus.popupRedirTopLinks=pg.structures.menus.popupTopLinks;
copyStructure('menus', 'shortmenus');
pg.structures.shortmenus.popupTopLinks=function(x) {
return pg.structures.menus.popupTopLinks(x,true);
};
pg.structures.shortmenus.popupRedirTopLinks=pg.structures.shortmenus.popupTopLinks;
//</NOLITE>
pg.structures.lite={};
pg.structures.lite.popupLayout=function () {
return ['popupTitle', 'popupPreview' ];
};
pg.structures.lite.popupTitle=function (x) {
log (x.article + ': structures.lite.popupTitle');
//return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
return '<div><span class="popup_mainlink"><b>' + x.article.toString() + '</b></span></div>';
};
// ENDFILE: structures.js
// STARTFILE: autoedit.js
//<NOLITE>
function substitute(data,cmdBody) {
// alert('sub\nfrom: '+cmdBody.from+'\nto: '+cmdBody.to+'\nflags: '+cmdBody.flags);
var fromRe=RegExp(cmdBody.from, cmdBody.flags);
return data.replace(fromRe, cmdBody.to);
}
function execCmds(data, cmdList) {
for (var i=0; i<cmdList.length; ++i) {
data=cmdList[i].action(data, cmdList[i]);
}
return data;
}
function parseCmd(str) {
// returns a list of commands
if (!str.length) { return []; }
var p=false;
switch (str.charAt(0)) {
case 's':
p=parseSubstitute(str);
break;
default:
return false;
}
if (p) { return [p].concat(parseCmd(p.remainder)); }
return false;
}
function unEscape(str, sep) {
return str.split('\\\\').join('\\').split('\\'+sep).join(sep).split('\\n').join('\n');
}
function parseSubstitute(str) {
// takes a string like s/a/b/flags;othercmds and parses it
var from,to,flags,tmp;
if (str.length<4) { return false; }
var sep=str.charAt(1);
str=str.substring(2);
tmp=skipOver(str,sep);
if (tmp) { from=tmp.segment; str=tmp.remainder; }
else { return false; }
tmp=skipOver(str,sep);
if (tmp) { to=tmp.segment; str=tmp.remainder; }
else { return false; }
flags='';
if (str.length) {
tmp=skipOver(str,';') || skipToEnd(str, ';');
if (tmp) {flags=tmp.segment; str=tmp.remainder; }
}
return {action: substitute, from: from, to: to, flags: flags, remainder: str};
}
function skipOver(str,sep) {
var endSegment=findNext(str,sep);
if (endSegment<0) { return false; }
var segment=unEscape(str.substring(0,endSegment), sep);
return {segment: segment, remainder: str.substring(endSegment+1)};
}
/*eslint-disable*/
function skipToEnd(str,sep) {
return {segment: str, remainder: ''};
}
/*eslint-enable */
function findNext(str, ch) {
for (var i=0; i<str.length; ++i) {
if (str.charAt(i)=='\\') { i+=2; }
if (str.charAt(i)==ch) { return i; }
}
return -1;
}
function setCheckbox(param, box) {
var val=mw.util.getParamValue(param);
if (val) {
switch (val) {
case '1': case 'yes': case 'true':
box.checked=true;
break;
case '0': case 'no': case 'false':
box.checked=false;
}
}
}
function autoEdit() {
setupPopups( function () {
if (mw.util.getParamValue('autoimpl') !== popupString('autoedit_version') ) { return false; }
if (mw.util.getParamValue('autowatchlist') && mw.util.getParamValue('actoken')===autoClickToken()) {
pg.fn.modifyWatchlist(mw.util.getParamValue('title'), mw.util.getParamValue('action'));
}
if (!document.editform) { return false; }
if (autoEdit.alreadyRan) { return false; }
autoEdit.alreadyRan=true;
var cmdString=mw.util.getParamValue('autoedit');
if (cmdString) {
try {
var editbox=document.editform.wpTextbox1;
var cmdList=parseCmd(cmdString);
var input=editbox.value;
var output=execCmds(input, cmdList);
editbox.value=output;
} catch (dang) { return; }
// wikEd user script compatibility
if (typeof(wikEdUseWikEd) != 'undefined') {
if (wikEdUseWikEd === true) {
WikEdUpdateFrame();
}
}
}
setCheckbox('autominor', document.editform.wpMinoredit);
setCheckbox('autowatch', document.editform.wpWatchthis);
var rvid = mw.util.getParamValue('autorv');
if (rvid) {
var url=pg.wiki.apiwikibase + '?action=query&format=json&formatversion=2&prop=revisions&revids='+rvid;
startDownload(url, null, autoEdit2);
} else { autoEdit2(); }
} );
}
function autoEdit2(d) {
var summary=mw.util.getParamValue('autosummary');
var summaryprompt=mw.util.getParamValue('autosummaryprompt');
var summarynotice='';
if (d && d.data && mw.util.getParamValue('autorv')) {
var s = getRvSummary(summary, d.data);
if (s === false) {
summaryprompt=true;
summarynotice=popupString('Failed to get revision information, please edit manually.\n\n');
summary = simplePrintf(summary, [mw.util.getParamValue('autorv'), '(unknown)', '(unknown)']);
} else { summary = s; }
}
if (summaryprompt) {
var txt= summarynotice +
popupString('Enter a non-empty edit summary or press cancel to abort');
var response=prompt(txt, summary);
if (response) { summary=response; }
else { return; }
}
if (summary) { document.editform.wpSummary.value=summary; }
// Attempt to avoid possible premature clicking of the save button
// (maybe delays in updates to the DOM are to blame?? or a red herring)
setTimeout(autoEdit3, 100);
}
function autoClickToken() {
return mw.user.sessionId();
}
function autoEdit3() {
if( mw.util.getParamValue('actoken') != autoClickToken()) { return; }
var btn=mw.util.getParamValue('autoclick');
if (btn) {
if (document.editform && document.editform[btn]) {
var button=document.editform[btn];
var msg=tprintf('The %s button has been automatically clicked. Please wait for the next page to load.',
[ button.value ]);
bannerMessage(msg);
document.title='('+document.title+')';
button.click();
} else {
alert(tprintf('Could not find button %s. Please check the settings in your javascript file.',
[ btn ]));
}
}
}
function bannerMessage(s) {
var headings=document.getElementsByTagName('h1');
if (headings) {
var div=document.createElement('div');
div.innerHTML='<font size=+1><b>' + s + '</b></font>';
headings[0].parentNode.insertBefore(div, headings[0]);
}
}
function getRvSummary(template, json) {
try {
var o=getJsObj(json);
var edit = anyChild(o.query.pages).revisions[0];
var timestamp = edit.timestamp.split(/[A-Z]/g).join(' ').replace(/^ *| *$/g, '');
return simplePrintf(template, [edit.revid, timestamp, edit.userhidden ? '(hidden)' : edit.user ]);
} catch (badness) {
return false;
}
}
//</NOLITE>
// ENDFILE: autoedit.js
// STARTFILE: downloader.js
/**
@fileoverview
{@link Downloader}, a xmlhttprequest wrapper, and helper functions.
*/
/**
Creates a new Downloader
@constructor
@class The Downloader class. Create a new instance of this class to download stuff.
@param {String} url The url to download. This can be omitted and supplied later.
*/
function Downloader(url) {
if (typeof XMLHttpRequest!='undefined') { this.http = new XMLHttpRequest(); }
/**
The url to download
@type String
*/
this.url = url;
/**
A universally unique ID number
@type integer
*/
this.id=null;
/**
Modification date, to be culled from the incoming headers
@type Date
@private
*/
this.lastModified = null;
/**
What to do when the download completes successfully
@type Function
@private
*/
this.callbackFunction = null;
/**
What to do on failure
@type Function
@private
*/
this.onFailure = null;
/**
Flag set on <code>abort</code>
@type boolean
*/
this.aborted = false;
/**
HTTP method. See https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html for possibilities.
@type String
*/
this.method='GET';
/**
Async flag.
@type boolean
*/
this.async=true;
}
new Downloader();
/** Submits the http request. */
Downloader.prototype.send = function (x) {
if (!this.http) { return null; }
return this.http.send(x);
};
/** Aborts the download, setting the <code>aborted</code> field to true. */
Downloader.prototype.abort = function () {
if (!this.http) { return null; }
this.aborted=true;
return this.http.abort();
};
/** Returns the downloaded data. */
Downloader.prototype.getData = function () {if (!this.http) { return null; } return this.http.responseText;};
/** Prepares the download. */
Downloader.prototype.setTarget = function () {
if (!this.http) { return null; }
this.http.open(this.method, this.url, this.async);
this.http.setRequestHeader( 'Api-User-Agent', pg.misc.userAgent );
};
/** Gets the state of the download. */
Downloader.prototype.getReadyState=function () {if (!this.http) { return null; } return this.http.readyState;};
pg.misc.downloadsInProgress = { };
/** Starts the download.
Note that setTarget {@link Downloader#setTarget} must be run first
*/
Downloader.prototype.start=function () {
if (!this.http) { return; }
pg.misc.downloadsInProgress[this.id] = this;
this.http.send(null);
};
/** Gets the 'Last-Modified' date from the download headers.
Should be run after the download completes.
Returns <code>null</code> on failure.
@return {Date}
*/
Downloader.prototype.getLastModifiedDate=function () {
if(!this.http) { return null; }
var lastmod=null;
try {
lastmod=this.http.getResponseHeader('Last-Modified');
} catch (err) {}
if (lastmod) { return new Date(lastmod); }
return null;
};
/** Sets the callback function.
@param {Function} f callback function, called as <code>f(this)</code> on success
*/
Downloader.prototype.setCallback = function (f) {
if(!this.http) { return; }
this.http.onreadystatechange = f;
};
Downloader.prototype.getStatus = function() { if (!this.http) { return null; } return this.http.status; };
//////////////////////////////////////////////////
// helper functions
/** Creates a new {@link Downloader} and prepares it for action.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function newDownload(url, id, callback, onfailure) {
var d=new Downloader(url);
if (!d.http) { return 'ohdear'; }
d.id=id;
d.setTarget();
if (!onfailure) {
onfailure=2;
}
var f = function () {
if (d.getReadyState() == 4) {
delete pg.misc.downloadsInProgress[this.id];
try {
if ( d.getStatus() == 200 ) {
d.data=d.getData();
d.lastModified=d.getLastModifiedDate();
callback(d);
} else if (typeof onfailure == typeof 1) {
if (onfailure > 0) {
// retry
newDownload(url, id, callback, onfailure - 1);
}
} else if (typeof onfailure === 'function') {
onfailure(d,url,id,callback);
}
} catch (somerr) { /* ignore it */ }
}
};
d.setCallback(f);
return d;
}
/** Simulates a download from cached data.
The supplied data is put into a {@link Downloader} as if it had downloaded it.
@param {String} url The url.
@param {integer} id The ID.
@param {Function} callback The callback, which is invoked immediately as <code>callback(d)</code>,
where <code>d</code> is the new {@link Downloader}.
@param {String} data The (cached) data.
@param {Date} lastModified The (cached) last modified date.
*/
function fakeDownload(url, id, callback, data, lastModified, owner) {
var d=newDownload(url,callback);
d.owner=owner;
d.id=id; d.data=data;
d.lastModified=lastModified;
return callback(d);
}
/**
Starts a download.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function startDownload(url, id, callback) {
var d=newDownload(url, id, callback);
if (typeof d == typeof '' ) { return d; }
d.start();
return d;
}
/**
Aborts all downloads which have been started.
*/
function abortAllDownloads() {
for ( var x in pg.misc.downloadsInProgress ) {
try {
pg.misc.downloadsInProgress[x].aborted=true;
pg.misc.downloadsInProgress[x].abort();
delete pg.misc.downloadsInProgress[x];
} catch (e) {}
}
}
// ENDFILE: downloader.js
// STARTFILE: livepreview.js
// TODO: location is often not correct (eg relative links in previews)
// NOTE: removed md5 and image and math parsing. was broken, lots of bytes.
/**
* InstaView - a Mediawiki to HTML converter in JavaScript
* Version 0.6.1
* Copyright (C) Pedro Fayolle 2005-2006
* https://en.wikipedia.org/wiki/User:Pilaf
* Distributed under the BSD license
*
* Changelog:
*
* 0.6.1
* - Fixed problem caused by \r characters
* - Improved inline formatting parser
*
* 0.6
* - Changed name to InstaView
* - Some major code reorganizations and factored out some common functions
* - Handled conversion of relative links (i.e. [[/foo]])
* - Fixed misrendering of adjacent definition list items
* - Fixed bug in table headings handling
* - Changed date format in signatures to reflect Mediawiki's
* - Fixed handling of [[:Image:...]]
* - Updated MD5 function (hopefully it will work with UTF-8)
* - Fixed bug in handling of links inside images
*
* To do:
* - Better support for math tags
* - Full support for <nowiki>
* - Parser-based (as opposed to RegExp-based) inline wikicode handling (make it one-pass and bullet-proof)
* - Support for templates (through AJAX)
* - Support for coloured links (AJAX)
*/
var Insta = {};
function setupLivePreview() {
// options
Insta.conf =
{
baseUrl: '',
user: {},
wiki: {
lang: pg.wiki.lang,
interwiki: pg.wiki.interwiki,
default_thumb_width: 180
},
paths: {
articles: pg.wiki.articlePath + '/',
// Only used for Insta previews with images. (not in popups)
math: '/math/',
images: '//upload.wikimedia.org/wikipedia/en/', // FIXME getImageUrlStart(pg.wiki.hostname),
images_fallback: '//upload.wikimedia.org/wikipedia/commons/',
},
locale: {
user: mw.config.get('wgFormattedNamespaces')[pg.nsUserId],
image: mw.config.get('wgFormattedNamespaces')[pg.nsImageId],
category: mw.config.get('wgFormattedNamespaces')[pg.nsCategoryId],
// shouldn't be used in popup previews, i think
months: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
}
};
// options with default values or backreferences
Insta.conf.user.name = Insta.conf.user.name || 'Wikipedian';
Insta.conf.user.signature = '[['+Insta.conf.locale.user+':'+Insta.conf.user.name+'|'+Insta.conf.user.name+']]';
//Insta.conf.paths.images = '//upload.wikimedia.org/wikipedia/' + Insta.conf.wiki.lang + '/';
// define constants
Insta.BLOCK_IMAGE = new RegExp('^\\[\\[(?:File|Image|'+Insta.conf.locale.image+
'):.*?\\|.*?(?:frame|thumbnail|thumb|none|right|left|center)', 'i');
}
Insta.dump = function(from, to)
{
if (typeof from == 'string') { from = document.getElementById(from); }
if (typeof to == 'string') { to = document.getElementById(to); }
to.innerHTML = this.convert(from.value);
};
Insta.convert = function(wiki)
{
var ll = (typeof wiki == 'string')? wiki.replace(/\r/g,'').split(/\n/): wiki, // lines of wikicode
o = '', // output
p = 0, // para flag
$r; // result of passing a regexp to $()
// some shorthands
function remain() { return ll.length; }
function sh() { return ll.shift(); } // shift
function ps(s) { o += s; } // push
// similar to C's printf, uses ? as placeholders, ?? to escape question marks
function f()
{
var i=1, a=arguments, f=a[0], o='', c, p;
for (; i<a.length; i++) {
if ((p=f.indexOf('?'))+1) {
// allow character escaping
i -= c = f.charAt(p+1)=='?' ? 1 : 0;
o += f.substring(0,p) + (c ? '?' : a[i]);
f = f.substr(p+1+c);
} else { break; }
}
return o+f;
}
function html_entities(s) {
return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
}
// Wiki text parsing to html is a nightmare.
// The below functions deliberately don't escape the ampersand since this would make it more difficult,
// and we don't absolutely need to for how we need it.
// This means that any unescaped ampersands in wikitext will remain unescaped and can cause invalid HTML.
// Browsers should all be able to handle it though.
// We also escape significant wikimarkup characters to prevent further matching on the processed text
function htmlescape_text(s) {
return s.replace(/</g,"<").replace(/>/g,">").replace(/:/g,":").replace(/\[/g,"[").replace(/]/g,"]");
}
function htmlescape_attr(s) {
return htmlescape_text(s).replace(/'/g,"'").replace(/"/g,""");
}
// return the first non matching character position between two strings
function str_imatch(a, b)
{
for (var i=0, l=Math.min(a.length, b.length); i<l; i++) {
if (a.charAt(i)!=b.charAt(i)) { break; }
}
return i;
}
// compare current line against a string or regexp
// if passed a string it will compare only the first string.length characters
// if passed a regexp the result is stored in $r
function $(c) { return (typeof c == 'string') ? (ll[0].substr(0,c.length)==c) : ($r = ll[0].match(c)); }
function $$(c) { return ll[0]==c; } // compare current line against a string
function _(p) { return ll[0].charAt(p); } // return char at pos p
function endl(s) { ps(s); sh(); }
function parse_list()
{
var prev='';
while (remain() && $(/^([*#:;]+)(.*)$/)) {
var l_match = $r;
sh();
var ipos = str_imatch(prev, l_match[1]);
// close uncontinued lists
for (var prevPos=prev.length-1; prevPos >= ipos; prevPos--) {
var pi = prev.charAt(prevPos);
if (pi=='*') { ps('</ul>'); }
else if (pi=='#') { ps('</ol>'); }
// close a dl only if the new item is not a dl item (:, ; or empty)
else if($.inArray(l_match[1].charAt(prevPos), ['','*','#'])) { ps('</dl>'); }
}
// open new lists
for (var matchPos=ipos; matchPos<l_match[1].length; matchPos++) {
var li = l_match[1].charAt(matchPos);
if (li=='*') { ps('<ul>'); }
else if (li=='#') { ps('<ol>'); }
// open a new dl only if the prev item is not a dl item (:, ; or empty)
else if ($.inArray(prev.charAt(matchPos), ['','*','#'])) { ps('<dl>'); }
}
switch (l_match[1].charAt(l_match[1].length-1)) {
case '*': case '#':
ps('<li>' + parse_inline_nowiki(l_match[2]));
break;
case ';':
ps('<dt>');
var dt_match = l_match[2].match(/(.*?)(:.*?)$/);
// handle ;dt :dd format
if (dt_match) {
ps(parse_inline_nowiki(dt_match[1]));
ll.unshift(dt_match[2]);
} else ps(parse_inline_nowiki(l_match[2]));
break;
case ':':
ps('<dd>' + parse_inline_nowiki(l_match[2]));
}
prev=l_match[1];
}
// close remaining lists
for (var i=prev.length-1; i>=0; i--) {
ps(f('</?>', (prev.charAt(i)=='*')? 'ul': ((prev.charAt(i)=='#')? 'ol': 'dl')));
}
}
function parse_table()
{
endl(f('<table>', $(/^\{\|( .*)$/)? $r[1]: ''));
for (;remain();) if ($('|')) switch (_(1)) {
case '}':
endl('</table>');
return;
case '-':
endl(f('<tr>', $(/\|-*(.*)/)[1]));
break;
default:
parse_table_data();
}
else if ($('!')) { parse_table_data(); }
else { sh(); }
}
function parse_table_data()
{
var td_line, match_i;
// 1: "|+", '|' or '+'
// 2: ??
// 3: attributes ??
// TODO: finish commenting this regexp
var td_match = sh().match(/^(\|\+|\||!)((?:([^[|]*?)\|(?!\|))?(.*))$/);
if (td_match[1] == '|+') ps('<caption');
else ps('<t' + ((td_match[1]=='|')?'d':'h'));
if (typeof td_match[3] != 'undefined') {
//ps(' ' + td_match[3])
match_i = 4;
} else match_i = 2;
ps('>');
if (td_match[1] != '|+') {
// use || or !! as a cell separator depending on context
// NOTE: when split() is passed a regexp make sure to use non-capturing brackets
td_line = td_match[match_i].split((td_match[1] == '|')? '||': /(?:\|\||!!)/);
ps(parse_inline_nowiki(td_line.shift()));
while (td_line.length) ll.unshift(td_match[1] + td_line.pop());
} else ps(td_match[match_i]);
var tc = 0, td = [];
while (remain()) {
td.push(sh());
if ($('|')) {
if (!tc) break; // we're at the outer-most level (no nested tables), skip to td parse
else if (_(1)=='}') tc--;
}
else if (!tc && $('!')) break;
else if ($('{|')) tc++;
}
if (td.length) ps(Insta.convert(td));
}
function parse_pre()
{
ps('<pre>');
do {
endl(parse_inline_nowiki(ll[0].substring(1)) + "\n");
} while (remain() && $(' '));
ps('</pre>');
}
function parse_block_image()
{
ps(parse_image(sh()));
}
function parse_image(str)
{
//<NOLITE>
// get what's in between "[[Image:" and "]]"
var tag = str.substring(str.indexOf(':') + 1, str.length - 2);
/* eslint-disable no-unused-vars */
var width;
var attr = [], filename, caption = '';
var thumb=0, frame=0, center=0;
var align='';
/* eslint-enable no-unused-vars */
if (tag.match(/\|/)) {
// manage nested links
var nesting = 0;
var last_attr;
for (var i = tag.length-1; i > 0; i--) {
if (tag.charAt(i) == '|' && !nesting) {
last_attr = tag.substr(i+1);
tag = tag.substring(0, i);
break;
} else switch (tag.substr(i-1, 2)) {
case ']]':
nesting++;
i--;
break;
case '[[':
nesting--;
i--;
}
}
attr = tag.split(/\s*\|\s*/);
attr.push(last_attr);
filename = attr.shift();
var w_match;
for (;attr.length; attr.shift()) {
w_match = attr[0].match(/^(\d*)(?:[px]*\d*)?px$/);
if (w_match) width = w_match[1];
else switch(attr[0]) {
case 'thumb':
case 'thumbnail':
thumb=true;
frame=true;
break;
case 'frame':
frame=true;
break;
case 'none':
case 'right':
case 'left':
center=false;
align=attr[0];
break;
case 'center':
center=true;
align='none';
break;
default:
if (attr.length == 1) caption = attr[0];
}
}
} else filename = tag;
return '';
//</NOLITE>
}
function parse_inline_nowiki(str)
{
var start, lastend=0;
var substart=0, nestlev=0, open, close, subloop;
var html='';
while (-1 != (start = str.indexOf('<nowiki>', substart))) {
html += parse_inline_wiki(str.substring(lastend, start));
start += 8;
substart = start;
subloop = true;
do {
open = str.indexOf('<nowiki>', substart);
close = str.indexOf('</nowiki>', substart);
if (close<=open || open==-1) {
if (close==-1) {
return html + html_entities(str.substr(start));
}
substart = close+9;
if (nestlev) {
nestlev--;
} else {
lastend = substart;
html += html_entities(str.substring(start, lastend-9));
subloop = false;
}
} else {
substart = open+8;
nestlev++;
}
} while (subloop);
}
return html + parse_inline_wiki(str.substr(lastend));
}
function parse_inline_images(str)
{
//<NOLITE>
var start, substart=0, nestlev=0;
var loop, close, open, wiki, html;
while (-1 != (start=str.indexOf('[[', substart))) {
if(str.substr(start+2).match(RegExp('^(Image|File|' + Insta.conf.locale.image + '):','i'))) {
loop=true;
substart=start;
do {
substart+=2;
close=str.indexOf(']]',substart);
open=str.indexOf('[[',substart);
if (close<=open||open==-1) {
if (close==-1) return str;
substart=close;
if (nestlev) {
nestlev--;
} else {
wiki=str.substring(start,close+2);
html=parse_image(wiki);
str=str.replace(wiki,html);
substart=start+html.length;
loop=false;
}
} else {
substart=open;
nestlev++;
}
} while (loop);
} else break;
}
//</NOLITE>
return str;
}
// the output of this function doesn't respect the FILO structure of HTML
// but since most browsers can handle it I'll save myself the hassle
function parse_inline_formatting(str)
{
var em,st,i,li,o='';
while ((i=str.indexOf("''",li))+1) {
o += str.substring(li,i);
li=i+2;
if (str.charAt(i+2)=="'") {
li++;
st=!st;
o+=st?'<strong>':'</strong>';
} else {
em=!em;
o+=em?'<em>':'</em>';
}
}
return o+str.substr(li);
}
function parse_inline_wiki(str)
{
str = parse_inline_images(str);
str = parse_inline_formatting(str);
// math
str = str.replace(/<(?:)math>(.*?)<\/math>/ig, '');
// Build a Mediawiki-formatted date string
var date = new Date();
var minutes = date.getUTCMinutes();
if (minutes < 10) minutes = '0' + minutes;
date = f("?:?, ? ? ? (UTC)", date.getUTCHours(), minutes, date.getUTCDate(), Insta.conf.locale.months[date.getUTCMonth()], date.getUTCFullYear());
// text formatting
return str.
// signatures
replace(/~{5}(?!~)/g, date).
replace(/~{4}(?!~)/g, Insta.conf.user.name+' '+date).
replace(/~{3}(?!~)/g, Insta.conf.user.name).
// [[:Category:...]], [[:Image:...]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):[^|]*?)\\]\\](\\w*)','gi'), function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2));}).
// remove straight category and interwiki tags
replace(RegExp('\\[\\[(?:'+Insta.conf.locale.category+'|'+Insta.conf.wiki.interwiki+'):.*?\\]\\]','gi'),'').
// [[:Category:...|Links]], [[:Image:...|Links]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):.*?)\\|([^\\]]+?)\\]\\](\\w*)','gi'), function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3));}).
// [[/Relative links]]
replace(/\[\[(\/[^|]*?)\]\]/g, function($0,$1){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($1)); }).
// [[/Replaced|Relative links]]
replace(/\[\[(\/.*?)\|(.+?)\]\]/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($2)); }).
// [[Common links]]
replace(/\[\[([^[|]*?)\]\](\w*)/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2)); }).
// [[Replaced|Links]]
replace(/\[\[([^[]*?)\|([^\]]+?)\]\](\w*)/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3)); }).
// [[Stripped:Namespace|Namespace]]
replace(/\[\[([^\]]*?:)?(.*?)( *\(.*?\))?\|\]\]/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1) + htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($2)); }).
// External links
replace(/\[(https?|news|ftp|mailto|gopher|irc):(\/*)([^\]]*?) (.*?)\]/g, function($0,$1,$2,$3,$4){return f("<a class='external' href='?:?'>?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($4)); }).
replace(/\[http:\/\/(.*?)\]/g, function($0,$1){return f("<a class='external' href='http://?'>[#]</a>", htmlescape_attr($1)); }).
replace(/\[(news|ftp|mailto|gopher|irc):(\/*)(.*?)\]/g, function($0,$1,$2,$3){return f("<a class='external' href='?:?'>?:?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($1), htmlescape_text($2) + htmlescape_text($3)); }).
replace(/(^| )(https?|news|ftp|mailto|gopher|irc):(\/*)([^ $]*[^.,!?;: $])/g, function($0,$1,$2,$3,$4){return f("?<a class='external' href='?:?'>?:?</a>", htmlescape_text($1), htmlescape_attr($2), htmlescape_attr($3) + htmlescape_attr($4), htmlescape_text($2), htmlescape_text($3) + htmlescape_text($4)); }).
replace('__NOTOC__','').
replace('__NOEDITSECTION__','');
}
// begin parsing
for (;remain();) if ($(/^(={1,6})(.*)\1(.*)$/)) {
p=0;
endl(f('<h?>?</h?>?', $r[1].length, parse_inline_nowiki($r[2]), $r[1].length, $r[3]));
} else if ($(/^[*#:;]/)) {
p=0;
parse_list();
} else if ($(' ')) {
p=0;
parse_pre();
} else if ($('{|')) {
p=0;
parse_table();
} else if ($(/^----+$/)) {
p=0;
endl('<hr />');
} else if ($(Insta.BLOCK_IMAGE)) {
p=0;
parse_block_image();
} else {
// handle paragraphs
if ($$('')) {
p = (remain()>1 && ll[1]===(''));
if (p) endl('<p><br>');
} else {
if(!p) {
ps('<p>');
p=1;
}
ps(parse_inline_nowiki(ll[0]) + ' ');
}
sh();
}
return o;
};
function wiki2html(txt,baseurl) {
Insta.conf.baseUrl=baseurl;
return Insta.convert(txt);
}
// ENDFILE: livepreview.js
// STARTFILE: pageinfo.js
//<NOLITE>
function popupFilterPageSize(data) {
return formatBytes(data.length);
}
function popupFilterCountLinks(data) {
var num=countLinks(data);
return String(num) + ' ' + ((num!=1)?popupString('wikiLinks'):popupString('wikiLink'));
}
function popupFilterCountImages(data) {
var num=countImages(data);
return String(num) + ' ' + ((num!=1)?popupString('images'):popupString('image'));
}
function popupFilterCountCategories(data) {
var num=countCategories(data);
return String(num) + ' ' + ((num!=1)?popupString('categories'):popupString('category'));
}
function popupFilterLastModified(data,download) {
var lastmod=download.lastModified;
var now=new Date();
var age=now-lastmod;
if (lastmod && getValueOf('popupLastModified')) {
return (tprintf('%s old', [formatAge(age)])).replace(RegExp(' ','g'), ' ');
}
return '';
}
function formatAge(age) {
// coerce into a number
var a=0+age, aa=a;
var seclen = 1000;
var minlen = 60*seclen;
var hourlen = 60*minlen;
var daylen = 24*hourlen;
var weeklen = 7*daylen;
var numweeks = (a-a%weeklen)/weeklen; a = a-numweeks*weeklen; var sweeks = addunit(numweeks, 'week');
var numdays = (a-a%daylen)/daylen; a = a-numdays*daylen; var sdays = addunit(numdays, 'day');
var numhours = (a-a%hourlen)/hourlen; a = a-numhours*hourlen; var shours = addunit(numhours,'hour');
var nummins = (a-a%minlen)/minlen; a = a-nummins*minlen; var smins = addunit(nummins, 'minute');
var numsecs = (a-a%seclen)/seclen; a = a-numsecs*seclen; var ssecs = addunit(numsecs, 'second');
if (aa > 4*weeklen) { return sweeks; }
if (aa > weeklen) { return sweeks + ' ' + sdays; }
if (aa > daylen) { return sdays + ' ' + shours; }
if (aa > 6*hourlen) { return shours; }
if (aa > hourlen) { return shours + ' ' + smins; }
if (aa > 10*minlen) { return smins; }
if (aa > minlen) { return smins + ' ' + ssecs; }
return ssecs;
}
function addunit(num,str) { return '' + num + ' ' + ((num!=1) ? popupString(str+'s') : popupString(str)) ;}
function runPopupFilters(list, data, download) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (list[i] && typeof list[i] == 'function') {
var s=list[i](data, download, download.owner.article);
if (s) { ret.push(s); }
}
}
return ret;
}
function getPageInfo(data, download) {
if (!data || data.length === 0) { return popupString('Empty page'); }
var popupFilters=getValueOf('popupFilters') || [];
var extraPopupFilters = getValueOf('extraPopupFilters') || [];
var pageInfoArray = runPopupFilters(popupFilters.concat(extraPopupFilters), data, download);
var pageInfo=pageInfoArray.join(', ');
if (pageInfo !== '' ) { pageInfo = upcaseFirst(pageInfo); }
return pageInfo;
}
// this could be improved!
function countLinks(wikiText) { return wikiText.split('[[').length - 1; }
// if N = # matches, n = # brackets, then
// String.parenSplit(regex) intersperses the N+1 split elements
// with Nn other elements. So total length is
// L= N+1 + Nn = N(n+1)+1. So N=(L-1)/(n+1).
function countImages(wikiText) {
return (wikiText.parenSplit(pg.re.image).length - 1) / (pg.re.imageBracketCount + 1);
}
function countCategories(wikiText) {
return (wikiText.parenSplit(pg.re.category).length - 1) / (pg.re.categoryBracketCount + 1);
}
function popupFilterStubDetect(data, download, article) {
var counts=stubCount(data, article);
if (counts.real) { return popupString('stub'); }
if (counts.sect) { return popupString('section stub'); }
return '';
}
function popupFilterDisambigDetect(data, download, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return ''; }
return (isDisambig(data, article)) ? popupString('disambig') : '';
}
function formatBytes(num) {
return (num > 949) ? (Math.round(num/100)/10+popupString('kB')) : (num +' ' + popupString('bytes')) ;
}
//</NOLITE>
// ENDFILE: pageinfo.js
// STARTFILE: titles.js
/**
@fileoverview Defines the {@link Title} class, and associated crufty functions.
<code>Title</code> deals with article titles and their various
forms. {@link Stringwrapper} is the parent class of
<code>Title</code>, which exists simply to make things a little
neater.
*/
/**
Creates a new Stringwrapper.
@constructor
@class the Stringwrapper class. This base class is not really
useful on its own; it just wraps various common string operations.
*/
function Stringwrapper() {
/**
Wrapper for this.toString().indexOf()
@param {String} x
@type integer
*/
this.indexOf=function(x){return this.toString().indexOf(x);};
/**
Returns this.value.
@type String
*/
this.toString=function(){return this.value;};
/**
Wrapper for {@link String#parenSplit} applied to this.toString()
@param {RegExp} x
@type Array
*/
this.parenSplit=function(x){return this.toString().parenSplit(x);};
/**
Wrapper for this.toString().substring()
@param {String} x
@param {String} y (optional)
@type String
*/
this.substring=function(x,y){
if (typeof y=='undefined') { return this.toString().substring(x); }
return this.toString().substring(x,y);
};
/**
Wrapper for this.toString().split()
@param {String} x
@type Array
*/
this.split=function(x){return this.toString().split(x);};
/**
Wrapper for this.toString().replace()
@param {String} x
@param {String} y
@type String
*/
this.replace=function(x,y){ return this.toString().replace(x,y); };
}
/**
Creates a new <code>Title</code>.
@constructor
@class The Title class. Holds article titles and converts them into
various forms. Also deals with anchors, by which we mean the bits
of the article URL after a # character, representing locations
within an article.
@param {String} value The initial value to assign to the
article. This must be the canonical title (see {@link
Title#value}. Omit this in the constructor and use another function
to set the title if this is unavailable.
*/
function Title(val) {
/**
The canonical article title. This must be in UTF-8 with no
entities, escaping or nasties. Also, underscores should be
replaced with spaces.
@type String
@private
*/
this.value=null;
/**
The canonical form of the anchor. This should be exactly as
it appears in the URL, i.e. with the .C3.0A bits in.
@type String
*/
this.anchor='';
this.setUtf(val);
}
Title.prototype=new Stringwrapper();
/**
Returns the canonical representation of the article title, optionally without anchor.
@param {boolean} omitAnchor
@fixme Decide specs for anchor
@return String The article title and the anchor.
*/
Title.prototype.toString=function(omitAnchor) {
return this.value + ( (!omitAnchor && this.anchor) ? '#' + this.anchorString() : '' );
};
Title.prototype.anchorString=function() {
if (!this.anchor) { return ''; }
var split=this.anchor.parenSplit(/((?:[.][0-9A-F]{2})+)/);
var len=split.length;
for (var j=1; j<len; j+=2) {
// FIXME s/decodeURI/decodeURIComponent/g ?
split[j]=decodeURIComponent(split[j].split('.').join('%')).split('_').join(' ');
}
return split.join('');
};
Title.prototype.urlAnchor=function() {
var split=this.anchor.parenSplit('/((?:[%][0-9A-F]{2})+)/');
var len=split.length;
for (var j=1; j<len; j+=2) {
split[j]=split[j].split('%').join('.');
}
return split.join('');
};
Title.prototype.anchorFromUtf=function(str) {
this.anchor=encodeURIComponent(str.split(' ').join('_'))
.split('%3A').join(':').split("'").join('%27').split('%').join('.');
};
Title.fromURL=function(h) {
return new Title().fromURL(h);
};
Title.prototype.fromURL=function(h) {
if (typeof h != 'string') {
this.value=null;
return this;
}
// NOTE : playing with decodeURI, encodeURI, escape, unescape,
// we seem to be able to replicate the IE borked encoding
// IE doesn't do this new-fangled utf-8 thing.
// and it's worse than that.
// IE seems to treat the query string differently to the rest of the url
// the query is treated as bona-fide utf8, but the first bit of the url is pissed around with
// we fix up & for all browsers, just in case.
var splitted=h.split('?');
splitted[0]=splitted[0].split('&').join('%26');
h=splitted.join('?');
var contribs=pg.re.contribs.exec(h);
if (contribs) {
if (contribs[1]=='title=') { contribs[3]=contribs[3].split('+').join(' '); }
var u=new Title(contribs[3]);
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + u.stripNamespace()));
return this;
}
var email=pg.re.email.exec(h);
if (email) {
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(email[3]).stripNamespace()));
return this;
}
var backlinks=pg.re.backlinks.exec(h);
if (backlinks) {
this.setUtf(this.decodeNasties(new Title(backlinks[3])));
return this;
}
//A dummy title object for a Special:Diff link.
var specialdiff=pg.re.specialdiff.exec(h);
if (specialdiff) {
this.setUtf(this.decodeNasties(new Title(mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Diff')));
return this;
}
// no more special cases to check --
// hopefully it's not a disguised user-related or specially treated special page
var m=pg.re.main.exec(h);
if(m === null) { this.value=null; }
else {
var fromBotInterface = /[?](.+[&])?title=/.test(h);
if (fromBotInterface) {
m[2]=m[2].split('+').join('_');
}
var extracted = m[2] + (m[3] ? '#' + m[3] : '');
if (pg.flag.isSafari && /%25[0-9A-Fa-f]{2}/.test(extracted)) {
// Fix Safari issue
// Safari sometimes encodes % as %25 in UTF-8 encoded strings like %E5%A3 -> %25E5%25A3.
this.setUtf(decodeURIComponent(unescape(extracted)));
} else {
this.setUtf(this.decodeNasties(extracted));
}
}
return this;
};
Title.prototype.decodeNasties=function(txt) {
var ret= this.decodeEscapes(decodeURI(txt));
ret = ret.replace(/[_ ]*$/, '');
return ret;
};
Title.prototype.decodeEscapes=function(txt) {
var split=txt.parenSplit(/((?:[%][0-9A-Fa-f]{2})+)/);
var len=split.length;
for (var i=1; i<len; i=i+2) {
// FIXME is decodeURIComponent better?
split[i]=unescape(split[i]);
}
return split.join('');
};
Title.fromAnchor=function(a) {
return new Title().fromAnchor(a);
};
Title.prototype.fromAnchor=function(a) {
if (!a) { this.value=null; return this; }
return this.fromURL(a.href);
};
Title.fromWikiText=function(txt) {
return new Title().fromWikiText(txt);
};
Title.prototype.fromWikiText=function(txt) {
// FIXME - testing needed
txt=myDecodeURI(txt);
this.setUtf(txt);
return this;
};
Title.prototype.hintValue=function(){
if(!this.value) { return ''; }
return safeDecodeURI(this.value);
};
//<NOLITE>
Title.prototype.toUserName=function(withNs) {
if (this.namespaceId() != pg.nsUserId && this.namespaceId() != pg.nsUsertalkId) {
this.value=null;
return;
}
this.value = (withNs ? mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' : '') + this.stripNamespace().split('/')[0];
};
Title.prototype.userName=function(withNs) {
var t=(new Title(this.value));
t.toUserName(withNs);
if (t.value) { return t; }
return null;
};
Title.prototype.toTalkPage=function() {
// convert article to a talk page, or if we can't, return null
// In other words: return null if this ALREADY IS a talk page
// and return the corresponding talk page otherwise
//
// Per https://www.mediawiki.org/wiki/Manual:Namespace#Subject_and_talk_namespaces
// * All discussion namespaces have odd-integer indices
// * The discussion namespace index for a specific namespace with index n is n + 1
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId>=0 && namespaceId % 2 === 0) //non-special and subject namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId+1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
//</NOLITE>
// Return canonical, localized namespace
Title.prototype.namespace=function() {
return mw.config.get('wgFormattedNamespaces')[this.namespaceId()];
};
Title.prototype.namespaceId=function() {
var n=this.value.indexOf(':');
if (n<0) { return 0; } //mainspace
var namespaceId = mw.config.get('wgNamespaceIds')[this.value.substring(0,n).split(' ').join('_').toLowerCase()];
if (typeof namespaceId=='undefined') return 0; //mainspace
return namespaceId;
};
//<NOLITE>
Title.prototype.talkPage=function() {
var t=new Title(this.value);
t.toTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.isTalkPage=function() {
if (this.talkPage()===null) { return true; }
return false;
};
Title.prototype.toArticleFromTalkPage=function() {
//largely copy/paste from toTalkPage above.
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId >= 0 && namespaceId % 2 == 1) //non-special and talk namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId-1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
Title.prototype.articleFromTalkPage=function() {
var t=new Title(this.value);
t.toArticleFromTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.articleFromTalkOrArticle=function() {
var t=new Title(this.value);
if ( t.toArticleFromTalkPage() ) { return t; }
return this;
};
Title.prototype.isIpUser=function() {
return pg.re.ipUser.test(this.userName());
};
//</NOLITE>
Title.prototype.stripNamespace=function(){ // returns a string, not a Title
var n=this.value.indexOf(':');
if (n<0) { return this.value; }
var namespaceId = this.namespaceId();
if (namespaceId === pg.nsMainspaceId) return this.value;
return this.value.substring(n+1);
};
Title.prototype.setUtf=function(value){
if (!value) { this.value=''; return; }
var anch=value.indexOf('#');
if(anch < 0) { this.value=value.split('_').join(' '); this.anchor=''; return; }
this.value=value.substring(0,anch).split('_').join(' ');
this.anchor=value.substring(anch+1);
this.ns=null; // wait until namespace() is called
};
Title.prototype.setUrl=function(urlfrag) {
var anch=urlfrag.indexOf('#');
this.value=safeDecodeURI(urlfrag.substring(0,anch));
this.anchor=this.value.substring(anch+1);
};
Title.prototype.append=function(x){
this.setUtf(this.value + x);
};
Title.prototype.urlString=function(x) {
if(!x) { x={}; }
var v=this.toString(true);
if (!x.omitAnchor && this.anchor) { v+= '#' + this.urlAnchor(); }
if (!x.keepSpaces) { v=v.split(' ').join('_'); }
return encodeURI(v).split('&').join('%26').split('?').join('%3F').split('+').join('%2B');
};
Title.prototype.removeAnchor=function() {
return new Title(this.toString(true));
};
Title.prototype.toUrl=function() {
return pg.wiki.titlebase + this.urlString();
};
function parseParams(url) {
var specialDiff = pg.re.specialdiff.exec(url);
if (specialDiff)
{
var split= specialDiff[1].split('/');
if (split.length==1) return {oldid:split[0], diff: 'prev'};
else if (split.length==2) return {oldid: split[0], diff: split[1]};
}
var ret={};
if (url.indexOf('?')==-1) { return ret; }
url = url.split('#')[0];
var s=url.split('?').slice(1).join();
var t=s.split('&');
for (var i=0; i<t.length; ++i) {
var z=t[i].split('=');
z.push(null);
ret[z[0]]=z[1];
}
//Diff revision with no oldid is interpreted as a diff to the previous revision by MediaWiki
if (ret.diff && typeof(ret.oldid)==='undefined')
{
ret.oldid = "prev";
}
//Documentation seems to say something different, but oldid can also accept prev/next, and Echo is emitting such URLs. Simple fixup during parameter decoding:
if (ret.oldid && (ret.oldid==='prev' || ret.oldid==='next' || ret.oldid==='cur'))
{
var helper = ret.diff;
ret.diff = ret.oldid;
ret.oldid = helper;
}
return ret;
}
// (a) myDecodeURI (first standard decodeURI, then pg.re.urlNoPopup)
// (b) change spaces to underscores
// (c) encodeURI (just the straight one, no pg.re.urlNoPopup)
function myDecodeURI (str) {
var ret;
// FIXME decodeURIComponent??
try { ret=decodeURI(str.toString()); }
catch (summat) { return str; }
for (var i=0; i<pg.misc.decodeExtras.length; ++i) {
var from=pg.misc.decodeExtras[i].from;
var to=pg.misc.decodeExtras[i].to;
ret=ret.split(from).join(to);
}
return ret;
}
function safeDecodeURI(str) { var ret=myDecodeURI(str); return ret || str; }
///////////
// TESTS //
///////////
//<NOLITE>
function isDisambig(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
return ! article.isTalkPage() && pg.re.disambig.test(data);
}
function stubCount(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
var sectStub=0;
var realStub=0;
if (pg.re.stub.test(data)) {
var s=data.parenSplit(pg.re.stub);
for (var i=1; i<s.length; i=i+2) {
if (s[i]) { ++sectStub; }
else { ++realStub; }
}
}
return { real: realStub, sect: sectStub };
}
function isValidImageName(str){ // extend as needed...
return ( str.indexOf('{') == -1 );
}
function isInStrippableNamespace(article) {
// Does the namespace allow subpages
// Note, would be better if we had access to wgNamespacesWithSubpages
return ( article.namespaceId() !== 0 );
}
function isInMainNamespace(article) { return article.namespaceId() === 0; }
function anchorContainsImage(a) {
// iterate over children of anchor a
// see if any are images
if (a === null) { return false; }
var kids=a.childNodes;
for (var i=0; i<kids.length; ++i) { if (kids[i].nodeName=='IMG') { return true; } }
return false;
}
//</NOLITE>
function isPopupLink(a) {
// NB for performance reasons, TOC links generally return true
// they should be stripped out later
if (!markNopopupSpanLinks.done) { markNopopupSpanLinks(); }
if (a.inNopopupSpan) { return false; }
// FIXME is this faster inline?
if (a.onmousedown || a.getAttribute('nopopup')) { return false; }
var h=a.href;
if (h === document.location.href+'#') { return false; }
if (!pg.re.basenames.test(h)) { return false; }
if (!pg.re.urlNoPopup.test(h)) { return true; }
return (
(pg.re.email.test(h) || pg.re.contribs.test(h) || pg.re.backlinks.test(h) || pg.re.specialdiff.test(h)) &&
h.indexOf('&limit=') == -1 );
}
function markNopopupSpanLinks() {
if( !getValueOf('popupOnlyArticleLinks'))
fixVectorMenuPopups();
var s = $('.nopopups').toArray();
for (var i=0; i<s.length; ++i) {
var as=s[i].getElementsByTagName('a');
for (var j=0; j<as.length; ++j) {
as[j].inNopopupSpan=true;
}
}
markNopopupSpanLinks.done=true;
}
function fixVectorMenuPopups() {
$('div.vectorMenu h3:first a:first').prop('inNopopupSpan', true);
}
// ENDFILE: titles.js
// STARTFILE: getpage.js
//////////////////////////////////////////////////
// Wiki-specific downloading
//
// Schematic for a getWiki call
//
// getPageWithCaching
// |
// false | true
// getPage<-[findPictureInCache]->-onComplete(a fake download)
// \.
// (async)->addPageToCache(download)->-onComplete(download)
// check cache to see if page exists
function getPageWithCaching(url, onComplete, owner) {
log('getPageWithCaching, url='+url);
var i=findInPageCache(url);
var d;
if (i > -1) {
d=fakeDownload(url, owner.idNumber, onComplete,
pg.cache.pages[i].data, pg.cache.pages[i].lastModified,
owner);
} else {
d=getPage(url, onComplete, owner);
if (d && owner && owner.addDownload) {
owner.addDownload(d);
d.owner=owner;
}
}
}
function getPage(url, onComplete, owner) {
log('getPage');
var callback= function (d) { if (!d.aborted) {addPageToCache(d); onComplete(d);} };
return startDownload(url, owner.idNumber, callback);
}
function findInPageCache(url) {
for (var i=0; i<pg.cache.pages.length; ++i) {
if (url==pg.cache.pages[i].url) { return i; }
}
return -1;
}
function addPageToCache(download) {
log('addPageToCache '+download.url);
var page = {url: download.url, data: download.data, lastModified: download.lastModified};
return pg.cache.pages.push(page);
}
// ENDFILE: getpage.js
// STARTFILE: parensplit.js
//////////////////////////////////////////////////
// parenSplit
// String.prototype.parenSplit should do what ECMAscript says String.prototype.split does,
// interspersing paren matches (regex capturing groups) between the split elements.
// i.e. 'abc'.split(/(b)/)) should return ['a','b','c'], not ['a','c']
if (String('abc'.split(/(b)/))!='a,b,c') {
// broken String.split, e.g. konq, IE < 10
String.prototype.parenSplit=function (re) {
re=nonGlobalRegex(re);
var s=this;
var m=re.exec(s);
var ret=[];
while (m && s) {
// without the following loop, we have
// 'ab'.parenSplit(/a|(b)/) != 'ab'.split(/a|(b)/)
for(var i=0; i<m.length; ++i) {
if (typeof m[i]=='undefined') m[i]='';
}
ret.push(s.substring(0,m.index));
ret = ret.concat(m.slice(1));
s=s.substring(m.index + m[0].length);
m=re.exec(s);
}
ret.push(s);
return ret;
};
} else {
String.prototype.parenSplit=function (re) { return this.split(re); };
String.prototype.parenSplit.isNative=true;
}
function nonGlobalRegex(re) {
var s=re.toString();
var flags='';
for (var j=s.length; s.charAt(j) != '/'; --j) {
if (s.charAt(j) != 'g') { flags += s.charAt(j); }
}
var t=s.substring(1,j);
return RegExp(t,flags);
}
// ENDFILE: parensplit.js
// STARTFILE: tools.js
// IE madness with encoding
// ========================
//
// suppose throughout that the page is in utf8, like wikipedia
//
// if a is an anchor DOM element and a.href should consist of
//
// http://host.name.here/wiki/foo?bar=baz
//
// then IE gives foo as "latin1-encoded" utf8; we have foo = decode_utf8(decodeURI(foo_ie))
// but IE gives bar=baz correctly as plain utf8
//
// ---------------------------------
//
// IE's xmlhttp doesn't understand utf8 urls. Have to use encodeURI here.
//
// ---------------------------------
//
// summat else
// Source: http://aktuell.de.selfhtml.org/artikel/javascript/utf8b64/utf8.htm
//<NOLITE>
function getJsObj(json) {
try {
var json_ret = JSON.parse(json);
if( json_ret.warnings ) {
for( var w=0; w < json_ret.warnings.length; w++ ) {
if( json_ret.warnings[w]['*'] ) {
log( json_ret.warnings[w]['*'] );
} else {
log( json_ret.warnings[w]['warnings'] );
}
}
} else if ( json_ret.error ) {
errlog( json_ret.error.code + ': ' + json_ret.error.info );
}
return json_ret;
} catch (someError) {
errlog('Something went wrong with getJsObj, json='+json);
return 1;
}
}
function anyChild(obj) {
for (var p in obj) {
return obj[p];
}
return null;
}
//</NOLITE>
function upcaseFirst(str) {
if (typeof str != typeof '' || str === '') return '';
return str.charAt(0).toUpperCase() + str.substring(1);
}
function findInArray(arr, foo) {
if (!arr || !arr.length) { return -1; }
var len=arr.length;
for (var i=0; i<len; ++i) { if (arr[i]==foo) { return i; } }
return -1;
}
/* eslint-disable no-unused-vars */
function nextOne (array, value) {
// NB if the array has two consecutive entries equal
// then this will loop on successive calls
var i=findInArray(array, value);
if (i<0) { return null; }
return array[i+1];
}
/* eslint-enable no-unused-vars */
function literalizeRegex(str){
return mw.util.escapeRegExp(str);
}
String.prototype.entify=function() {
//var shy='­';
return this.split('&').join('&').split('<').join('<').split('>').join('>'/*+shy*/).split('"').join('"');
};
// Array filter function
function removeNulls(val) { return val !== null; }
function joinPath(list) {
return list.filter(removeNulls).join('/');
}
function simplePrintf(str, subs) {
if (!str || !subs) { return str; }
var ret=[];
var s=str.parenSplit(/(%s|\$[0-9]+)/);
var i=0;
do {
ret.push(s.shift());
if ( !s.length ) { break; }
var cmd=s.shift();
if (cmd == '%s') {
if ( i < subs.length ) { ret.push(subs[i]); } else { ret.push(cmd); }
++i;
} else {
var j=parseInt( cmd.replace('$', ''), 10 ) - 1;
if ( j > -1 && j < subs.length ) { ret.push(subs[j]); } else { ret.push(cmd); }
}
} while (s.length > 0);
return ret.join('');
}
/* eslint-disable no-unused-vars */
function isString(x) { return (typeof x === 'string' || x instanceof String); }
function isNumber(x) { return (typeof x === 'number' || x instanceof Number); }
function isRegExp(x) { return x instanceof RegExp; }
function isArray (x) { return x instanceof Array; }
function isObject(x) { return x instanceof Object; }
function isFunction(x) {
return !isRegExp(x) && (typeof x === 'function' || x instanceof Function);
}
/* eslint-enable no-unused-vars */
function repeatString(s,mult) {
var ret='';
for (var i=0; i<mult; ++i) { ret += s; }
return ret;
}
function zeroFill(s, min) {
min = min || 2;
var t=s.toString();
return repeatString('0', min - t.length) + t;
}
function map(f, o) {
if (isArray(o)) { return map_array(f,o); }
return map_object(f,o);
}
function map_array(f,o) {
var ret=[];
for (var i=0; i<o.length; ++i) {
ret.push(f(o[i]));
}
return ret;
}
function map_object(f,o) {
var ret={};
for (var i in o) { ret[o]=f(o[i]); }
return ret;
}
pg.escapeQuotesHTML = function ( text ) {
return text
.replace(/&/g, "&")
.replace(/"/g, """)
.replace(/</g, "<")
.replace(/>/g, ">");
};
// ENDFILE: tools.js
// STARTFILE: dab.js
//<NOLITE>
//////////////////////////////////////////////////
// Dab-fixing code
//
function retargetDab(newTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) {
log('retargetDab: newTarget='+newTarget + ' oldTarget=' + oldTarget);
return changeLinkTargetLink(
{newTarget: newTarget,
text: newTarget.split(' ').join(' '),
hint: tprintf('disambigHint', [newTarget]),
summary: simplePrintf(
getValueOf('popupFixDabsSummary'), [friendlyCurrentArticleName, newTarget ]),
clickButton: getValueOf('popupDabsAutoClick'), minor: true, oldTarget: oldTarget,
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit});
}
function listLinks(wikitext, oldTarget, titleToEdit) {
// mediawiki strips trailing spaces, so we do the same
// testcase: https://en.wikipedia.org/w/index.php?title=Radial&oldid=97365633
var reg=RegExp('\\[\\[([^|]*?) *(\\||\\]\\])', 'gi');
var ret=[];
var splitted=wikitext.parenSplit(reg);
// ^[a-z]+ should match interwiki links, hopefully (case-insensitive)
// and ^[a-z]* should match those and [[:Category...]] style links too
var omitRegex=RegExp('^[a-z]*:|^[Ss]pecial:|^[Ii]mage|^[Cc]ategory');
var friendlyCurrentArticleName= oldTarget.toString();
var wikPos = getValueOf('popupDabWiktionary');
for (var i=1; i<splitted.length; i=i+3) {
if (typeof splitted[i] == typeof 'string' && splitted[i].length>0 && !omitRegex.test(splitted[i])) {
ret.push( retargetDab(splitted[i], oldTarget, friendlyCurrentArticleName, titleToEdit) );
} /* if */
} /* for loop */
ret = rmDupesFromSortedList(ret.sort());
if (wikPos) {
var wikTarget='wiktionary:' +
friendlyCurrentArticleName.replace( RegExp('^(.+)\\s+[(][^)]+[)]\\s*$'), '$1' );
var meth;
if (wikPos.toLowerCase() == 'first') { meth = 'unshift'; }
else { meth = 'push'; }
ret[meth]( retargetDab(wikTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) );
}
ret.push(changeLinkTargetLink(
{ newTarget: null,
text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this disambig page from this article"),
clickButton: getValueOf('popupDabsAutoClick'), oldTarget: oldTarget,
summary: simplePrintf(getValueOf('popupRmDabLinkSummary'), [friendlyCurrentArticleName]),
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit
}));
return ret;
}
function rmDupesFromSortedList(list) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (ret.length === 0 || list[i]!=ret[ret.length-1]) { ret.push(list[i]); }
}
return ret;
}
function makeFixDab(data, navpop) {
// grab title from parent popup if there is one; default exists in changeLinkTargetLink
var titleToEdit=(navpop.parentPopup && navpop.parentPopup.article.toString());
var list=listLinks(data, navpop.originalArticle, titleToEdit);
if (list.length === 0) { log('listLinks returned empty list'); return null; }
var html='<hr />' + popupString('Click to disambiguate this link to:') + '<br>';
html+=list.join(', ');
return html;
}
function makeFixDabs(wikiText, navpop) {
if (getValueOf('popupFixDabs') && isDisambig(wikiText, navpop.article) &&
Title.fromURL(location.href).namespaceId() != pg.nsSpecialId &&
navpop.article.talkPage() ) {
setPopupHTML(makeFixDab(wikiText, navpop), 'popupFixDab', navpop.idNumber);
}
}
function popupRedlinkHTML(article) {
return changeLinkTargetLink(
{ newTarget: null, text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this page from this article"),
clickButton: getValueOf('popupRedlinkAutoClick'),
oldTarget: article.toString(),
summary: simplePrintf(getValueOf('popupRedlinkSummary'), [article.toString()])});
}
//</NOLITE>
// ENDFILE: dab.js
// STARTFILE: htmloutput.js
// this has to use a timer loop as we don't know if the DOM element exists when we want to set the text
function setPopupHTML (str, elementId, popupId, onSuccess, append) {
if (typeof popupId === 'undefined') {
//console.error('popupId is not defined in setPopupHTML, html='+str.substring(0,100));
popupId = pg.idNumber;
}
var popupElement=document.getElementById(elementId+popupId);
if (popupElement) {
if (!append) { popupElement.innerHTML=''; }
if (isString(str)) {
popupElement.innerHTML+=str;
} else {
popupElement.appendChild(str);
}
if (onSuccess) { onSuccess(); }
setTimeout(checkPopupPosition, 100);
return true;
} else {
// call this function again in a little while...
setTimeout(function(){
setPopupHTML(str,elementId,popupId,onSuccess);
}, 600);
}
return null;
}
//<NOLITE>
function setPopupTrailer(str,id) {return setPopupHTML(str, 'popupData', id);}
//</NOLITE>
// args.navpopup is mandatory
// optional: args.redir, args.redirTarget
// FIXME: ye gods, this is ugly stuff
function fillEmptySpans(args) {
// if redir is present and true then redirTarget is mandatory
var redir=true;
var rcid;
if (typeof args != 'object' || typeof args.redir == 'undefined' || !args.redir) { redir=false; }
var a=args.navpopup.parentAnchor;
var article, hint=null, oldid=null, params={};
if (redir && typeof args.redirTarget == typeof {}) {
article=args.redirTarget;
//hint=article.hintValue();
} else {
article=(new Title()).fromAnchor(a);
hint=a.originalTitle || article.hintValue();
params=parseParams(a.href);
oldid=(getValueOf('popupHistoricalLinks')) ? params.oldid : null;
rcid=params.rcid;
}
var x={ article:article, hint: hint, oldid: oldid, rcid: rcid, navpop:args.navpopup, params:params };
var structure=pg.structures[getValueOf('popupStructure')];
if (typeof structure != 'object') {
setPopupHTML('popupError', 'Unknown structure (this should never happen): '+
pg.option.popupStructure, args.navpopup.idNumber);
return;
}
var spans=flatten(pg.misc.layout);
var numspans = spans.length;
var redirs=pg.misc.redirSpans;
for (var i=0; i<numspans; ++i) {
var found = redirs && (redirs.indexOf( spans[i] ) !== -1);
//log('redir='+redir+', found='+found+', spans[i]='+spans[i]);
if ( (found && !redir) || (!found && redir) ) {
//log('skipping this set of the loop');
continue;
}
var structurefn=structure[spans[i]];
var setfn = setPopupHTML;
if (getValueOf('popupActiveNavlinks') &&
(spans[i].indexOf('popupTopLinks')===0 || spans[i].indexOf('popupRedirTopLinks')===0)
) {
setfn = setPopupTipsAndHTML;
}
switch (typeof structurefn) {
case 'function':
log('running '+spans[i]+'({article:'+x.article+', hint:'+x.hint+', oldid: '+x.oldid+'})');
setfn(structurefn(x), spans[i], args.navpopup.idNumber);
break;
case 'string':
setfn(structurefn, spans[i], args.navpopup.idNumber);
break;
default:
errlog('unknown thing with label '+spans[i] + ' (span index was ' + i + ')');
break;
}
}
}
// flatten an array
function flatten(list, start) {
var ret=[];
if (typeof start == 'undefined') { start=0; }
for (var i=start; i<list.length; ++i) {
if (typeof list[i] == typeof []) {
return ret.concat(flatten(list[i])).concat(flatten(list, i+1));
}
else { ret.push(list[i]); }
}
return ret;
}
// Generate html for whole popup
function popupHTML (a) {
getValueOf('popupStructure');
var structure=pg.structures[pg.option.popupStructure];
if (typeof structure != 'object') {
//return 'Unknown structure: '+pg.option.popupStructure;
// override user choice
pg.option.popupStructure=pg.optionDefault.popupStructure;
return popupHTML(a);
}
if (typeof structure.popupLayout != 'function') { return 'Bad layout'; }
pg.misc.layout=structure.popupLayout();
if (typeof structure.popupRedirSpans === 'function') { pg.misc.redirSpans=structure.popupRedirSpans(); }
else { pg.misc.redirSpans=[]; }
return makeEmptySpans(pg.misc.layout, a.navpopup);
}
function makeEmptySpans (list, navpop) {
var ret='';
for (var i=0; i<list.length; ++i) {
if (typeof list[i] == typeof '') {
ret += emptySpanHTML(list[i], navpop.idNumber, 'div');
} else if (typeof list[i] == typeof [] && list[i].length > 0 ) {
ret = ret.parenSplit(RegExp('(</[^>]*?>$)')).join(makeEmptySpans(list[i], navpop));
} else if (typeof list[i] == typeof {} && list[i].nodeType ) {
ret += emptySpanHTML(list[i].name, navpop.idNumber, list[i].nodeType);
}
}
return ret;
}
function emptySpanHTML(name, id, tag, classname) {
tag = tag || 'span';
if (!classname) { classname = emptySpanHTML.classAliases[name]; }
classname = classname || name;
if (name == getValueOf('popupDragHandle')) { classname += ' popupDragHandle'; }
return simplePrintf('<%s id="%s" class="%s"></%s>', [tag, name + id, classname, tag]);
}
emptySpanHTML.classAliases={ 'popupSecondPreview': 'popupPreview' };
// generate html for popup image
// <a id="popupImageLinkn"><img id="popupImagen">
// where n=idNumber
function imageHTML(article, idNumber) {
return simplePrintf('<a id="popupImageLink$1">' +
'<img align="right" valign="top" id="popupImg$1" style="display: none;"></img>' +
'</a>', [ idNumber ]);
}
function popTipsSoonFn(id, when, popData) {
if (!when) { when=250; }
var popTips=function(){ setupTooltips(document.getElementById(id), false, true, popData); };
return function() { setTimeout( popTips, when, popData ); };
}
function setPopupTipsAndHTML(html, divname, idnumber, popData) {
setPopupHTML(html, divname, idnumber,
getValueOf('popupSubpopups') ?
popTipsSoonFn(divname + idnumber, null, popData) :
null);
}
// ENDFILE: htmloutput.js
// STARTFILE: mouseout.js
//////////////////////////////////////////////////
// fuzzy checks
function fuzzyCursorOffMenus(x,y, fuzz, parent) {
if (!parent) { return null; }
var uls=parent.getElementsByTagName('ul');
for (var i=0; i<uls.length; ++i) {
if (uls[i].className=='popup_menu') {
if (uls[i].offsetWidth > 0) return false;
} // else {document.title+='.';}
}
return true;
}
function checkPopupPosition () { // stop the popup running off the right of the screen
// FIXME avoid pg.current.link
if (pg.current.link && pg.current.link.navpopup)
pg.current.link.navpopup.limitHorizontalPosition();
}
function mouseOutWikiLink () {
//console ('mouseOutWikiLink');
var a=this;
removeModifierKeyHandler(a);
if (a.navpopup === null || typeof a.navpopup === 'undefined') return;
if ( ! a.navpopup.isVisible() ) {
a.navpopup.banish();
return;
}
restoreTitle(a);
Navpopup.tracker.addHook(posCheckerHook(a.navpopup));
}
function posCheckerHook(navpop) {
return function() {
if (!navpop.isVisible()) { return true; /* remove this hook */ }
if (Navpopup.tracker.dirty) {
return false;
}
var x=Navpopup.tracker.x, y=Navpopup.tracker.y;
var mouseOverNavpop = navpop.isWithin(x,y,navpop.fuzz, navpop.mainDiv) ||
!fuzzyCursorOffMenus(x,y,navpop.fuzz, navpop.mainDiv);
// FIXME it'd be prettier to do this internal to the Navpopup objects
var t=getValueOf('popupHideDelay');
if (t) { t = t * 1000; }
if (!t) {
if(!mouseOverNavpop) {
if(navpop.parentAnchor) {
restoreTitle( navpop.parentAnchor );
}
navpop.banish();
return true; /* remove this hook */
}
return false;
}
// we have a hide delay set
var d=+(new Date());
if ( !navpop.mouseLeavingTime ) {
navpop.mouseLeavingTime = d;
return false;
}
if ( mouseOverNavpop ) {
navpop.mouseLeavingTime=null;
return false;
}
if (d - navpop.mouseLeavingTime > t) {
navpop.mouseLeavingTime=null;
navpop.banish(); return true; /* remove this hook */
}
return false;
};
}
function runStopPopupTimer(navpop) {
// at this point, we should have left the link but remain within the popup
// so we call this function again until we leave the popup.
if (!navpop.stopPopupTimer) {
navpop.stopPopupTimer=setInterval(posCheckerHook(navpop), 500);
navpop.addHook(function(){clearInterval(navpop.stopPopupTimer);},
'hide', 'before');
}
}
// ENDFILE: mouseout.js
// STARTFILE: previewmaker.js
/**
@fileoverview
Defines the {@link Previewmaker} object, which generates short previews from wiki markup.
*/
/**
Creates a new Previewmaker
@constructor
@class The Previewmaker class. Use an instance of this to generate short previews from Wikitext.
@param {String} wikiText The Wikitext source of the page we wish to preview.
@param {String} baseUrl The url we should prepend when creating relative urls.
@param {Navpopup} owner The navpop associated to this preview generator
*/
function Previewmaker(wikiText, baseUrl, owner) {
/** The wikitext which is manipulated to generate the preview. */
this.originalData=wikiText;
this.baseUrl=baseUrl;
this.owner=owner;
this.maxCharacters=getValueOf('popupMaxPreviewCharacters');
this.maxSentences=getValueOf('popupMaxPreviewSentences');
this.setData();
}
Previewmaker.prototype.setData=function() {
var maxSize=Math.max(10000, 2*this.maxCharacters);
this.data=this.originalData.substring(0,maxSize);
};
/** Remove HTML comments
@private
*/
Previewmaker.prototype.killComments = function () {
// this also kills one trailing newline, eg [[diamyo]]
this.data=this.data.replace(RegExp('^<!--[^$]*?-->\\n|\\n<!--[^$]*?-->(?=\\n)|<!--[^$]*?-->', 'g'), '');
};
/**
@private
*/
Previewmaker.prototype.killDivs = function () {
// say goodbye, divs (can be nested, so use * not *?)
this.data=this.data.replace(RegExp('< *div[^>]* *>[\\s\\S]*?< */ *div *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killGalleries = function () {
this.data=this.data.replace(RegExp('< *gallery[^>]* *>[\\s\\S]*?< */ *gallery *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.kill = function(opening, closing, subopening, subclosing, repl) {
var oldk=this.data;
var k=this.killStuff(this.data, opening, closing, subopening, subclosing, repl);
while (k.length < oldk.length) {
oldk=k;
k=this.killStuff(k, opening, closing, subopening, subclosing, repl);
}
this.data=k;
};
/**
@private
*/
Previewmaker.prototype.killStuff = function (txt, opening, closing, subopening, subclosing, repl) {
var op=this.makeRegexp(opening);
var cl=this.makeRegexp(closing, '^');
var sb=subopening ? this.makeRegexp(subopening, '^') : null;
var sc=subclosing ? this.makeRegexp(subclosing, '^') : cl;
if (!op || !cl) {
alert('Navigation Popups error: op or cl is null! something is wrong.');
return;
}
if (!op.test(txt)) { return txt; }
var ret='';
var opResult = op.exec(txt);
ret = txt.substring(0,opResult.index);
txt=txt.substring(opResult.index+opResult[0].length);
var depth = 1;
while (txt.length > 0) {
var removal=0;
if (depth==1 && cl.test(txt)) {
depth--;
removal=cl.exec(txt)[0].length;
} else if (depth > 1 && sc.test(txt)) {
depth--;
removal=sc.exec(txt)[0].length;
}else if (sb && sb.test(txt)) {
depth++;
removal=sb.exec(txt)[0].length;
}
if ( !removal ) { removal = 1; }
txt=txt.substring(removal);
if (depth === 0) { break; }
}
return ret + (repl || '') + txt;
};
/**
@private
*/
Previewmaker.prototype.makeRegexp = function (x, prefix, suffix) {
prefix = prefix || '';
suffix = suffix || '';
var reStr='';
var flags='';
if (isString(x)) {
reStr=prefix + literalizeRegex(x) + suffix;
} else if (isRegExp(x)) {
var s=x.toString().substring(1);
var sp=s.split('/');
flags=sp[sp.length-1];
sp[sp.length-1]='';
s=sp.join('/');
s=s.substring(0,s.length-1);
reStr= prefix + s + suffix;
} else {
log ('makeRegexp failed');
}
log ('makeRegexp: got reStr=' + reStr + ', flags=' + flags);
return RegExp(reStr, flags);
};
/**
@private
*/
Previewmaker.prototype.killBoxTemplates = function () {
// taxobox removal... in fact, there's a saudiprincebox_begin, so let's be more general
// also, have float_begin, ... float_end
this.kill(RegExp('[{][{][^{}\\s|]*?(float|box)[_ ](begin|start)', 'i'), /[}][}]\s*/, '{{');
// infoboxes etc
// from User:Zyxw/popups.js: kill frames too
this.kill(RegExp('[{][{][^{}\\s|]*?(infobox|elementbox|frame)[_ ]', 'i'), /[}][}]\s*/, '{{');
};
/**
@private
*/
Previewmaker.prototype.killTemplates = function () {
// the plus sign is to avoid transcluding [[Template:', ']]
this.kill('{'+'{', '}'+'}', '{', '}', ' ');
};
/**
@private
*/
Previewmaker.prototype.killTables = function () {
// tables are bad, too
// this can be slow, but it's an inprovement over a browser hang
// torture test: [[Comparison_of_Intel_Central_Processing_Units]]
this.kill('{|', /[|]}\s*/, '{|');
this.kill(/<table.*?>/i, /<\/table.*?>/i, /<table.*?>/i);
// remove lines starting with a pipe for the hell of it (?)
this.data=this.data.replace(RegExp('^[|].*$', 'mg'), '');
};
/**
@private
*/
Previewmaker.prototype.killImages = function () {
var forbiddenNamespaceAliases = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=pg.nsImageId && _namespaceId!=pg.nsCategoryId) return;
forbiddenNamespaceAliases.push(_localizedNamespaceLc.split(' ').join('[ _]')); //todo: escape regexp fragments!
});
// images and categories are a nono
this.kill(RegExp('[[][[]\\s*(' + forbiddenNamespaceAliases.join('|') + ')\\s*:', 'i'),
/\]\]\s*/, '[', ']');
};
/**
@private
*/
Previewmaker.prototype.killHTML = function () {
// kill <ref ...>...</ref>
this.kill(/<ref\b[^/>]*?>/i, /<\/ref>/i);
// let's also delete entire lines starting with <. it's worth a try.
this.data=this.data.replace(RegExp('(^|\\n) *<.*', 'g'), '\n');
// and those pesky html tags, but not <nowiki> or <blockquote>
var splitted=this.data.parenSplit(/(<[\w\W]*?(?:>|$|(?=<)))/);
var len=splitted.length;
for (var i=1; i<len; i=i+2) {
switch (splitted[i]) {
case '<nowiki>':
case '</nowiki>':
case '<blockquote>':
case '</blockquote>':
break;
default:
splitted[i]='';
}
}
this.data=splitted.join('');
};
/**
@private
*/
Previewmaker.prototype.killChunks = function() { // heuristics alert
// chunks of italic text? you crazy, man?
var italicChunkRegex=new RegExp
("((^|\\n)\\s*:*\\s*''[^']([^']|'''|'[^']){20}(.|\\n[^\\n])*''[.!?\\s]*\\n)+", 'g');
// keep stuff separated, though, so stick in \n (fixes [[Union Jack]]?
this.data=this.data.replace(italicChunkRegex, '\n');
};
/**
@private
*/
Previewmaker.prototype.mopup = function () {
// we simply *can't* be doing with horizontal rules right now
this.data=this.data.replace(RegExp('^-{4,}','mg'),'');
// no indented lines
this.data=this.data.replace(RegExp('(^|\\n) *:[^\\n]*','g'), '');
// replace __TOC__, __NOTOC__ and whatever else there is
// this'll probably do
this.data=this.data.replace(RegExp('^__[A-Z_]*__ *$', 'gmi'),'');
};
/**
@private
*/
Previewmaker.prototype.firstBit = function () {
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d=this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data=this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data=this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data=this.data.replace(RegExp('^[\\s\\n]*'), '');
var stuff=(RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) { d = stuff[0]; }
if (!getValueOf('popupPreviewFirstParOnly')) { d = this.data; }
/// now put \n\n after sections so that bullets and numbered lists work
d=d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// Split sentences. Superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["'+"'"+']*\\s)','g'));
// leading space is bad, mmkay?
d[0]=d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds=RegExp('([^.][a-z][.] *[a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|op|cit|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
this.fullLength=d.join('').length;
var n=this.maxSentences;
var dd=this.firstSentences(d,n);
do {
dd=this.firstSentences(d,n); --n;
} while ( dd.length > this.maxCharacters && n !== 0 );
this.data = dd;
};
/**
@private
*/
Previewmaker.prototype.fixSentenceEnds = function(strs, reg) {
// take an array of strings, strs
// join strs[i] to strs[i+1] & strs[i+2] if strs[i] matches regex reg
for (var i=0; i<strs.length-2; ++i) {
if (reg.test(strs[i])) {
var a=[];
for (var j=0; j<strs.length; ++j) {
if (j<i) a[j]=strs[j];
if (j==i) a[i]=strs[i]+strs[i+1]+strs[i+2];
if (j>i+2) a[j-2]=strs[j];
}
return this.fixSentenceEnds(a,reg);
}
}
return strs;
};
/**
@private
*/
Previewmaker.prototype.firstSentences = function(strs, howmany) {
var t=strs.slice(0, 2*howmany);
return t.join('');
};
/**
@private
*/
Previewmaker.prototype.killBadWhitespace = function() {
// also cleans up isolated '''', eg [[Suntory Sungoliath]]
this.data=this.data.replace(RegExp('^ *\'+ *$', 'gm'), '');
};
/**
Runs the various methods to generate the preview.
The preview is stored in the <code>html</html> field.
@private
*/
Previewmaker.prototype.makePreview = function() {
if (this.owner.article.namespaceId()!=pg.nsTemplateId &&
this.owner.article.namespaceId()!=pg.nsImageId ) {
this.killComments();
this.killDivs();
this.killGalleries();
this.killBoxTemplates();
if (getValueOf('popupPreviewKillTemplates')) {
this.killTemplates();
} else {
this.killMultilineTemplates();
}
this.killTables();
this.killImages();
this.killHTML();
this.killChunks();
this.mopup();
this.firstBit();
this.killBadWhitespace();
}
else
{
this.killHTML();
}
this.html=wiki2html(this.data, this.baseUrl); // needs livepreview
this.fixHTML();
this.stripLongTemplates();
};
/**
@private
*/
Previewmaker.prototype.esWiki2HtmlPart = function(data) {
var reLinks = /(?:\[\[([^|\]]*)(?:\|([^|\]]*))*]]([a-z]*))/gi; //match a wikilink
reLinks.lastIndex = 0; //reset regex
var match;
var result = "";
var postfixIndex = 0;
while ((match = reLinks.exec(data))) //match all wikilinks
{
//FIXME: the way that link is built here isn't perfect. It is clickable, but popups preview won't recognize it in some cases.
result += pg.escapeQuotesHTML(data.substring(postfixIndex, match.index)) +
'<a href="'+Insta.conf.paths.articles+pg.escapeQuotesHTML(match[1])+'">'+pg.escapeQuotesHTML((match[2]?match[2]:match[1])+match[3])+"</a>";
postfixIndex = reLinks.lastIndex;
}
//append the rest
result += pg.escapeQuotesHTML(data.substring(postfixIndex));
return result;
};
Previewmaker.prototype.editSummaryPreview=function() {
var reAes = /\/\* *(.*?) *\*\//g; //match the first section marker
reAes.lastIndex = 0; //reset regex
var match;
match = reAes.exec(this.data);
if (match)
{
//we have a section link. Split it, process it, combine it.
var prefix = this.data.substring(0,match.index-1);
var section = match[1];
var postfix = this.data.substring(reAes.lastIndex);
var start = "<span class='autocomment'>";
var end = "</span>";
if (prefix.length>0) start = this.esWiki2HtmlPart(prefix) + " " + start + "- ";
if (postfix.length>0) end = ": " + end + this.esWiki2HtmlPart(postfix);
var t=new Title().fromURL(this.baseUrl);
t.anchorFromUtf(section);
var sectionLink = Insta.conf.paths.articles + pg.escapeQuotesHTML(t.toString(true)) + '#' + pg.escapeQuotesHTML(t.anchor);
return start + '<a href="'+sectionLink+'">→</a> '+pg.escapeQuotesHTML(section) + end;
}
//else there's no section link, htmlify the whole thing.
return this.esWiki2HtmlPart(this.data);
};
//<NOLITE>
/** Test function for debugging preview problems one step at a time.
*/
/*eslint-disable */
function previewSteps(txt) {
try {
txt=txt || document.editform.wpTextbox1.value;
} catch (err) {
if (pg.cache.pages.length > 0) {
txt=pg.cache.pages[pg.cache.pages.length-1].data;
} else {
alert('provide text or use an edit page');
}
}
txt=txt.substring(0,10000);
var base=pg.wiki.articlebase + Title.fromURL(document.location.href).urlString();
var p=new Previewmaker(txt, base, pg.current.link.navpopup);
if (this.owner.article.namespaceId() != pg.nsTemplateId) {
p.killComments(); if (!confirm('done killComments(). Continue?\n---\n' + p.data)) { return; }
p.killDivs(); if (!confirm('done killDivs(). Continue?\n---\n' + p.data)) { return; }
p.killGalleries(); if (!confirm('done killGalleries(). Continue?\n---\n' + p.data)) { return; }
p.killBoxTemplates(); if (!confirm('done killBoxTemplates(). Continue?\n---\n' + p.data)) { return; }
if (getValueOf('popupPreviewKillTemplates')) {
p.killTemplates(); if (!confirm('done killTemplates(). Continue?\n---\n' + p.data)) { return; }
} else {
p.killMultilineTemplates(); if (!confirm('done killMultilineTemplates(). Continue?\n---\n' + p.data)) { return; }
}
p.killTables(); if (!confirm('done killTables(). Continue?\n---\n' + p.data)) { return; }
p.killImages(); if (!confirm('done killImages(). Continue?\n---\n' + p.data)) { return; }
p.killHTML(); if (!confirm('done killHTML(). Continue?\n---\n' + p.data)) { return; }
p.killChunks(); if (!confirm('done killChunks(). Continue?\n---\n' + p.data)) { return; }
p.mopup(); if (!confirm('done mopup(). Continue?\n---\n' + p.data)) { return; }
p.firstBit(); if (!confirm('done firstBit(). Continue?\n---\n' + p.data)) { return; }
p.killBadWhitespace(); if (!confirm('done killBadWhitespace(). Continue?\n---\n' + p.data)) { return; }
}
p.html=wiki2html(p.data, base); // needs livepreview
p.fixHTML(); if (!confirm('done fixHTML(). Continue?\n---\n' + p.html)) { return; }
p.stripLongTemplates(); if (!confirm('done stripLongTemplates(). Continue?\n---\n' + p.html)) { return; }
alert('finished preview - end result follows.\n---\n' + p.html);
}
/*eslint-enable */
//</NOLITE>
/**
Works around livepreview bugs.
@private
*/
Previewmaker.prototype.fixHTML = function() {
if(!this.html) return;
var ret = this.html;
// fix question marks in wiki links
// maybe this'll break some stuff :-(
ret=ret.replace(RegExp('(<a href="' + pg.wiki.articlePath + '/[^"]*)[?](.*?")', 'g'), '$1%3F$2');
ret=ret.replace(RegExp('(<a href=\'' + pg.wiki.articlePath + '/[^\']*)[?](.*?\')', 'g'), '$1%3F$2');
// FIXME fix up % too
this.html=ret;
};
/**
Generates the preview and displays it in the current popup.
Does nothing if the generated preview is invalid or consists of whitespace only.
Also activates wikilinks in the preview for subpopups if the popupSubpopups option is true.
*/
Previewmaker.prototype.showPreview = function () {
this.makePreview();
if (typeof this.html != typeof '') return;
if (RegExp('^\\s*$').test(this.html)) return;
setPopupHTML('<hr />', 'popupPrePreviewSep', this.owner.idNumber);
setPopupTipsAndHTML(this.html, 'popupPreview', this.owner.idNumber, { owner: this.owner });
var more = (this.fullLength > this.data.length) ? this.moreLink() : '';
setPopupHTML(more, 'popupPreviewMore', this.owner.idNumber);
};
/**
@private
*/
Previewmaker.prototype.moreLink=function() {
var a=document.createElement('a');
a.className='popupMoreLink';
a.innerHTML=popupString('more...');
var savedThis=this;
a.onclick=function() {
savedThis.maxCharacters+=2000;
savedThis.maxSentences+=20;
savedThis.setData();
savedThis.showPreview();
};
return a;
};
/**
@private
*/
Previewmaker.prototype.stripLongTemplates = function() {
// operates on the HTML!
this.html=this.html.replace(RegExp('^.{0,1000}[{][{][^}]*?(<(p|br)( /)?>\\s*){2,}([^{}]*?[}][}])?', 'gi'), '');
this.html=this.html.split('\n').join(' '); // workaround for <pre> templates
this.html=this.html.replace(RegExp('[{][{][^}]*<pre>[^}]*[}][}]','gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killMultilineTemplates = function() {
this.kill('{{{', '}}}');
this.kill(RegExp('\\s*[{][{][^{}]*\\n'), '}}', '{{');
};
// ENDFILE: previewmaker.js
// STARTFILE: querypreview.js
function loadAPIPreview(queryType, article, navpop) {
var art=new Title(article).urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
var htmlGenerator=function(/*a, d*/){alert('invalid html generator');};
var usernameart = '';
switch (queryType) {
case 'history':
url += 'titles=' + art + '&prop=revisions&rvlimit=' +
getValueOf('popupHistoryPreviewLimit');
htmlGenerator=APIhistoryPreviewHTML;
break;
case 'category':
url += 'list=categorymembers&cmtitle=' + art;
htmlGenerator=APIcategoryPreviewHTML;
break;
case 'userinfo':
var username = new Title( article ).userName();
usernameart = encodeURIComponent( username );
if (pg.re.ipUser.test(username)) {
url += 'list=blocks&bkprop=range|restrictions&bkip=' + usernameart;
} else {
url += 'list=users|usercontribs&usprop=blockinfo|groups|editcount|registration|gender&ususers=' + usernameart + "&meta=globaluserinfo&guiprop=groups|unattached&guiuser="+ usernameart + "&uclimit=1&ucprop=timestamp&ucuser=" + usernameart;
}
htmlGenerator=APIuserInfoPreviewHTML;
break;
case 'contribs':
usernameart = encodeURIComponent( new Title( article ).userName() );
url += 'list=usercontribs&ucuser=' + usernameart +
'&uclimit=' + getValueOf('popupContribsPreviewLimit');
htmlGenerator=APIcontribsPreviewHTML;
break;
case 'imagepagepreview':
var trail='';
if (getValueOf('popupImageLinks')) { trail = '&list=imageusage&iutitle=' + art; }
url += 'titles=' + art + '&prop=revisions|imageinfo&rvprop=content' + trail;
htmlGenerator=APIimagepagePreviewHTML;
break;
case 'backlinks':
url += 'list=backlinks&bltitle=' + art;
htmlGenerator=APIbacklinksPreviewHTML;
break;
case 'revision':
if (article.oldid) {
url += 'revids=' + article.oldid;
} else {
url += 'titles=' + article.removeAnchor().urlString();
}
url += '&prop=revisions|pageprops|info|images|categories&rvprop=ids|timestamp|flags|comment|user|content&cllimit=max&imlimit=max';
htmlGenerator=APIrevisionPreviewHTML;
break;
}
pendingNavpopTask(navpop);
var callback=function(d){
log( "callback of API functions was hit" );
showAPIPreview(queryType, htmlGenerator(article,d,navpop), navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_'+queryType+'_QUERY_DATA'); }
}
function linkList(list) {
list.sort(function(x,y) { return (x==y ? 0 : (x<y ? -1 : 1)); });
var buf=[];
for (var i=0; i<list.length; ++i) {
buf.push(wikiLink({article: new Title(list[i]),
text: list[i].split(' ').join(' '),
action: 'view'}));
}
return buf.join(', ');
}
function getTimeOffset() {
var tz = mw.user.options.get('timecorrection');
if(tz) {
if( tz.indexOf('|') > -1 ) {
// New format
return parseInt(tz.split('|')[1],10);
} else if ( tz.indexOf(':') > -1 ) {
// Old format
return( parseInt(tz,10)*60 + parseInt(tz.split(':')[1],10) );
}
}
return 0;
}
/*
* Creates a HTML table that's shown in the history and user-contribs popups.
* @param {Object[]} h - a list of revisions, returned from the API
* @param {boolean} reallyContribs - true only if we're displaying user contributions
*/
function editPreviewTable(article, h, reallyContribs, timeOffset) {
var html=['<table>'];
var day=null;
var curart=article;
var page=null;
var makeFirstColumnLinks;
if(reallyContribs) {
// We're showing user contributions, so make (diff | hist) links
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&diff=prev' +
'&oldid=' + currentRevision.revid + '">' + popupString('diff') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&action=history">' +
popupString('hist') + '</a>';
result += ')';
return result;
};
} else {
// It's a regular history page, so make (cur | last) links
var firstRevid = h[0].revid;
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=' + firstRevid + '&oldid=' + currentRevision.revid + '">' + popupString('cur') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=prev&oldid=' + currentRevision.revid + '">' + popupString('last') + '</a>';
result += ')';
return result;
};
}
for (var i=0; i<h.length; ++i) {
if (reallyContribs) {
page = h[i].title;
curart = new Title(page);
}
var minor = h[i].minor ? '<b>m </b>' : '';
var editDate = adjustDate(getDateFromTimestamp(h[i].timestamp), timeOffset);
var thisDay = dayFormat(editDate);
var thisTime = timeFormat(editDate);
if (thisDay == day) {
thisDay = '';
} else {
day = thisDay;
}
if (thisDay) {
html.push( '<tr><td colspan=3><span class="popup_history_date">' +
thisDay+'</span></td></tr>' );
}
html.push('<tr class="popup_history_row_' + ( (i%2) ? 'odd' : 'even') + '">');
html.push('<td>' + makeFirstColumnLinks(h[i]) + '</td>');
html.push('<td>' +
'<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&oldid=' + h[i].revid + '">' + thisTime + '</a></td>');
var col3url='', col3txt='';
if (!reallyContribs) {
var user=h[i].user;
if( !h[i].userhidden ) {
if( pg.re.ipUser.test(user) ) {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Contributions&target=' + new Title(user).urlString();
} else {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(user).urlString();
}
col3txt=pg.escapeQuotesHTML(user);
} else {
col3url=getValueOf('popupRevDelUrl');
col3txt=pg.escapeQuotesHTML( popupString('revdel'));
}
} else {
col3url=pg.wiki.titlebase + curart.urlString();
col3txt=pg.escapeQuotesHTML(page);
}
html.push('<td>' + (reallyContribs ? minor : '') +
'<a href="' + col3url + '">' + col3txt + '</a></td>');
var comment='';
var c=h[i].comment || h[i].content;
if (c) {
comment=new Previewmaker(c, new Title(curart).toUrl()).editSummaryPreview();
} else if ( h[i].commenthidden ) {
comment=popupString('revdel');
}
html.push('<td>' + (!reallyContribs ? minor : '') + comment + '</td>');
html.push('</tr>');
html=[html.join('')];
}
html.push('</table>');
return html.join('');
}
function getDateFromTimestamp(t) {
var s=t.split(/[^0-9]/);
switch(s.length) {
case 0: return null;
case 1: return new Date(s[0]);
case 2: return new Date(s[0], s[1]-1);
case 3: return new Date(s[0], s[1]-1, s[2]);
case 4: return new Date(s[0], s[1]-1, s[2], s[3]);
case 5: return new Date(s[0], s[1]-1, s[2], s[3], s[4]);
case 6: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5]);
default: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5], s[6]);
}
}
function adjustDate(d, offset) {
// offset is in minutes
var o=offset * 60 * 1000;
return new Date( +d + o);
}
function dayFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCFullYear(), editDate.getUTCMonth()+1, editDate.getUTCDate()]).join('-'); }
return map(zeroFill, [editDate.getFullYear(), editDate.getMonth()+1, editDate.getDate()]).join('-');
}
function timeFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCHours(), editDate.getUTCMinutes(), editDate.getUTCSeconds()]).join(':'); }
return map(zeroFill, [editDate.getHours(), editDate.getMinutes(), editDate.getSeconds()]).join(':');
}
function showAPIPreview(queryType, html, id, navpop, download) {
// DJ: done
var target='popupPreview';
completedNavpopTask(navpop);
switch (queryType) {
case 'imagelinks':
case 'category':
target='popupPostPreview'; break;
case 'userinfo':
target='popupUserData'; break;
case 'revision':
insertPreview(download);
return;
}
setPopupTipsAndHTML(html, target, id);
}
function APIrevisionPreviewHTML(article, download) {
try{
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
if( page.missing ) {
// TODO we need to fix this proper later on
download.owner = null;
return;
}
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
download.data = content;
download.lastModified = new Date(page.revisions[0].timestamp);
}
} catch(someError) {
return 'Revision preview failed :(';
}
}
function APIbacklinksPreviewHTML(article, download/*, navpop*/ ) {
try {
var jsObj=getJsObj(download.data);
var list=jsObj.query.backlinks;
var html=[];
if (!list) { return popupString('No backlinks found'); }
for ( var i=0; i < list.length; i++ ) {
var t=new Title(list[i].title);
html.push('<a href="' + pg.wiki.titlebase + t.urlString() + '">' + t + '</a>');
}
html=html.join(', ');
if (jsObj['continue'] && jsObj['continue'].blcontinue) {
html += popupString(' and more');
}
return html;
} catch (someError) {
return 'backlinksPreviewHTML went wonky';
}
}
pg.fn.APIsharedImagePagePreviewHTML = function APIsharedImagePagePreviewHTML(obj) {
log( "APIsharedImagePagePreviewHTML" );
var popupid = obj.requestid;
if( obj.query && obj.query.pages )
{
var page=anyChild(obj.query.pages );
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
/* Not entirely safe, but the best we can do */
var p=new Previewmaker(content, pg.current.link.navpopup.article, pg.current.link.navpopup);
p.makePreview();
setPopupHTML( p.html, "popupSecondPreview", popupid );
}
}
};
function APIimagepagePreviewHTML(article, download, navpop) {
try {
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
var content= (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
var ret='';
var alt='';
try{alt=navpop.parentAnchor.childNodes[0].alt;} catch(e){}
if (alt) {
ret = ret + '<hr /><b>' + popupString('Alt text:') + '</b> ' + pg.escapeQuotesHTML(alt);
}
if (typeof content === 'string') {
var p=prepPreviewmaker(content, article, navpop);
p.makePreview();
if (p.html) { ret += '<hr />' + p.html; }
if (getValueOf('popupSummaryData')) {
var info=getPageInfo(content, download);
log(info);
setPopupTrailer(info, navpop.idNumber);
}
}
if (page && page.imagerepository == "shared" ) {
var art=new Title(article);
var encart = encodeURIComponent( "File:" + art.stripNamespace() );
var shared_url = pg.wiki.apicommonsbase + '?format=json&formatversion=2' +
'&callback=pg.fn.APIsharedImagePagePreviewHTML' +
'&requestid=' + navpop.idNumber +
'&action=query&prop=revisions&rvprop=content&titles=' + encart;
ret = ret +'<hr />' + popupString( 'Image from Commons') +
': <a href="' + pg.wiki.commonsbase + '?title=' + encart + '">' +
popupString( 'Description page') + '</a>';
mw.loader.load( shared_url );
}
showAPIPreview('imagelinks', APIimagelinksPreviewHTML(article,download), navpop.idNumber, download);
return ret;
} catch (someError) {
return 'API imagepage preview failed :(';
}
}
function APIimagelinksPreviewHTML(article, download) {
try {
var jsobj=getJsObj(download.data);
var list=jsobj.query.imageusage;
if (list) {
var ret=[];
for (var i=0; i < list.length; i++) {
ret.push(list[i].title);
}
if (ret.length === 0) { return popupString('No image links found'); }
return '<h2>' + popupString('File links') + '</h2>' + linkList(ret);
} else {
return popupString('No image links found');
}
} catch(someError) {
return 'Image links preview generation failed :(';
}
}
function APIcategoryPreviewHTML(article, download) {
try{
var jsobj=getJsObj(download.data);
var list=jsobj.query.categorymembers;
var ret=[];
for (var p=0; p < list.length; p++) {
ret.push(list[p].title);
}
if (ret.length === 0) { return popupString('Empty category'); }
ret = '<h2>' + tprintf('Category members (%s shown)', [ret.length]) + '</h2>' +linkList(ret);
if (jsobj['continue'] && jsobj['continue'].cmcontinue) {
ret += popupString(' and more');
}
return ret;
} catch(someError) {
return 'Category preview failed :(';
}
}
function APIuserInfoPreviewHTML(article, download) {
var ret=[];
var queryobj = {};
try{
queryobj=getJsObj(download.data).query;
} catch(someError) { return 'Userinfo preview failed :('; }
var user=anyChild(queryobj.users);
if (user) {
var globaluserinfo=queryobj.globaluserinfo;
if (user.invalid === '') {
ret.push( popupString( 'Invalid user') );
} else if (user.missing === '') {
ret.push( popupString( 'Not a registered username') );
}
if( user.blockedby ) {
if( user.blockpartial ) {
ret.push('<b>' + popupString('Has blocks') + '</b>');
} else {
ret.push('<b>' + popupString('BLOCKED') + '</b>');
}
}
if( globaluserinfo && ( 'locked' in globaluserinfo || 'hidden' in globaluserinfo ) ) {
var lockedSulAccountIsAttachedToThis = true;
for( var i=0; globaluserinfo.unattached && i < globaluserinfo.unattached.length; i++) {
if ( globaluserinfo.unattached[i].wiki === mw.config.get('wgDBname') ) {
lockedSulAccountIsAttachedToThis=false;
break;
}
}
if (lockedSulAccountIsAttachedToThis) {
if ( 'locked' in globaluserinfo ) ret.push('<b><i>' + popupString('LOCKED') + '</i></b>');
if ( 'hidden' in globaluserinfo ) ret.push('<b><i>' + popupString('HIDDEN') + '</i></b>');
}
}
if( getValueOf('popupShowGender') && user.gender ) {
switch( user.gender ) {
case "male": ret.push( popupString( "\u2642" ) ); break;
case "female": ret.push( popupString( "\u2640" ) ); break;
}
}
if( user.groups ) {
for( var j=0; j < user.groups.length; j++) {
var currentGroup = user.groups[j];
if( ["*", "user", "autoconfirmed", "extendedconfirmed"].indexOf( currentGroup ) === -1 ) {
ret.push( pg.escapeQuotesHTML(user.groups[j]) );
}
}
}
if( globaluserinfo && globaluserinfo.groups ) {
for( var k=0; k < globaluserinfo.groups.length; k++) {
ret.push( '<i>'+pg.escapeQuotesHTML(globaluserinfo.groups[k])+'</i>' );
}
}
if( user.registration )
ret.push( pg.escapeQuotesHTML((user.editcount?user.editcount:'0') + popupString(' edits since: ') + (user.registration?dayFormat(getDateFromTimestamp(user.registration)):'')) );
}
if (queryobj.usercontribs && queryobj.usercontribs.length) {
ret.push( popupString('last edit on ') + dayFormat(getDateFromTimestamp(queryobj.usercontribs[0].timestamp)) );
}
if (queryobj.blocks) {
ret.push( popupString( 'IP user') ); //we only request list=blocks for IPs
for (var l=0; l<queryobj.blocks.length; l++) {
console.log(queryobj);
var rbstr = queryobj.blocks[l].rangestart === queryobj.blocks[l].rangeend ? 'BLOCK' : 'RANGEBLOCK';
rbstr = (!Array.isArray(queryobj.blocks[l].restrictions) ? 'Has ' + rbstr.toLowerCase() + 's' : rbstr + 'ED');
ret.push('<b>' + popupString(rbstr) + '</b>' );
}
}
ret = '<hr />' + ret.join( ', ' );
return ret;
}
function APIcontribsPreviewHTML(article, download, navpop) {
return APIhistoryPreviewHTML(article, download, navpop, true);
}
function APIhistoryPreviewHTML(article, download, navpop, reallyContribs) {
try {
var jsobj=getJsObj(download.data);
var edits = [];
if( reallyContribs ) {
edits=jsobj.query.usercontribs;
} else {
edits=anyChild(jsobj.query.pages).revisions;
}
var ret=editPreviewTable(article, edits, reallyContribs, getTimeOffset());
return ret;
} catch (someError) {
return 'History preview failed :-(';
}
}
//</NOLITE>
// ENDFILE: querypreview.js
// STARTFILE: debug.js
////////////////////////////////////////////////////////////////////
// Debugging functions
////////////////////////////////////////////////////////////////////
function setupDebugging() {
//<NOLITE>
if (window.popupDebug) { // popupDebug is set from .version
window.log=function(x) { //if(gMsg!='')gMsg += '\n'; gMsg+=time() + ' ' + x; };
window.console.log(x);
};
window.errlog=function(x) {
window.console.error(x);
};
log('Initializing logger');
} else {
//</NOLITE>
window.log = function() {};
window.errlog = function() {};
//<NOLITE>
}
//</NOLITE>
}
// ENDFILE: debug.js
// STARTFILE: images.js
// load image of type Title.
function loadImage(image, navpop) {
if (typeof image.stripNamespace != 'function') { alert('loadImages bad'); }
// API call to retrieve image info.
if ( !getValueOf('popupImages') ) return;
if ( !isValidImageName(image) ) return false;
var art=image.urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query';
url += '&prop=imageinfo&iiprop=url|mime&iiurlwidth=' + getValueOf('popupImageSizeLarge');
url += '&titles=' + art;
pendingNavpopTask(navpop);
var callback=function(d){
popupsInsertImage(navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'after', 'DOWNLOAD_IMAGE_QUERY_DATA'); }
}
function popupsInsertImage(id, navpop, download) {
log( "popupsInsertImage");
var imageinfo;
try {
var jsObj=getJsObj(download.data);
var imagepage=anyChild(jsObj.query.pages);
if (typeof imagepage.imageinfo === 'undefined') return;
imageinfo = imagepage.imageinfo[0];
} catch (someError) {
log( "popupsInsertImage failed :(" );
return;
}
var popupImage = document.getElementById("popupImg"+id);
if (!popupImage) {
log( "could not find insertion point for image");
return;
}
popupImage.width=getValueOf('popupImageSize');
popupImage.style.display='inline';
// Set the source for the image.
if( imageinfo.thumburl )
popupImage.src=imageinfo.thumburl;
else if( imageinfo.mime.indexOf("image") === 0 ){
popupImage.src=imageinfo.url;
log( "a thumb could not be found, using original image" );
} else log( "fullsize imagethumb, but not sure if it's an image");
var a=document.getElementById("popupImageLink"+id);
if (a === null) { return null; }
// Determine the action of the surrouding imagelink.
switch (getValueOf('popupThumbAction')) {
case 'imagepage':
if (pg.current.article.namespaceId()!=pg.nsImageId) {
a.href=imageinfo.descriptionurl;
// FIXME: unreliable pg.idNumber
popTipsSoonFn('popupImage' + id)();
break;
}
/* falls through */
case 'sizetoggle':
a.onclick=toggleSize;
a.title=popupString('Toggle image size');
return;
case 'linkfull':
a.href = imageinfo.url;
a.title=popupString('Open full-size image');
return;
}
}
// Toggles the image between inline small and navpop fullwidth.
// It's the same image, no actual sizechange occurs, only display width.
function toggleSize() {
var imgContainer=this;
if (!imgContainer) {
alert('imgContainer is null :/');
return;
}
var img=imgContainer.firstChild;
if (!img) {
alert('img is null :/');
return;
}
if (!img.style.width || img.style.width==='') {
img.style.width='100%';
} else {
img.style.width='';
}
}
// Returns one title of an image from wikiText.
function getValidImageFromWikiText(wikiText) {
// nb in pg.re.image we're interested in the second bracketed expression
// this may change if the regex changes :-(
//var match=pg.re.image.exec(wikiText);
var matched=null;
var match;
// strip html comments, used by evil bots :-(
var t = removeMatchesUnless(wikiText, RegExp('(<!--[\\s\\S]*?-->)'), 1,
RegExp('^<!--[^[]*popup', 'i'));
while ( ( match = pg.re.image.exec(t) ) ) {
// now find a sane image name - exclude templates by seeking {
var m = match[2] || match[6];
if ( isValidImageName(m) ) {
matched=m;
break;
}
}
pg.re.image.lastIndex=0;
if (!matched) { return null; }
return mw.config.get('wgFormattedNamespaces')[pg.nsImageId]+':'+upcaseFirst(matched);
}
function removeMatchesUnless(str, re1, parencount, re2) {
var split=str.parenSplit(re1);
var c=parencount + 1;
for (var i=0; i<split.length; ++i) {
if ( i%c === 0 || re2.test(split[i]) ) { continue; }
split[i]='';
}
return split.join('');
}
//</NOLITE>
// ENDFILE: images.js
// STARTFILE: namespaces.js
// Set up namespaces and other non-strings.js localization
// (currently that means redirs too)
function setNamespaces() {
pg.nsSpecialId = -1;
pg.nsMainspaceId = 0;
pg.nsImageId = 6;
pg.nsUserId = 2;
pg.nsUsertalkId = 3;
pg.nsCategoryId = 14;
pg.nsTemplateId = 10;
}
function setRedirs() {
var r='redirect';
var R='REDIRECT';
var redirLists={
//<NOLITE>
'ar': [ R, 'تحويل' ],
'be': [ r, 'перанакіраваньне' ],
'bg': [ r, 'пренасочване', 'виж' ],
'bs': [ r, 'Preusmjeri', 'preusmjeri', 'PREUSMJERI' ],
'cs': [ R, 'PŘESMĚRUJ' ],
'cy': [ r, 'ail-cyfeirio' ],
'de': [ R, 'WEITERLEITUNG' ],
'el': [ R, 'ΑΝΑΚΑΤΕΥΘΥΝΣΗ'],
'eo': [ R, 'ALIDIREKTU', 'ALIDIREKTI' ],
'es': [ R, 'REDIRECCIÓN' ],
'et': [ r, 'suuna' ],
'ga': [ r, 'athsheoladh' ],
'gl': [ r, 'REDIRECCIÓN', 'REDIRECIONAMENTO'],
'he': [ R, 'הפניה' ],
'hu': [ R, 'ÁTIRÁNYÍTÁS' ],
'is': [ r, 'tilvísun', 'TILVÍSUN' ],
'it': [ R, 'RINVIA', 'Rinvia'],
'ja': [ R, '転送' ],
'mk': [ r, 'пренасочување', 'види' ],
'nds': [ r, 'wiederleiden' ],
'nl': [ R, 'DOORVERWIJZING' ],
'nn': [ r, 'omdiriger' ],
'pl': [ R, 'PATRZ', 'PRZEKIERUJ', 'TAM' ],
'pt': [ R, 'redir' ],
'ru': [ R, 'ПЕРЕНАПРАВЛЕНИЕ', 'ПЕРЕНАПР' ],
'sk': [ r, 'presmeruj' ],
'sr': [ r, 'Преусмери', 'преусмери', 'ПРЕУСМЕРИ', 'Preusmeri', 'preusmeri', 'PREUSMERI' ],
'tt': [ R, 'yünältü', 'перенаправление', 'перенапр' ],
'uk': [ R, 'ПЕРЕНАПРАВЛЕННЯ', 'ПЕРЕНАПР' ],
'vi': [ r, 'đổi' ],
'zh': [ R, '重定向'] // no comma
//</NOLITE>
};
var redirList=redirLists[ pg.wiki.lang ] || [r, R];
// Mediawiki is very tolerant about what comes after the #redirect at the start
pg.re.redirect=RegExp('^\\s*[#](' + redirList.join('|') + ').*?\\[{2}([^\\|\\]]*)(|[^\\]]*)?\\]{2}\\s*(.*)', 'i');
}
function setInterwiki() {
if (pg.wiki.wikimedia) {
// From https://meta.wikimedia.org/wiki/List_of_Wikipedias
pg.wiki.interwiki='aa|ab|ace|af|ak|als|am|an|ang|ar|arc|arz|as|ast|av|ay|az|ba|bar|bat-smg|bcl|be|be-x-old|bg|bh|bi|bjn|bm|bn|bo|bpy|br|bs|bug|bxr|ca|cbk-zam|cdo|ce|ceb|ch|cho|chr|chy|ckb|co|cr|crh|cs|csb|cu|cv|cy|da|de|diq|dsb|dv|dz|ee|el|eml|en|eo|es|et|eu|ext|fa|ff|fi|fiu-vro|fj|fo|fr|frp|frr|fur|fy|ga|gag|gan|gd|gl|glk|gn|got|gu|gv|ha|hak|haw|he|hi|hif|ho|hr|hsb|ht|hu|hy|hz|ia|id|ie|ig|ii|ik|ilo|io|is|it|iu|ja|jbo|jv|ka|kaa|kab|kbd|kg|ki|kj|kk|kl|km|kn|ko|koi|kr|krc|ks|ksh|ku|kv|kw|ky|la|lad|lb|lbe|lg|li|lij|lmo|ln|lo|lt|ltg|lv|map-bms|mdf|mg|mh|mhr|mi|mk|ml|mn|mo|mr|mrj|ms|mt|mus|mwl|my|myv|mzn|na|nah|nap|nds|nds-nl|ne|new|ng|nl|nn|no|nov|nrm|nv|ny|oc|om|or|os|pa|pag|pam|pap|pcd|pdc|pfl|pi|pih|pl|pms|pnb|pnt|ps|pt|qu|rm|rmy|rn|ro|roa-rup|roa-tara|ru|rue|rw|sa|sah|sc|scn|sco|sd|se|sg|sh|si|simple|sk|sl|sm|sn|so|sq|sr|srn|ss|st|stq|su|sv|sw|szl|ta|te|tet|tg|th|ti|tk|tl|tn|to|tpi|tr|ts|tt|tum|tw|ty|udm|ug|uk|ur|uz|ve|vec|vi|vls|vo|wa|war|wo|wuu|xal|xh|yi|yo|za|zea|zh|zh-classical|zh-min-nan|zh-yue|zu';
pg.re.interwiki=RegExp('^'+pg.wiki.interwiki+':');
} else {
pg.wiki.interwiki=null;
pg.re.interwiki=RegExp('^$');
}
}
// return a regexp pattern matching all variants to write the given namespace
function nsRe(namespaceId) {
var imageNamespaceVariants = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=namespaceId) return;
_localizedNamespaceLc = upcaseFirst(_localizedNamespaceLc);
imageNamespaceVariants.push(mw.util.escapeRegExp(_localizedNamespaceLc).split(' ').join('[ _]'));
imageNamespaceVariants.push(mw.util.escapeRegExp(encodeURI(_localizedNamespaceLc)));
});
return '(?:' + imageNamespaceVariants.join('|') + ')';
}
function nsReImage() {
return nsRe(pg.nsImageId);
}
// ENDFILE: namespaces.js
// STARTFILE: selpop.js
//<NOLITE>
function getEditboxSelection() {
// see http://www.webgurusforum.com/8/12/0
var editbox;
try {
editbox=document.editform.wpTextbox1;
} catch (dang) { return; }
// IE, Opera
if (document.selection) { return document.selection.createRange().text; }
// Mozilla
var selStart = editbox.selectionStart;
var selEnd = editbox.selectionEnd;
return (editbox.value).substring(selStart, selEnd);
}
function doSelectionPopup() {
// popup if the selection looks like [[foo|anything afterwards at all
// or [[foo|bar]]text without ']]'
// or [[foo|bar]]
var sel=getEditboxSelection();
var open=sel.indexOf('[[');
var pipe=sel.indexOf('|');
var close=sel.indexOf(']]');
if (open == -1 || ( pipe == -1 && close == -1) ) { return; }
if (pipe != -1 && open > pipe || close != -1 && open > close) { return; }
if (getValueOf('popupOnEditSelection')=='boxpreview') {
return doSeparateSelectionPopup(sel);
}
var article=new Title(sel.substring(open+2, (pipe < 0) ? close : pipe)).urlString();
if (close > 0 && sel.substring(close+2).indexOf('[[') >= 0) {
return;
}
var a=document.createElement('a');
a.href=pg.wiki.titlebase + article;
mouseOverWikiLink2(a);
if (a.navpopup) {
a.navpopup.addHook(function(){runStopPopupTimer(a.navpopup);}, 'unhide', 'after');
}
}
function doSeparateSelectionPopup(str) {
var div=document.getElementById('selectionPreview');
if (!div) {
div = document.createElement('div');
div.id='selectionPreview';
try {
var box=document.editform.wpTextbox1;
box.parentNode.insertBefore(div, box);
} catch (error) {
return;
}
}
div.innerHTML=wiki2html(str);
div.ranSetupTooltipsAlready = false;
popTipsSoonFn('selectionPreview')();
}
//</NOLITE>
// ENDFILE: selpop.js
// STARTFILE: navpopup.js
/**
@fileoverview Defines two classes: {@link Navpopup} and {@link Mousetracker}.
<code>Navpopup</code> describes popups: when they appear, where, what
they look like and so on.
<code>Mousetracker</code> "captures" the mouse using
<code>document.onmousemove</code>.
*/
/**
Creates a new Mousetracker.
@constructor
@class The Mousetracker class. This monitors mouse movements and manages associated hooks.
*/
function Mousetracker() {
/**
Interval to regularly run the hooks anyway, in milliseconds.
@type Integer
*/
this.loopDelay=400;
/**
Timer for the loop.
@type Timer
*/
this.timer=null;
/**
Flag - are we switched on?
@type Boolean
*/
this.active=false;
/**
Flag - are we probably inaccurate, i.e. not reflecting the actual mouse position?
*/
this.dirty=true;
/**
Array of hook functions.
@private
@type Array
*/
this.hooks=[];
}
/**
Adds a hook, to be called when we get events.
@param {Function} f A function which is called as
<code>f(x,y)</code>. It should return <code>true</code> when it
wants to be removed, and <code>false</code> otherwise.
*/
Mousetracker.prototype.addHook = function (f) {
this.hooks.push(f);
};
/**
Runs hooks, passing them the x
and y coords of the mouse. Hook functions that return true are
passed to {@link Mousetracker#removeHooks} for removal.
@private
*/
Mousetracker.prototype.runHooks = function () {
if (!this.hooks || !this.hooks.length) { return; }
//log('Mousetracker.runHooks; we got some hooks to run');
var remove=false;
var removeObj={};
// this method gets called a LOT -
// pre-cache some variables
var x=this.x, y=this.y, len = this.hooks.length;
for (var i=0; i<len; ++i) {
//~ run the hook function, and remove it if it returns true
if (this.hooks[i](x, y)===true) {
remove=true;
removeObj[i]=true;
}
}
if (remove) { this.removeHooks(removeObj); }
};
/**
Removes hooks.
@private
@param {Object} removeObj An object whose keys are the index
numbers of functions for removal, with values that evaluate to true
*/
Mousetracker.prototype.removeHooks = function(removeObj) {
var newHooks=[];
var len = this.hooks.length;
for (var i=0; i<len; ++i) {
if (! removeObj[i]) { newHooks.push(this.hooks[i]); }
}
this.hooks=newHooks;
};
/**
Event handler for mouse wiggles.
We simply grab the event, set x and y and run the hooks.
This makes the cpu all hot and bothered :-(
@private
@param {Event} e Mousemove event
*/
Mousetracker.prototype.track=function (e) {
//~ Apparently this is needed in IE.
e = e || window.event;
var x, y;
if (e) {
if (e.pageX) { x=e.pageX; y=e.pageY; }
else if (typeof e.clientX!='undefined') {
var left, top, docElt = document.documentElement;
if (docElt) { left=docElt.scrollLeft; }
left = left || document.body.scrollLeft || document.scrollLeft || 0;
if (docElt) { top=docElt.scrollTop; }
top = top || document.body.scrollTop || document.scrollTop || 0;
x=e.clientX + left;
y=e.clientY + top;
} else { return; }
this.setPosition(x,y);
}
};
/**
Sets the x and y coordinates stored and takes appropriate action,
running hooks as appropriate.
@param {Integer} x, y Screen coordinates to set
*/
Mousetracker.prototype.setPosition=function(x,y) {
this.x = x;
this.y = y;
if (this.dirty || this.hooks.length === 0) { this.dirty=false; return; }
if (typeof this.lastHook_x != 'number') { this.lastHook_x = -100; this.lastHook_y=-100; }
var diff = (this.lastHook_x - x)*(this.lastHook_y - y);
diff = (diff >= 0) ? diff : -diff;
if ( diff > 1 ) {
this.lastHook_x=x;
this.lastHook_y=y;
if (this.dirty) { this.dirty = false; }
else { this.runHooks(); }
}
};
/**
Sets things in motion, unless they are already that is, registering an event handler on <code>document.onmousemove</code>.
A half-hearted attempt is made to preserve the old event handler if there is one.
*/
Mousetracker.prototype.enable = function () {
if (this.active) { return; }
this.active=true;
//~ Save the current handler for mousemove events. This isn't too
//~ robust, of course.
this.savedHandler=document.onmousemove;
//~ Gotta save @tt{this} again for the closure, and use apply for
//~ the member function.
var savedThis=this;
document.onmousemove=function (e) {savedThis.track.apply(savedThis, [e]);};
if (this.loopDelay) { this.timer = setInterval(function() { //log('loop delay in mousetracker is working');
savedThis.runHooks();}, this.loopDelay); }
};
/**
Disables the tracker, removing the event handler.
*/
Mousetracker.prototype.disable = function () {
if (!this.active) { return; }
if (typeof this.savedHandler === 'function') {
document.onmousemove=this.savedHandler;
} else { delete document.onmousemove; }
if (this.timer) { clearInterval(this.timer); }
this.active=false;
};
/**
Creates a new Navpopup.
Gets a UID for the popup and
@param init Contructor object. If <code>init.draggable</code> is true or absent, the popup becomes draggable.
@constructor
@class The Navpopup class. This generates popup hints, and does some management of them.
*/
function Navpopup(/*init*/) {
//alert('new Navpopup(init)');
/** UID for each Navpopup instance.
Read-only.
@type integer
*/
this.uid=Navpopup.uid++;
/**
Read-only flag for current visibility of the popup.
@type boolean
@private
*/
this.visible=false;
/** Flag to be set when we want to cancel a previous request to
show the popup in a little while.
@private
@type boolean
*/
this.noshow=false;
/** Categorised list of hooks.
@see #runHooks
@see #addHook
@private
@type Object
*/
this.hooks={
'create': [],
'unhide': [],
'hide': []
};
/** list of unique IDs of hook functions, to avoid duplicates
@private
*/
this.hookIds={};
/** List of downloads associated with the popup.
@private
@type Array
*/
this.downloads=[];
/** Number of uncompleted downloads.
@type integer
*/
this.pending=null;
/** Tolerance in pixels when detecting whether the mouse has left the popup.
@type integer
*/
this.fuzz=5;
/** Flag to toggle running {@link #limitHorizontalPosition} to regulate the popup's position.
@type boolean
*/
this.constrained=true;
/** The popup width in pixels.
@private
@type integer
*/
this.width=0;
/** The popup width in pixels.
@private
@type integer
*/
this.height=0;
/** The main content DIV element.
@type HTMLDivElement
*/
this.mainDiv=null;
this.createMainDiv();
// if (!init || typeof init.popups_draggable=='undefined' || init.popups_draggable) {
// this.makeDraggable(true);
// }
}
/**
A UID for each Navpopup. This constructor property is just a counter.
@type integer
@private
*/
Navpopup.uid=0;
/**
Retrieves the {@link #visible} attribute, indicating whether the popup is currently visible.
@type boolean
*/
Navpopup.prototype.isVisible=function() {
return this.visible;
};
/**
Repositions popup using CSS style.
@private
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@param {boolean} noLimitHor Don't call {@link #limitHorizontalPosition}
*/
Navpopup.prototype.reposition= function (x,y, noLimitHor) {
log ('reposition('+x+','+y+','+noLimitHor+')');
if (typeof x != 'undefined' && x !== null) { this.left=x; }
if (typeof y != 'undefined' && y !== null) { this.top=y; }
if (typeof this.left != 'undefined' && typeof this.top != 'undefined') {
this.mainDiv.style.left=this.left + 'px';
this.mainDiv.style.top=this.top + 'px';
}
if (!noLimitHor) { this.limitHorizontalPosition(); }
//console.log('navpop'+this.uid+' - (left,top)=(' + this.left + ',' + this.top + '), css=('
//+ this.mainDiv.style.left + ',' + this.mainDiv.style.top + ')');
};
/**
Prevents popups from being in silly locations. Hopefully.
Should not be run if {@link #constrained} is true.
@private
*/
Navpopup.prototype.limitHorizontalPosition=function() {
if (!this.constrained || this.tooWide) { return; }
this.updateDimensions();
var x=this.left;
var w=this.width;
var cWidth=document.body.clientWidth;
// log('limitHorizontalPosition: x='+x+
// ', this.left=' + this.left +
// ', this.width=' + this.width +
// ', cWidth=' + cWidth);
if ( (x+w) >= cWidth ||
( x > 0 &&
this.maxWidth &&
this.width < this.maxWidth &&
this.height > this.width &&
x > cWidth - this.maxWidth ) ) {
// This is a very nasty hack. There has to be a better way!
// We find the "natural" width of the div by positioning it at the far left
// then reset it so that it should be flush right (well, nearly)
this.mainDiv.style.left='-10000px';
this.mainDiv.style.width = this.maxWidth + 'px';
var naturalWidth=parseInt(this.mainDiv.offsetWidth, 10);
var newLeft=cWidth - naturalWidth - 1;
if (newLeft < 0) { newLeft = 0; this.tooWide=true; } // still unstable for really wide popups?
log ('limitHorizontalPosition: moving to ('+newLeft + ','+ this.top+');' + ' naturalWidth=' + naturalWidth + ', clientWidth=' + cWidth);
this.reposition(newLeft, null, true);
}
};
/**
Counter indicating the z-order of the "highest" popup.
We start the z-index at 1000 so that popups are above everything
else on the screen.
@private
@type integer
*/
Navpopup.highest=1000;
/**
Brings popup to the top of the z-order.
We increment the {@link #highest} property of the contructor here.
@private
*/
Navpopup.prototype.raise = function () {
this.mainDiv.style.zIndex=Navpopup.highest + 1;
++Navpopup.highest;
};
/**
Shows the popup provided {@link #noshow} is not true.
Updates the position, brings the popup to the top of the z-order and unhides it.
*/
Navpopup.prototype.show = function () {
//document.title+='s';
if (this.noshow) { return; }
//document.title+='t';
this.reposition();
this.raise();
this.unhide();
};
/**
Checks to see if the mouse pointer has
stabilised (checking every <code>time</code>/2 milliseconds) and runs the
{@link #show} method if it has.
@param {integer} time The minimum time (ms) before the popup may be shown.
*/
Navpopup.prototype.showSoonIfStable = function (time) {
log ('showSoonIfStable, time='+time);
if (this.visible) { return; }
this.noshow = false;
//~ initialize these variables so that we never run @tt{show} after
//~ just half the time
this.stable_x = -10000; this.stable_y = -10000;
var stableShow = function() {
log('stableShow called');
var new_x = Navpopup.tracker.x, new_y = Navpopup.tracker.y;
var dx = savedThis.stable_x - new_x, dy = savedThis.stable_y - new_y;
var fuzz2 = 0; // savedThis.fuzz * savedThis.fuzz;
//document.title += '[' + [savedThis.stable_x,new_x, savedThis.stable_y,new_y, dx, dy, fuzz2].join(',') + '] ';
if ( dx * dx <= fuzz2 && dy * dy <= fuzz2 ) {
log ('mouse is stable');
clearInterval(savedThis.showSoonStableTimer);
savedThis.reposition.apply(savedThis, [new_x + 2, new_y + 2]);
savedThis.show.apply(savedThis, []);
savedThis.limitHorizontalPosition.apply(savedThis, []);
return;
}
savedThis.stable_x = new_x; savedThis.stable_y = new_y;
};
var savedThis = this;
this.showSoonStableTimer = setInterval(stableShow, time/2);
};
/**
Sets the {@link #noshow} flag and hides the popup. This should be called
when the mouse leaves the link before
(or after) it's actually been displayed.
*/
Navpopup.prototype.banish = function () {
log ('banish called');
// hide and prevent showing with showSoon in the future
this.noshow=true;
if (this.showSoonStableTimer) {
log('clearing showSoonStableTimer');
clearInterval(this.showSoonStableTimer);
}
this.hide();
};
/**
Runs hooks added with {@link #addHook}.
@private
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
*/
Navpopup.prototype.runHooks = function (key, when) {
if (!this.hooks[key]) { return; }
var keyHooks=this.hooks[key];
var len=keyHooks.length;
for (var i=0; i< len; ++i) {
if (keyHooks[i] && keyHooks[i].when == when) {
if (keyHooks[i].hook.apply(this, [])) {
// remove the hook
if (keyHooks[i].hookId) {
delete this.hookIds[keyHooks[i].hookId];
}
keyHooks[i]=null;
}
}
}
};
/**
Adds a hook to the popup. Hook functions are run with <code>this</code> set to refer to the Navpopup instance, and no arguments.
@param {Function} hook The hook function. Functions that return true are deleted.
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
@param {String} uid A truthy string identifying the hook function; if it matches another hook in this position, it won't be added again.
*/
Navpopup.prototype.addHook = function ( hook, key, when, uid ) {
when = when || 'after';
if (!this.hooks[key]) { return; }
// if uid is specified, don't add duplicates
var hookId=null;
if (uid) {
hookId=[key,when,uid].join('|');
if (this.hookIds[hookId]) {
return;
}
this.hookIds[hookId]=true;
}
this.hooks[key].push( {hook: hook, when: when, hookId: hookId} );
};
/**
Creates the main DIV element, which contains all the actual popup content.
Runs hooks with key 'create'.
@private
*/
Navpopup.prototype.createMainDiv = function () {
if (this.mainDiv) { return; }
this.runHooks('create', 'before');
var mainDiv=document.createElement('div');
var savedThis=this;
mainDiv.onclick=function(e) {savedThis.onclickHandler(e);};
mainDiv.className=(this.className) ? this.className : 'navpopup_maindiv';
mainDiv.id=mainDiv.className + this.uid;
mainDiv.style.position='absolute';
mainDiv.style.minWidth = '350px';
mainDiv.style.display='none';
mainDiv.className='navpopup';
// easy access to javascript object through DOM functions
mainDiv.navpopup=this;
this.mainDiv=mainDiv;
document.body.appendChild(mainDiv);
this.runHooks('create', 'after');
};
/**
Calls the {@link #raise} method.
@private
*/
Navpopup.prototype.onclickHandler=function(/*e*/) {
this.raise();
};
/**
Makes the popup draggable, using a {@link Drag} object.
@private
*/
Navpopup.prototype.makeDraggable=function(handleName) {
if (!this.mainDiv) { this.createMainDiv(); }
var drag=new Drag();
if (!handleName) {
drag.startCondition=function(e) {
try { if (!e.shiftKey) { return false; } } catch (err) { return false; }
return true;
};
}
var dragHandle;
if (handleName) dragHandle = document.getElementById(handleName);
if (!dragHandle) dragHandle = this.mainDiv;
var np=this;
drag.endHook=function(x,y) {
Navpopup.tracker.dirty=true;
np.reposition(x,y);
};
drag.init(dragHandle,this.mainDiv);
};
/** Hides the popup using CSS. Runs hooks with key 'hide'.
Sets {@link #visible} appropriately. {@link #banish} should be called externally instead of this method.
@private
*/
Navpopup.prototype.hide = function () {
this.runHooks('hide', 'before');
this.abortDownloads();
if (typeof this.visible != 'undefined' && this.visible) {
this.mainDiv.style.display='none';
this.visible=false;
}
this.runHooks('hide', 'after');
};
/** Shows the popup using CSS. Runs hooks with key 'unhide'.
Sets {@link #visible} appropriately. {@link #show} should be called externally instead of this method.
@private
*/
Navpopup.prototype.unhide = function () {
this.runHooks('unhide', 'before');
if (typeof this.visible != 'undefined' && !this.visible) {
this.mainDiv.style.display='inline';
this.visible=true;
}
this.runHooks('unhide', 'after');
};
/**
Sets the <code>innerHTML</code> attribute of the main div containing the popup content.
@param {String} html The HTML to set.
*/
Navpopup.prototype.setInnerHTML = function (html) {
this.mainDiv.innerHTML = html;
};
/**
Updates the {@link #width} and {@link #height} attributes with the CSS properties.
@private
*/
Navpopup.prototype.updateDimensions = function () {
this.width=parseInt(this.mainDiv.offsetWidth, 10);
this.height=parseInt(this.mainDiv.offsetHeight, 10);
};
/**
Checks if the point (x,y) is within {@link #fuzz} of the
{@link #mainDiv}.
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@type boolean
*/
Navpopup.prototype.isWithin = function(x,y) {
//~ If we're not even visible, no point should be considered as
//~ being within the popup.
if (!this.visible) { return false; }
this.updateDimensions();
var fuzz=this.fuzz || 0;
//~ Use a simple box metric here.
return (x+fuzz >= this.left && x-fuzz <= this.left + this.width &&
y+fuzz >= this.top && y-fuzz <= this.top + this.height);
};
/**
Adds a download to {@link #downloads}.
@param {Downloader} download
*/
Navpopup.prototype.addDownload=function(download) {
if (!download) { return; }
this.downloads.push(download);
};
/**
Aborts the downloads listed in {@link #downloads}.
@see Downloader#abort
*/
Navpopup.prototype.abortDownloads=function() {
for(var i=0; i<this.downloads.length; ++i) {
var d=this.downloads[i];
if (d && d.abort) { d.abort(); }
}
this.downloads=[];
};
/**
A {@link Mousetracker} instance which is a property of the constructor (pseudo-global).
*/
Navpopup.tracker=new Mousetracker();
// ENDFILE: navpopup.js
// STARTFILE: diff.js
//<NOLITE>
/*
* Javascript Diff Algorithm
* By John Resig (http://ejohn.org/) and [[:en:User:Lupin]]
*
* More Info:
* http://ejohn.org/projects/javascript-diff-algorithm/
*/
function delFmt(x) {
if (!x.length) { return ''; }
return "<del class='popupDiff'>" + x.join('') +"</del>";
}
function insFmt(x) {
if (!x.length) { return ''; }
return "<ins class='popupDiff'>" + x.join('') +"</ins>";
}
function countCrossings(a, b, i, eject) {
// count the crossings on the edge starting at b[i]
if (!b[i].row && b[i].row !== 0) { return -1; }
var count=0;
for (var j=0; j<a.length; ++j) {
if (!a[j].row && a[j].row !== 0) { continue; }
if ( (j-b[i].row)*(i-a[j].row) > 0) {
if(eject) { return true; }
count++;
}
}
return count;
}
function shortenDiffString(str, context) {
var re=RegExp('(<del[\\s\\S]*?</del>|<ins[\\s\\S]*?</ins>)');
var splitted=str.parenSplit(re);
var ret=[''];
for (var i=0; i<splitted.length; i+=2) {
if (splitted[i].length < 2*context) {
ret[ret.length-1] += splitted[i];
if (i+1<splitted.length) { ret[ret.length-1] += splitted[i+1]; }
continue;
}
else {
if (i > 0) { ret[ret.length-1] += splitted[i].substring(0,context); }
if (i+1 < splitted.length) {
ret.push(splitted[i].substring(splitted[i].length-context) +
splitted[i+1]);
}
}
}
while (ret.length > 0 && !ret[0]) { ret = ret.slice(1); }
return ret;
}
function diffString( o, n, simpleSplit ) {
var splitRe=RegExp('([[]{2}|[\\]]{2}|[{]{2,3}|[}]{2,3}|[|]|=|<|>|[*:]+|\\s|\\b)');
// We need to split the strings o and n first, and entify() the parts
// individually, so that the HTML entities are never cut apart. (AxelBoldt)
var out, i, oSplitted, nSplitted;
if (simpleSplit) {
oSplitted=o.split(/\b/);
nSplitted=n.split(/\b/);
} else {
oSplitted=o.parenSplit(splitRe);
nSplitted=n.parenSplit(splitRe);
}
for (i=0; i<oSplitted.length; ++i) {oSplitted[i]=oSplitted[i].entify();}
for (i=0; i<nSplitted.length; ++i) {nSplitted[i]=nSplitted[i].entify();}
out = diff (oSplitted, nSplitted);
var str = "";
var acc=[]; // accumulator for prettier output
// crossing pairings -- eg 'A B' vs 'B A' -- cause problems, so let's iron them out
// this doesn't always do things optimally but it should be fast enough
var maxOutputPair=0;
for (i=0; i<out.n.length; ++i) {
if ( out.n[i].paired ) {
if( maxOutputPair > out.n[i].row ) {
// tangle - delete pairing
out.o[ out.n[i].row ]=out.o[ out.n[i].row ].text;
out.n[i]=out.n[i].text;
}
if (maxOutputPair < out.n[i].row) { maxOutputPair = out.n[i].row; }
}
}
// output the stuff preceding the first paired old line
for (i=0; i<out.o.length && !out.o[i].paired; ++i) { acc.push( out.o[i] ); }
str += delFmt(acc); acc=[];
// main loop
for ( i = 0; i < out.n.length; ++i ) {
// output unpaired new "lines"
while ( i < out.n.length && !out.n[i].paired ) { acc.push( out.n[i++] ); }
str += insFmt(acc); acc=[];
if ( i < out.n.length ) { // this new "line" is paired with the (out.n[i].row)th old "line"
str += out.n[i].text;
// output unpaired old rows starting after this new line's partner
var m = out.n[i].row + 1;
while ( m < out.o.length && !out.o[m].paired ) { acc.push ( out.o[m++] ); }
str += delFmt(acc); acc=[];
}
}
return str;
}
// see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Object
// FIXME: use obj.hasOwnProperty instead of this kludge!
var jsReservedProperties=RegExp('^(constructor|prototype|__((define|lookup)[GS]etter)__' +
'|eval|hasOwnProperty|propertyIsEnumerable' +
'|to(Source|String|LocaleString)|(un)?watch|valueOf)$');
function diffBugAlert(word) {
if (!diffBugAlert.list[word]) {
diffBugAlert.list[word]=1;
alert('Bad word: '+word+'\n\nPlease report this bug.');
}
}
diffBugAlert.list={};
function makeDiffHashtable(src) {
var ret={};
for ( var i = 0; i < src.length; i++ ) {
if ( jsReservedProperties.test(src[i]) ) { src[i] += '<!-- -->'; }
if ( !ret[ src[i] ] ) { ret[ src[i] ] = []; }
try { ret[ src[i] ].push( i ); } catch (err) { diffBugAlert(src[i]); }
}
return ret;
}
function diff( o, n ) {
// pass 1: make hashtable ns with new rows as keys
var ns = makeDiffHashtable(n);
// pass 2: make hashtable os with old rows as keys
var os = makeDiffHashtable(o);
// pass 3: pair unique new rows and matching unique old rows
var i;
for ( i in ns ) {
if ( ns[i].length == 1 && os[i] && os[i].length == 1 ) {
n[ ns[i][0] ] = { text: n[ ns[i][0] ], row: os[i][0], paired: true };
o[ os[i][0] ] = { text: o[ os[i][0] ], row: ns[i][0], paired: true };
}
}
// pass 4: pair matching rows immediately following paired rows (not necessarily unique)
for ( i = 0; i < n.length - 1; i++ ) {
if ( n[i].paired && ! n[i+1].paired && n[i].row + 1 < o.length && ! o[ n[i].row + 1 ].paired &&
n[i+1] == o[ n[i].row + 1 ] ) {
n[i+1] = { text: n[i+1], row: n[i].row + 1, paired: true };
o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1, paired: true };
}
}
// pass 5: pair matching rows immediately preceding paired rows (not necessarily unique)
for ( i = n.length - 1; i > 0; i-- ) {
if ( n[i].paired && ! n[i-1].paired && n[i].row > 0 && ! o[ n[i].row - 1 ].paired &&
n[i-1] == o[ n[i].row - 1 ] ) {
n[i-1] = { text: n[i-1], row: n[i].row - 1, paired: true };
o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1, paired: true };
}
}
return { o: o, n: n };
}
//</NOLITE>
// ENDFILE: diff.js
// STARTFILE: init.js
function setSiteInfo() {
if (window.popupLocalDebug) {
pg.wiki.hostname = 'en.wikipedia.org';
} else {
pg.wiki.hostname = location.hostname; // use in preference to location.hostname for flexibility (?)
}
pg.wiki.wikimedia=RegExp('(wiki([pm]edia|source|books|news|quote|versity)|wiktionary|mediawiki)[.]org').test(pg.wiki.hostname);
pg.wiki.wikia=RegExp('[.]wikia[.]com$', 'i').test(pg.wiki.hostname);
pg.wiki.isLocal=RegExp('^localhost').test(pg.wiki.hostname);
pg.wiki.commons=( pg.wiki.wikimedia && pg.wiki.hostname != 'commons.wikimedia.org') ? 'commons.wikimedia.org' : null;
pg.wiki.lang = mw.config.get('wgContentLanguage');
var port = location.port ? ':' + location.port : '';
pg.wiki.sitebase = pg.wiki.hostname + port;
}
function setUserInfo() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
list: 'users',
ususers: mw.config.get('wgUserName'),
usprop: 'rights'
};
pg.user.canReview = false;
if (getValueOf('popupReview')) {
api.get(params).done(function(data){
var rights = data.query.users[0].rights;
pg.user.canReview = rights.indexOf('review') !== -1; // TODO: Should it be a getValueOf('ReviewRight') ?
});
}
}
function setTitleBase() {
var protocol = ( window.popupLocalDebug ? 'http:' : location.protocol );
pg.wiki.articlePath = mw.config.get('wgArticlePath').replace(/\/\$1/, ""); // as in http://some.thing.com/wiki/Article
pg.wiki.botInterfacePath = mw.config.get('wgScript');
pg.wiki.APIPath = mw.config.get('wgScriptPath') +"/api.php";
// default mediawiki setting is paths like http://some.thing.com/articlePath/index.php?title=foo
var titletail = pg.wiki.botInterfacePath + '?title=';
//var titletail2 = joinPath([pg.wiki.botInterfacePath, 'wiki.phtml?title=']);
// other sites may need to add code here to set titletail depending on how their urls work
pg.wiki.titlebase = protocol + '//' + pg.wiki.sitebase + titletail;
//pg.wiki.titlebase2 = protocol + '//' + joinPath([pg.wiki.sitebase, titletail2]);
pg.wiki.wikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.botInterfacePath;
pg.wiki.apiwikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.APIPath;
pg.wiki.articlebase = protocol + '//' + pg.wiki.sitebase + pg.wiki.articlePath;
pg.wiki.commonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.botInterfacePath;
pg.wiki.apicommonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.APIPath;
pg.re.basenames = RegExp( '^(' +
map( literalizeRegex, [ pg.wiki.titlebase, //pg.wiki.titlebase2,
pg.wiki.articlebase ]).join('|') + ')' );
}
//////////////////////////////////////////////////
// Global regexps
function setMainRegex() {
var reStart='[^:]*://';
var preTitles = literalizeRegex( mw.config.get('wgScriptPath') ) + '/(?:index[.]php|wiki[.]phtml)[?]title=';
preTitles += '|' + literalizeRegex( pg.wiki.articlePath + '/' );
var reEnd='(' + preTitles + ')([^&?#]*)[^#]*(?:#(.+))?';
pg.re.main = RegExp(reStart + literalizeRegex(pg.wiki.sitebase) + reEnd);
}
function setRegexps() {
// TODO: We shoud use an api call to get the aliases for special pages, now it does not work for non-English wikipedias:
// E.g., https://ru.wikipedia.org/w/api.php?action=query&meta=siteinfo&siprop=specialpagealiases&formatversion=2
setMainRegex();
var sp=nsRe(pg.nsSpecialId);
pg.re.urlNoPopup=RegExp('((title=|/)' + sp + '(?:%3A|:)|section=[0-9]|^#$)') ;
pg.re.contribs =RegExp('(title=|/)' + sp + '(?:%3A|:)Contributions' + '(&target=|/|/' + nsRe(pg.nsUserId)+':)(.*)') ;
pg.re.email =RegExp('(title=|/)' + sp + '(?:%3A|:)EmailUser' + '(&target=|/|/(?:' + nsRe(pg.nsUserId)+':)?)(.*)') ;
pg.re.backlinks =RegExp('(title=|/)' + sp + '(?:%3A|:)WhatLinksHere' + '(&target=|/)([^&]*)');
pg.re.specialdiff=RegExp('/' + sp + '(?:%3A|:)Diff/([^?#]*)');
//<NOLITE>
var im=nsReImage();
// note: tries to get images in infobox templates too, e.g. movie pages, album pages etc
// (^|\[\[)image: *([^|\]]*[^|\] ]) *
// (^|\[\[)image: *([^|\]]*[^|\] ])([^0-9\]]*([0-9]+) *px)?
// $4 = 120 as in 120px
pg.re.image = RegExp('(^|\\[\\[)' + im + ': *([^|\\]]*[^|\\] ])' +
'([^0-9\\]]*([0-9]+) *px)?|(?:\\n *[|]?|[|]) *' +
'(' + getValueOf('popupImageVarsRegexp') + ')' +
' *= *(?:\\[\\[ *)?(?:' + im + ':)?' +
'([^|]*?)(?:\\]\\])? *[|]? *\\n', 'img') ;
pg.re.imageBracketCount = 6;
pg.re.category = RegExp('\\[\\[' +nsRe(pg.nsCategoryId) +
': *([^|\\]]*[^|\\] ]) *', 'i');
pg.re.categoryBracketCount = 1;
pg.re.ipUser=RegExp('^' +
// IPv6
'(?::(?::|(?::[0-9A-Fa-f]{1,4}){1,7})|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,6}::|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){7})' +
// IPv4
'|(((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}' +
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9]))$');
pg.re.stub= RegExp(getValueOf('popupStubRegexp'), 'im');
pg.re.disambig=RegExp(getValueOf('popupDabRegexp'), 'im');
//</NOLITE>
// FIXME replace with general parameter parsing function, this is daft
pg.re.oldid=RegExp('[?&]oldid=([^&]*)');
pg.re.diff=RegExp('[?&]diff=([^&]*)');
}
//////////////////////////////////////////////////
// miscellany
function setupCache() {
// page caching
pg.cache.pages = [];
}
function setMisc() {
pg.current.link=null;
pg.current.links=[];
pg.current.linksHash={};
setupCache();
pg.timer.checkPopupPosition=null;
pg.counter.loop=0;
// ids change with each popup: popupImage0, popupImage1 etc
pg.idNumber=0;
// for myDecodeURI
pg.misc.decodeExtras = [
{from: '%2C', to: ',' },
{from: '_', to: ' ' },
{from: '%24', to: '$'},
{from: '%26', to: '&' } // no ,
];
pg.misc.userAgent = 'Navigation popups/1.0 (' + mw.config.get( 'wgServerName' ) +')';
}
// We need a callback since this might end up asynchronous because of
// the mw.loader.using() call.
function setupPopups( callback ) {
if ( setupPopups.completed ) {
if ( typeof callback === 'function' ) {
callback();
}
return;
}
// These dependencies are also enforced from the gadget,
// but not everyone loads this as a gadget, so double check
mw.loader.using( ['mediawiki.util', 'mediawiki.user', 'user.options'] ).then( function() {
// NB translatable strings should be set up first (strings.js)
// basics
setupDebugging();
setSiteInfo();
setTitleBase();
setOptions(); // see options.js
setUserInfo();
// namespaces etc
setNamespaces();
setInterwiki();
// regexps
setRegexps();
setRedirs();
// other stuff
setMisc();
setupLivePreview();
// main deal here
setupTooltips();
log('In setupPopups(), just called setupTooltips()');
Navpopup.tracker.enable();
setupPopups.completed = true;
if ( typeof callback === 'function' ) {
callback();
}
});
}
// ENDFILE: init.js
// STARTFILE: navlinks.js
//<NOLITE>
//////////////////////////////////////////////////
// navlinks... let the fun begin
//
function defaultNavlinkSpec() {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
if (getValueOf('popupLastEditLink')) {
str += '*<<lastEdit|shortcut=/>>|<<lastContrib>>|<<sinceMe>>if(oldid){|<<oldEdit>>|<<diffCur>>}';
}
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>*<<userlog|shortcut=L|log>>';
str+='if(ipuser){*<<arin>>}if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>|<<blocklog|log>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' + editstr + '}';
var historystr='<<history|shortcut=h>>|<<editors|shortcut=E|>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str+='<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>*<<move|shortcut=m>>';
// admin links
str += 'if(admin){<br><<unprotect|unprotectShort>>|<<protect|shortcut=p>>|<<protectlog|log>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>|<<deletelog|log>>}';
return str;
}
function navLinksHTML (article, hint, params) { //oldid, rcid) {
var str = '<span class="popupNavLinks">' + defaultNavlinkSpec() + '</span>';
// BAM
return navlinkStringToHTML(str, article, params);
}
function expandConditionalNavlinkString(s,article,z,recursionCount) {
var oldid=z.oldid, rcid=z.rcid, diff=z.diff;
// nested conditionals (up to 10 deep) are ok, hopefully! (work from the inside out)
if (typeof recursionCount!=typeof 0) { recursionCount=0; }
var conditionalSplitRegex=RegExp(
//(1 if \\( (2 2) \\) {(3 3)} (4 else {(5 5)} 4)1)
'(;?\\s*if\\s*\\(\\s*([\\w]*)\\s*\\)\\s*\\{([^{}]*)\\}(\\s*else\\s*\\{([^{}]*?)\\}|))', 'i');
var splitted=s.parenSplit(conditionalSplitRegex);
// $1: whole conditional
// $2: test condition
// $3: true expansion
// $4: else clause (possibly empty)
// $5: false expansion (possibly null)
var numParens=5;
var ret = splitted[0];
for (var i=1; i<splitted.length; i=i+numParens+1) {
var testString=splitted[i+2-1];
var trueString=splitted[i+3-1];
var falseString=splitted[i+5-1];
if (typeof falseString=='undefined' || !falseString) { falseString=''; }
var testResult=null;
switch (testString) {
case 'user':
testResult=(article.userName())?true:false;
break;
case 'talk':
testResult=(article.talkPage())?false:true; // talkPage converts _articles_ to talkPages
break;
case 'admin':
testResult=getValueOf('popupAdminLinks')?true:false;
break;
case 'oldid':
testResult=(typeof oldid != 'undefined' && oldid)?true:false;
break;
case 'rcid':
testResult=(typeof rcid != 'undefined' && rcid)?true:false;
break;
case 'ipuser':
testResult=(article.isIpUser())?true:false;
break;
case 'mainspace_en':
testResult=isInMainNamespace(article) &&
pg.wiki.hostname=='en.wikipedia.org';
break;
case 'wikimedia':
testResult=(pg.wiki.wikimedia) ? true : false;
break;
case 'diff':
testResult=(typeof diff != 'undefined' && diff)?true:false;
break;
}
switch(testResult) {
case null: ret+=splitted[i]; break;
case true: ret+=trueString; break;
case false: ret+=falseString; break;
}
// append non-conditional string
ret += splitted[i+numParens];
}
if (conditionalSplitRegex.test(ret) && recursionCount < 10) {
return expandConditionalNavlinkString(ret,article,z,recursionCount+1);
}
return ret;
}
function navlinkStringToArray(s, article, params) {
s=expandConditionalNavlinkString(s,article,params);
var splitted=s.parenSplit(RegExp('<<(.*?)>>'));
var ret=[];
for (var i=0; i<splitted.length; ++i) {
if (i%2) { // i odd, so s is a tag
var t=new navlinkTag();
var ss=splitted[i].split('|');
t.id=ss[0];
for (var j=1; j<ss.length; ++j) {
var sss=ss[j].split('=');
if (sss.length>1) {
t[sss[0]]=sss[1];
}
else { // no assignment (no "="), so treat this as a title (overwriting the last one)
t.text=popupString(sss[0]);
}
}
t.article=article;
var oldid=params.oldid, rcid=params.rcid, diff=params.diff;
if (typeof oldid !== 'undefined' && oldid !== null) { t.oldid=oldid; }
if (typeof rcid !== 'undefined' && rcid !== null) { t.rcid=rcid; }
if (typeof diff !== 'undefined' && diff !== null) { t.diff=diff; }
if (!t.text && t.id !== 'mainlink') { t.text=popupString(t.id); }
ret.push(t);
}
else { // plain HTML
ret.push(splitted[i]);
}
}
return ret;
}
function navlinkSubstituteHTML(s) {
return s.split('*').join(getValueOf('popupNavLinkSeparator'))
.split('<menurow>').join('<li class="popup_menu_row">')
.split('</menurow>').join('</li>')
.split('<menu>').join('<ul class="popup_menu">')
.split('</menu>').join('</ul>');
}
function navlinkDepth(magic,s) {
return s.split('<' + magic + '>').length - s.split('</' + magic + '>').length;
}
// navlinkString: * becomes the separator
// <<foo|bar=baz|fubar>> becomes a foo-link with attribute bar='baz'
// and visible text 'fubar'
// if(test){...} and if(test){...}else{...} work too (nested ok)
function navlinkStringToHTML(s,article,params) {
//limitAlert(navlinkStringToHTML, 5, 'navlinkStringToHTML\n' + article + '\n' + (typeof article));
var p=navlinkStringToArray(s,article,params);
var html='';
var menudepth = 0; // nested menus not currently allowed, but doesn't do any harm to code for it
var menurowdepth = 0;
for (var i=0; i<p.length; ++i) {
if (typeof p[i] == typeof '') {
html+=navlinkSubstituteHTML(p[i]);
menudepth += navlinkDepth('menu', p[i]);
menurowdepth += navlinkDepth('menurow', p[i]);
// if (menudepth === 0) {
// tagType='span';
// } else if (menurowdepth === 0) {
// tagType='li';
// } else {
// tagType = null;
// }
} else if (typeof p[i].type != 'undefined' && p[i].type=='navlinkTag') {
if (menudepth > 0 && menurowdepth === 0) {
html += '<li class="popup_menu_item">' + p[i].html() + '</li>';
} else {
html+=p[i].html();
}
}
}
return html;
}
function navlinkTag() {
this.type='navlinkTag';
}
navlinkTag.prototype.html=function () {
this.getNewWin();
this.getPrintFunction();
var html='';
var opening, closing;
var tagType='span';
if (!tagType) {
opening = ''; closing = '';
} else {
opening = '<' + tagType + ' class="popup_' + this.id + '">';
closing = '</' + tagType + '>';
}
if (typeof this.print!='function') {
errlog ('Oh dear - invalid print function for a navlinkTag, id='+this.id);
} else {
html=this.print(this);
if (typeof html != typeof '') {html='';}
else if (typeof this.shortcut!='undefined') html=addPopupShortcut(html, this.shortcut);
}
return opening + html + closing;
};
navlinkTag.prototype.getNewWin=function() {
getValueOf('popupLinksNewWindow');
if (typeof pg.option.popupLinksNewWindow[this.id] === 'undefined') { this.newWin=null; }
this.newWin=pg.option.popupLinksNewWindow[this.id];
};
navlinkTag.prototype.getPrintFunction=function() { //think about this some more
// this.id and this.article should already be defined
if (typeof this.id!=typeof '' || typeof this.article!=typeof {} ) { return; }
this.noPopup=1;
switch (this.id) {
case 'contribs': case 'history': case 'whatLinksHere':
case 'userPage': case 'monobook': case 'userTalk':
case 'talk': case 'article': case 'lastEdit':
this.noPopup=null;
}
switch (this.id) {
case 'email': case 'contribs': case 'block': case 'unblock':
case 'userlog': case 'userSpace': case 'deletedContribs':
this.article=this.article.userName();
}
switch (this.id) {
case 'userTalk': case 'newUserTalk': case 'editUserTalk':
case 'userPage': case 'monobook': case 'editMonobook': case 'blocklog':
this.article=this.article.userName(true);
/* fall through */
case 'pagelog': case 'deletelog': case 'protectlog':
delete this.oldid;
}
if (this.id=='editMonobook' || this.id=='monobook') { this.article.append('/monobook.js'); }
if (this.id != 'mainlink') {
// FIXME anchor handling should be done differently with Title object
this.article=this.article.removeAnchor();
// if (typeof this.text=='undefined') this.text=popupString(this.id);
}
switch (this.id) {
case 'undelete': this.print=specialLink; this.specialpage='Undelete'; this.sep='/'; break;
case 'whatLinksHere': this.print=specialLink; this.specialpage='Whatlinkshere'; break;
case 'relatedChanges': this.print=specialLink; this.specialpage='Recentchangeslinked'; break;
case 'move': this.print=specialLink; this.specialpage='Movepage'; break;
case 'contribs': this.print=specialLink; this.specialpage='Contributions'; break;
case 'deletedContribs':this.print=specialLink; this.specialpage='Deletedcontributions'; break;
case 'email': this.print=specialLink; this.specialpage='EmailUser'; this.sep='/'; break;
case 'block': this.print=specialLink; this.specialpage='Blockip'; this.sep='&ip='; break;
case 'unblock': this.print=specialLink; this.specialpage='Ipblocklist'; this.sep='&action=unblock&ip='; break;
case 'userlog': this.print=specialLink; this.specialpage='Log'; this.sep='&user='; break;
case 'blocklog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=block&page='; break;
case 'pagelog': this.print=specialLink; this.specialpage='Log'; this.sep='&page='; break;
case 'protectlog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=protect&page='; break;
case 'deletelog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=delete&page='; break;
case 'userSpace': this.print=specialLink; this.specialpage='PrefixIndex'; this.sep='&namespace=2&prefix='; break;
case 'search': this.print=specialLink; this.specialpage='Search'; this.sep='&fulltext=Search&search='; break;
case 'thank': this.print=specialLink; this.specialpage='Thanks'; this.sep='/'; this.article.value = this.diff; break;
case 'unwatch': case 'watch':
this.print=magicWatchLink; this.action=this.id+'&autowatchlist=1&autoimpl=' + popupString('autoedit_version') + '&actoken='+autoClickToken(); break;
case 'history': case 'historyfeed':
case 'unprotect': case 'protect':
this.print=wikiLink; this.action=this.id; break;
case 'delete':
this.print=wikiLink; this.action='delete';
if (this.article.namespaceId()==pg.nsImageId) {
var img=this.article.stripNamespace();
this.action+='&image='+img;
}
break;
case 'markpatrolled':
case 'edit': // editOld should keep the oldid, but edit should not.
delete this.oldid;
/* fall through */
case 'view': case 'purge': case 'render':
this.print=wikiLink;
this.action=this.id; break;
case 'raw':
this.print=wikiLink; this.action='raw'; break;
case 'new':
this.print=wikiLink; this.action='edit§ion=new'; break;
case 'mainlink':
if (typeof this.text=='undefined') { this.text=this.article.toString().entify(); }
if (getValueOf('popupSimplifyMainLink') && isInStrippableNamespace(this.article)) {
// only show the /subpage part of the title text
var s=this.text.split('/'); this.text=s[s.length-1];
if (this.text==='' && s.length > 1) { this.text=s[s.length-2]; }
}
this.print=titledWikiLink;
if (typeof this.title==='undefined' && pg.current.link && typeof pg.current.link.href !== 'undefined') {
this.title=safeDecodeURI((pg.current.link.originalTitle)?pg.current.link.originalTitle:this.article);
if (typeof this.oldid !== 'undefined' && this.oldid) {
this.title=tprintf('Revision %s of %s', [this.oldid, this.title]);
}
}
this.action='view'; break;
case 'userPage':
case 'article':
case 'monobook':
case 'editMonobook':
case 'editArticle':
delete this.oldid;
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.article=this.article.articleFromTalkOrArticle();
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.print=wikiLink;
if (this.id.indexOf('edit')===0) {
this.action='edit';
} else { this.action='view';}
break;
case 'userTalk':
case 'talk':
this.article=this.article.talkPage();
delete this.oldid;
this.print=wikiLink;
this.action='view'; break;
case 'arin':
this.print=arinLink; break;
case 'count':
this.print=editCounterLink; break;
case 'google':
this.print=googleLink; break;
case 'editors':
this.print=editorListLink; break;
case 'globalsearch':
this.print=globalSearchLink; break;
case 'lastEdit':
this.print=titledDiffLink;
this.title=popupString('Show the last edit');
this.from='prev'; this.to='cur'; break;
case 'oldEdit':
this.print=titledDiffLink;
this.title=popupString('Show the edit made to get revision') + ' ' + this.oldid;
this.from='prev'; this.to=this.oldid; break;
case 'editOld':
this.print=wikiLink; this.action='edit'; break;
case 'undo':
this.print=wikiLink; this.action='edit&undo='; break;
case 'revert':
this.print=wikiLink; this.action='revert'; break;
case 'nullEdit':
this.print=wikiLink; this.action='nullEdit'; break;
case 'diffCur':
this.print=titledDiffLink;
this.title=tprintf('Show changes since revision %s', [this.oldid]);
this.from=this.oldid; this.to='cur'; break;
case 'editUserTalk':
case 'editTalk':
delete this.oldid;
this.article=this.article.talkPage();
this.action='edit'; this.print=wikiLink; break;
case 'newUserTalk':
case 'newTalk':
this.article=this.article.talkPage();
this.action='edit§ion=new'; this.print=wikiLink; break;
case 'lastContrib':
case 'sinceMe':
this.print=magicHistoryLink;
break;
case 'togglePreviews':
this.text=popupString(pg.option.simplePopups ? 'enable previews' : 'disable previews');
/* fall through */
case 'disablePopups': case 'purgePopups':
this.print=popupMenuLink;
break;
default:
this.print=function () {return 'Unknown navlink type: '+this.id+'';};
}
};
//
// end navlinks
//////////////////////////////////////////////////
//</NOLITE>
// ENDFILE: navlinks.js
// STARTFILE: shortcutkeys.js
//<NOLITE>
function popupHandleKeypress(evt) {
var keyCode = window.event ? window.event.keyCode : ( evt.keyCode ? evt.keyCode : evt.which);
if (!keyCode || !pg.current.link || !pg.current.link.navpopup) { return; }
if (keyCode==27) { // escape
killPopup();
return false; // swallow keypress
}
var letter=String.fromCharCode(keyCode);
var links=pg.current.link.navpopup.mainDiv.getElementsByTagName('A');
var startLink=0;
var i,j;
if (popupHandleKeypress.lastPopupLinkSelected) {
for (i=0; i<links.length; ++i) {
if (links[i]==popupHandleKeypress.lastPopupLinkSelected) { startLink=i; }
}
}
for (j=0; j<links.length; ++j) {
i=(startLink + j + 1) % links.length;
if (links[i].getAttribute('popupkey')==letter) {
if (evt && evt.preventDefault) evt.preventDefault();
links[i].focus();
popupHandleKeypress.lastPopupLinkSelected=links[i];
return false; // swallow keypress
}
}
// pass keypress on
if (document.oldPopupOnkeypress) { return document.oldPopupOnkeypress(evt); }
return true;
}
function addPopupShortcuts() {
if (document.onkeypress!=popupHandleKeypress) {
document.oldPopupOnkeypress=document.onkeypress;
}
document.onkeypress=popupHandleKeypress;
}
function rmPopupShortcuts() {
popupHandleKeypress.lastPopupLinkSelected=null;
try {
if (document.oldPopupOnkeypress && document.oldPopupOnkeypress==popupHandleKeypress) {
// panic
document.onkeypress=null; //function () {};
return;
}
document.onkeypress=document.oldPopupOnkeypress;
} catch (nasties) { /* IE goes here */ }
}
function addLinkProperty(html, property) {
// take "<a href=...>...</a> and add a property
// not sophisticated at all, easily broken
var i=html.indexOf('>');
if (i<0) { return html; }
return html.substring(0,i) + ' ' + property + html.substring(i);
}
function addPopupShortcut(html, key) {
if (!getValueOf('popupShortcutKeys')) { return html; }
var ret= addLinkProperty(html, 'popupkey="'+key+'"');
if (key==' ') { key=popupString('spacebar'); }
return ret.replace(RegExp('^(.*?)(title=")(.*?)(".*)$', 'i'),'$1$2$3 ['+key+']$4');
}
//</NOLITE>
// ENDFILE: shortcutkeys.js
// STARTFILE: diffpreview.js
//<NOLITE>
//lets jump through hoops to find the rev ids we need to retrieve
function loadDiff(article, oldid, diff, navpop) {
navpop.diffData={ oldRev: {}, newRev: {} };
mw.loader.using( 'mediawiki.api' ).then( function() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'compare',
prop: 'ids|title'
};
if(article.title){
params.fromtitle = article.title;
}
switch (diff) {
case 'cur':
switch ( oldid ) {
case null:
case '':
case 'prev':
// this can only work if we have the title
// cur -> prev
params.torelative = 'prev';
break;
default:
params.fromrev = oldid;
params.torelative = 'cur';
break;
}
break;
case 'prev':
if( oldid ) {
params.fromrev = oldid;
} else {
params.fromtitle;
}
params.torelative = 'prev';
break;
case 'next':
params.fromrev = oldid || 0;
params.torelative = 'next';
break;
default:
params.fromrev = oldid || 0;
params.torev = diff || 0;
break;
}
api.get( params ).then( function( data ) {
navpop.diffData.oldRev.revid = data.compare.fromrevid;
navpop.diffData.newRev.revid = data.compare.torevid;
addReviewLink(navpop, 'popupMiscTools');
var go = function() {
pendingNavpopTask(navpop);
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
url += 'revids=' + navpop.diffData.oldRev.revid + '|' + navpop.diffData.newRev.revid;
url += '&prop=revisions&rvprop=ids|timestamp|content';
getPageWithCaching(url, doneDiff, navpop);
return true; // remove hook once run
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_DIFFS'); }
} );
} );
}
// Put a "mark patrolled" link to an element target
// TODO: Allow patrol a revision, as well as a diff
function addReviewLink (navpop, target) {
if (! pg.user.canReview) return;
// If 'newRev' is older than 'oldRev' than it could be confusing, so we do not show the review link.
if (navpop.diffData.newRev.revid <= navpop.diffData.oldRev.revid) return;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
prop: 'info|flagged',
revids: navpop.diffData.oldRev.revid,
formatversion : 2
};
api.get (params).then(function(data){
var stable_revid = data.query.pages[0].flagged && data.query.pages[0].flagged.stable_revid || 0;
// The diff can be reviewed if the old version is the last reviewed version
// TODO: Other possible conditions that we may want to implement instead of this one:
// * old version is patrolled and the new version is not patrolled
// * old version is patrolled and the new version is more recent than the last reviewed version
if (stable_revid == navpop.diffData.oldRev.revid) {
var a = document.createElement('a');
a.innerHTML = popupString('mark patrolled');
a.title=popupString('markpatrolledHint');
a.onclick = function() {
var params = {
action: 'review',
revid: navpop.diffData.newRev.revid,
comment: tprintf('defaultpopupReviewedSummary', [navpop.diffData.oldRev.revid, navpop.diffData.newRev.revid])
};
api.postWithToken('csrf',params).done(function(){
a.style.display = "none";
// TODO: Update current page and other already constructed popups
} ).fail(function(){
alert(popupString('Could not marked this edit as patrolled'));
});
};
setPopupHTML(a, target, navpop.idNumber,null,true);
}
});
}
function doneDiff(download) {
if (!download.owner || !download.owner.diffData) { return; }
var navpop=download.owner;
completedNavpopTask(navpop);
var pages, revisions=[];
try{
// Process the downloads
pages = getJsObj(download.data).query.pages;
for(var i=0; i < pages.length; i++ ) {
revisions = revisions.concat(pages[i].revisions);
}
for(i=0; i< revisions.length; i++){
if(revisions[i].revid == navpop.diffData.oldRev.revid) {
navpop.diffData.oldRev.revision = revisions[i];
} else if (revisions[i].revid == navpop.diffData.newRev.revid) {
navpop.diffData.newRev.revision = revisions[i];
}
}
} catch(someError) {
errlog( 'Could not get diff' );
}
insertDiff(navpop);
}
function rmBoringLines(a,b,context) {
if (typeof context == 'undefined') { context=2; }
// this is fairly slow... i think it's quicker than doing a word-based diff from the off, though
var aa=[], aaa=[];
var bb=[], bbb=[];
var i, j;
// first, gather all disconnected nodes in a and all crossing nodes in a and b
for (i=0; i<a.length; ++i ) {
if(!a[i].paired) { aa[i]=1; }
else if (countCrossings(b,a,i, true)) {
aa[i]=1;
bb[ a[i].row ] = 1;
}
}
// pick up remaining disconnected nodes in b
for (i=0; i<b.length; ++i ) {
if (bb[i]==1) { continue; }
if(!b[i].paired) { bb[i]=1; }
}
// another pass to gather context: we want the neighbours of included nodes which are not yet included
// we have to add in partners of these nodes, but we don't want to add context for *those* nodes in the next pass
for (i=0; i<b.length; ++i) {
if ( bb[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(b.length, i+context); ++j) {
if ( !bb[j] ) { bb[j] = 1; aa[ b[j].row ] = 0.5; }
}
}
}
for (i=0; i<a.length; ++i) {
if ( aa[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(a.length, i+context); ++j) {
if ( !aa[j] ) { aa[j] = 1; bb[ a[j].row ] = 0.5; }
}
}
}
for (i=0; i<bb.length; ++i) {
if (bb[i] > 0) { // it's a row we need
if (b[i].paired) { bbb.push(b[i].text); } // joined; partner should be in aa
else {
bbb.push(b[i]);
}
}
}
for (i=0; i<aa.length; ++i) {
if (aa[i] > 0) { // it's a row we need
if (a[i].paired) { aaa.push(a[i].text); } // joined; partner should be in aa
else {
aaa.push(a[i]);
}
}
}
return { a: aaa, b: bbb};
}
function stripOuterCommonLines(a,b,context) {
var i=0;
while (i<a.length && i < b.length && a[i]==b[i]) { ++i; }
var j=a.length-1; var k=b.length-1;
while ( j>=0 && k>=0 && a[j]==b[k] ) { --j; --k; }
return { a: a.slice(Math.max(0,i - 1 - context), Math.min(a.length+1, j + context+1)),
b: b.slice(Math.max(0,i - 1 - context), Math.min(b.length+1, k + context+1)) };
}
function insertDiff(navpop) {
// for speed reasons, we first do a line-based diff, discard stuff that seems boring, then do a word-based diff
// FIXME: sometimes this gives misleading diffs as distant chunks are squashed together
var oldlines = navpop.diffData.oldRev.revision.content.split('\n');
var newlines = navpop.diffData.newRev.revision.content.split('\n');
var inner=stripOuterCommonLines(oldlines,newlines,getValueOf('popupDiffContextLines'));
oldlines=inner.a; newlines=inner.b;
var truncated=false;
getValueOf('popupDiffMaxLines');
if (oldlines.length > pg.option.popupDiffMaxLines || newlines.length > pg.option.popupDiffMaxLines) {
// truncate
truncated=true;
inner=stripOuterCommonLines(oldlines.slice(0,pg.option.popupDiffMaxLines),
newlines.slice(0,pg.option.popupDiffMaxLines),
pg.option.popupDiffContextLines);
oldlines=inner.a; newlines=inner.b;
}
var lineDiff=diff(oldlines, newlines);
var lines2=rmBoringLines(lineDiff.o, lineDiff.n);
var oldlines2=lines2.a; var newlines2=lines2.b;
var simpleSplit = !String.prototype.parenSplit.isNative;
var html='<hr />';
if (getValueOf('popupDiffDates')) {
html += diffDatesTable(navpop);
html += '<hr />';
}
html += shortenDiffString(
diffString(oldlines2.join('\n'), newlines2.join('\n'), simpleSplit),
getValueOf('popupDiffContextCharacters') ).join('<hr />');
setPopupTipsAndHTML(html.split('\n').join('<br>') +
(truncated ? '<hr /><b>'+popupString('Diff truncated for performance reasons')+'</b>' : '') ,
'popupPreview', navpop.idNumber);
}
function diffDatesTable( navpop ) {
var html='<table class="popup_diff_dates">';
html += diffDatesTableRow( navpop.diffData.newRev.revision, tprintf('New revision'));
html += diffDatesTableRow( navpop.diffData.oldRev.revision, tprintf('Old revision'));
html += '</table>';
return html;
}
function diffDatesTableRow( revision, label ) {
var txt='';
var lastModifiedDate = new Date(revision.timestamp);
var datePrint=getValueOf('popupDiffDatePrinter');
if (typeof lastModifiedDate[datePrint] == 'function') {
var d2 = adjustDate(lastModifiedDate, getTimeOffset());
txt = dayFormat(d2, true) + ' ' + timeFormat(d2, true);
} else {
txt = tprintf('Invalid %s %s', ['popupDiffDatePrinter', datePrint]);
}
var revlink = generalLink({url: mw.config.get('wgScript') + '?oldid='+revision.revid,
text: label, title: label});
return simplePrintf('<tr><td>%s</td><td>%s</td></tr>', [ revlink, txt ]);
}
//</NOLITE>
// ENDFILE: diffpreview.js
// STARTFILE: links.js
//<NOLITE>
/////////////////////
// LINK GENERATION //
/////////////////////
// titledDiffLink --> titledWikiLink --> generalLink
// wikiLink --> titledWikiLink --> generalLink
// editCounterLink --> generalLink
// TODO Make these functions return Element objects, not just raw HTML strings.
function titledDiffLink(l) { // article, text, title, from, to) {
return titledWikiLink({article: l.article, action: l.to + '&oldid=' + l.from,
newWin: l.newWin,
noPopup: l.noPopup,
text: l.text, title: l.title,
/* hack: no oldid here */
actionName: 'diff'});
}
function wikiLink(l) {
//{article:article, action:action, text:text, oldid, newid}) {
if (! (typeof l.article == typeof {} &&
typeof l.action == typeof '' &&
typeof l.text==typeof '')) return null;
if (typeof l.oldid == 'undefined') { l.oldid=null; }
var savedOldid = l.oldid;
if (!/^(edit|view|revert|render)$|^raw/.test(l.action)) { l.oldid=null; }
var hint=popupString(l.action + 'Hint'); // revertHint etc etc etc
var oldidData=[l.oldid, safeDecodeURI(l.article)];
var revisionString = tprintf('revision %s of %s', oldidData);
log('revisionString='+revisionString);
switch (l.action) {
case 'edit§ion=new': hint = popupString('newSectionHint'); break;
case 'edit&undo=':
if (l.diff && l.diff != 'prev' && savedOldid ) {
l.action += l.diff + '&undoafter=' + savedOldid;
} else if (savedOldid) {
l.action += savedOldid;
}
hint = popupString('undoHint');
break;
case 'raw&ctype=text/css': hint=popupString('rawHint'); break;
case 'revert':
var p=parseParams(pg.current.link.href);
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=' + revertSummary(l.oldid, p.diff);
if (p.diff=='prev') {
l.action += '&direction=prev';
revisionString = tprintf('the revision prior to revision %s of %s', oldidData);
}
if (getValueOf('popupRevertSummaryPrompt')) { l.action += '&autosummaryprompt=true'; }
if (getValueOf('popupMinorReverts')) { l.action += '&autominor=true'; }
log('revisionString is now '+revisionString);
break;
case 'nullEdit':
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=null';
break;
case 'historyfeed':
l.action='history&feed=rss';
break;
case 'markpatrolled':
l.action='markpatrolled&rcid='+l.rcid;
}
if (hint) {
if (l.oldid) {
hint = simplePrintf(hint, [revisionString]);
}
else {
hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
}
}
else {
hint = safeDecodeURI(l.article + '&action=' + l.action) + (l.oldid) ? '&oldid='+l.oldid : '';
}
return titledWikiLink({article: l.article, action: l.action, text: l.text, newWin:l.newWin,
title: hint, oldid: l.oldid, noPopup: l.noPopup, onclick: l.onclick});
}
function revertSummary(oldid, diff) {
var ret='';
if (diff == 'prev') {
ret=getValueOf('popupQueriedRevertToPreviousSummary');
} else { ret = getValueOf('popupQueriedRevertSummary'); }
return ret + '&autorv=' + oldid;
}
function titledWikiLink(l) {
// possible properties of argument:
// article, action, text, title, oldid, actionName, className, noPopup
// oldid = null is fine here
// article and action are mandatory args
if (typeof l.article == 'undefined' || typeof l.action=='undefined') {
errlog('got undefined article or action in titledWikiLink');
return null;
}
var base = pg.wiki.titlebase + l.article.urlString();
var url=base;
if (typeof l.actionName=='undefined' || !l.actionName) { l.actionName='action'; }
// no need to add &action=view, and this confuses anchors
if (l.action != 'view') { url = base + '&' + l.actionName + '=' + l.action; }
if (typeof l.oldid!='undefined' && l.oldid) { url+='&oldid='+l.oldid; }
var cssClass=pg.misc.defaultNavlinkClassname;
if (typeof l.className!='undefined' && l.className) { cssClass=l.className; }
return generalNavLink({url: url, newWin: l.newWin,
title: (typeof l.title != 'undefined') ? l.title : null,
text: (typeof l.text!='undefined')?l.text:null,
className: cssClass, noPopup:l.noPopup, onclick:l.onclick});
}
pg.fn.getLastContrib = function getLastContrib(wikipage, newWin) {
getHistoryInfo(wikipage, function(x) {
processLastContribInfo(x, {page: wikipage, newWin: newWin});
});
};
function processLastContribInfo(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: an odd thing happened. Please retry.'); return; }
if(!info.firstNewEditor) {
alert(tprintf('Only found one editor: %s made %s edits', [info.edits[0].editor,info.edits.length]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+info.firstNewEditor.oldid;
displayUrl(newUrl, stuff.newWin);
}
pg.fn.getDiffSinceMyEdit = function getDiffSinceMyEdit(wikipage, newWin) {
getHistoryInfo(wikipage, function(x){
processDiffSinceMyEdit(x, {page: wikipage, newWin: newWin});
});
};
function processDiffSinceMyEdit(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: something fishy happened. Please try again.'); return; }
var friendlyName=stuff.page.split('_').join(' ');
if(!info.myLastEdit) {
alert(tprintf('Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
[info.userName, getValueOf('popupHistoryLimit'), friendlyName]));
return;
}
if(info.myLastEdit.index === 0) {
alert(tprintf("%s seems to be the last editor to the page %s", [info.userName, friendlyName]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+ info.myLastEdit.oldid;
displayUrl(newUrl, stuff.newWin);
}
function displayUrl(url, newWin){
if(newWin) { window.open(url); }
else { document.location=url; }
}
pg.fn.purgePopups = function purgePopups() {
processAllPopups(true);
setupCache(); // deletes all cached items (not browser cached, though...)
pg.option={};
abortAllDownloads();
};
function processAllPopups(nullify, banish) {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
if (!pg.current.links[i].navpopup) { continue; }
if (nullify || banish) pg.current.links[i].navpopup.banish();
pg.current.links[i].simpleNoMore=false;
if (nullify) pg.current.links[i].navpopup=null;
}
}
pg.fn.disablePopups = function disablePopups(){
processAllPopups(false, true);
setupTooltips(null, true);
};
pg.fn.togglePreviews = function togglePreviews() {
processAllPopups(true, true);
pg.option.simplePopups=!pg.option.simplePopups;
abortAllDownloads();
};
function magicWatchLink(l) {
//Yuck!! Would require a thorough redesign to add this as a click event though ...
l.onclick = simplePrintf( 'pg.fn.modifyWatchlist(\'%s\',\'%s\');return false;', [l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), this.id] );
return wikiLink(l);
}
pg.fn.modifyWatchlist = function modifyWatchlist(title, action) {
var reqData = {
'action': 'watch',
'formatversion': 2,
'titles': title,
'uselang': mw.config.get('wgUserLanguage')
};
if ( action === 'unwatch' ) reqData.unwatch = true;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
// Load the Addedwatchtext or Removedwatchtext message and show it
var mwTitle = mw.Title.newFromText( title );
var messageName;
if ( mwTitle && mwTitle.getNamespaceId() > 0 && mwTitle.getNamespaceId() % 2 === 1 ) {
messageName = action === 'watch' ? 'addedwatchtext-talk' : 'removedwatchtext-talk';
} else {
messageName = action === 'watch' ? 'addedwatchtext' : 'removedwatchtext';
}
$.when(
api.postWithToken( 'watch', reqData ),
mw.loader.using( [ 'mediawiki.api', 'mediawiki.jqueryMsg' ] ).then( function () {
return api.loadMessagesIfMissing( [ messageName ] );
} )
).done( function () {
mw.notify( mw.message( messageName, title ).parseDom() );
} );
};
function magicHistoryLink(l) {
// FIXME use onclick change href trick to sort this out instead of window.open
var jsUrl='', title='', onClick='';
switch(l.id) {
case 'lastContrib':
onClick=simplePrintf('pg.fn.getLastContrib(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('lastContribHint');
break;
case 'sinceMe':
onClick=simplePrintf('pg.fn.getDiffSinceMyEdit(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('sinceMeHint');
break;
}
jsUrl = 'javascript:' + onClick; // jshint ignore:line
onClick += ';return false;';
return generalNavLink({url: jsUrl, newWin: false, // can't have new windows with JS links, I think
title: title, text: l.text, noPopup: l.noPopup, onclick: onClick });
}
function popupMenuLink(l) {
var jsUrl=simplePrintf('javascript:pg.fn.%s()', [l.id]); // jshint ignore:line
var title=popupString(simplePrintf('%sHint', [l.id]));
var onClick=simplePrintf('pg.fn.%s();return false;', [l.id]);
return generalNavLink({url: jsUrl, newWin:false, title:title, text:l.text, noPopup:l.noPopup, onclick: onClick});
}
function specialLink(l) {
// properties: article, specialpage, text, sep
if (typeof l.specialpage=='undefined'||!l.specialpage) return null;
var base = pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId]+':'+l.specialpage;
if (typeof l.sep == 'undefined' || l.sep === null) l.sep='&target=';
var article=l.article.urlString({keepSpaces: l.specialpage=='Search'});
var hint=popupString(l.specialpage+'Hint');
switch (l.specialpage) {
case 'Log':
switch (l.sep) {
case '&user=': hint=popupString('userLogHint'); break;
case '&type=block&page=': hint=popupString('blockLogHint'); break;
case '&page=': hint=popupString('pageLogHint'); break;
case '&type=protect&page=': hint=popupString('protectLogHint'); break;
case '&type=delete&page=': hint=popupString('deleteLogHint'); break;
default: log('Unknown log type, sep=' + l.sep); hint='Missing hint (FIXME)';
}
break;
case 'PrefixIndex': article += '/'; break;
}
if (hint) hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
else hint = safeDecodeURI(l.specialpage+':'+l.article) ;
var url = base + l.sep + article;
return generalNavLink({url: url, title: hint, text: l.text, newWin:l.newWin, noPopup:l.noPopup});
}
function generalLink(l) {
// l.url, l.text, l.title, l.newWin, l.className, l.noPopup, l.onclick
if (typeof l.url=='undefined') return null;
// only quotation marks in the url can screw us up now... I think
var url=l.url.split('"').join('%22');
var ret='<a href="' + url + '"';
if (typeof l.title!='undefined' && l.title) { ret += ' title="' + pg.escapeQuotesHTML(l.title) + '"'; }
if (typeof l.onclick!='undefined' && l.onclick) { ret += ' onclick="' + pg.escapeQuotesHTML(l.onclick) + '"'; }
if (l.noPopup) { ret += ' noPopup=1'; }
var newWin;
if (typeof l.newWin=='undefined' || l.newWin === null) { newWin=getValueOf('popupNewWindows'); }
else { newWin=l.newWin; }
if (newWin) { ret += ' target="_blank"'; }
if (typeof l.className!='undefined'&&l.className) { ret+=' class="'+l.className+'"'; }
ret += '>';
if (typeof l.text==typeof '') { ret+= l.text; }
ret +='</a>';
return ret;
}
function appendParamsToLink(linkstr, params) {
var sp=linkstr.parenSplit(RegExp('(href="[^"]+?)"', 'i'));
if (sp.length<2) return null;
var ret=sp.shift() + sp.shift();
ret += '&' + params + '"';
ret += sp.join('');
return ret;
}
function changeLinkTargetLink(x) { // newTarget, text, hint, summary, clickButton, minor, title (optional), alsoChangeLabel {
if (x.newTarget) {
log ('changeLinkTargetLink: newTarget=' + x.newTarget);
}
if (x.oldTarget !== decodeURIComponent( x.oldTarget ) ) {
log ('This might be an input problem: ' + x.oldTarget );
}
// FIXME: first character of page title as well as namespace should be case insensitive
// eg [[category:X1]] and [[Category:X1]] are equivalent
// this'll break if charAt(0) is nasty
var cA = mw.util.escapeRegExp(x.oldTarget);
var chs = cA.charAt(0).toUpperCase();
chs='['+chs + chs.toLowerCase()+']';
var currentArticleRegexBit=chs+cA.substring(1);
currentArticleRegexBit=currentArticleRegexBit
.split(RegExp('(?:[_ ]+|%20)', 'g')).join('(?:[_ ]+|%20)')
.split('\\(').join('(?:%28|\\()')
.split('\\)').join('(?:%29|\\))'); // why does this need to match encoded strings ? links in the document ?
// leading and trailing space should be ignored, and anchor bits optional:
currentArticleRegexBit = '\\s*(' + currentArticleRegexBit + '(?:#[^\\[\\|]*)?)\\s*';
// e.g. Computer (archaic) -> \s*([Cc]omputer[_ ](?:%2528|\()archaic(?:%2528|\)))\s*
// autoedit=s~\[\[([Cc]ad)\]\]~[[Computer-aided%20design|$1]]~g;s~\[\[([Cc]AD)[|]~[[Computer-aided%20design|~g
var title=x.title || mw.config.get('wgPageName').split('_').join(' ');
var lk=titledWikiLink({article: new Title(title), newWin:x.newWin,
action: 'edit',
text: x.text,
title: x.hint,
className: 'popup_change_title_link'
});
var cmd='';
if (x.newTarget) {
// escape '&' and other nasties
var t = x.newTarget;
var s = mw.util.escapeRegExp(x.newTarget);
if (x.alsoChangeLabel) {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + ']]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
} else {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + '|$1]]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
}
} else {
cmd += 's~\\[\\['+currentArticleRegexBit+'\\]\\]~$1~g;';
cmd += 's~\\[\\['+currentArticleRegexBit+'[|](.*?)\\]\\]~$2~g';
}
// Build query
cmd = 'autoedit=' + encodeURIComponent ( cmd );
cmd += '&autoclick='+ encodeURIComponent( x.clickButton ) + '&actoken=' + encodeURIComponent( autoClickToken() );
cmd += ( x.minor === null ) ? '' : '&autominor='+ encodeURIComponent( x.minor );
cmd += ( x.watch === null ) ? '' : '&autowatch='+ encodeURIComponent( x.watch );
cmd += '&autosummary='+encodeURIComponent(x.summary);
cmd += '&autoimpl='+encodeURIComponent( popupString('autoedit_version') );
return appendParamsToLink(lk, cmd);
}
function redirLink(redirMatch, article) {
// NB redirMatch is in wikiText
var ret='';
if (getValueOf('popupAppendRedirNavLinks') && getValueOf('popupNavLinks')) {
ret += '<hr />';
if (getValueOf('popupFixRedirs') && typeof autoEdit != 'undefined' && autoEdit) {
ret += popupString('Redirects to: (Fix ');
log('redirLink: newTarget=' + redirMatch);
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target'),
hint: popupString('Fix this redirect, changing just the link target'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages')
}), 'R');
ret += popupString(' or ');
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target & label'),
hint: popupString('Fix this redirect, changing the link target and label'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages'),
alsoChangeLabel: true
}), 'R');
ret += popupString(')');
}
else ret += popupString('Redirects') + popupString(' to ');
return ret;
}
else return '<br> ' + popupString('Redirects') + popupString(' to ') +
titledWikiLink({article: new Title().fromWikiText(redirMatch), action: 'view', /* FIXME: newWin */
text: safeDecodeURI(redirMatch), title: popupString('Bypass redirect')});
}
function arinLink(l) {
if (!saneLinkCheck(l)) { return null; }
if ( ! l.article.isIpUser() || ! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
return generalNavLink({url:'http://ws.arin.net/cgi-bin/whois.pl?queryinput=' + encodeURIComponent(uN), newWin:l.newWin,
title: tprintf('Look up %s in ARIN whois database', [uN]),
text: l.text, noPopup:1});
}
function toolDbName(cookieStyle) {
var ret = mw.config.get('wgDBname');
if (!cookieStyle) { ret+= '_p'; }
return ret;
}
function saneLinkCheck(l) {
if (typeof l.article != typeof {} || typeof l.text != typeof '') { return false; }
return true;
}
function editCounterLink(l) {
if(!saneLinkCheck(l)) return null;
if (! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
var tool=getValueOf('popupEditCounterTool');
var url;
var defaultToolUrl='//tools.wmflabs.org/supercount/index.php?user=$1&project=$2.$3';
switch(tool) {
case 'custom':
url=simplePrintf(getValueOf('popupEditCounterUrl'), [ encodeURIComponent(uN), toolDbName() ]);
break;
case 'soxred': // no longer available
case 'kate': // no longer available
case 'interiot':// no longer available
/* fall through */
case 'supercount':
default:
var theWiki=pg.wiki.hostname.split('.');
url=simplePrintf(defaultToolUrl, [ encodeURIComponent(uN), theWiki[0], theWiki[1] ]);
}
return generalNavLink({url:url, title: tprintf('editCounterLinkHint', [uN]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function globalSearchLink(l) {
if(!saneLinkCheck(l)) return null;
var base='http://vs.aka-online.de/cgi-bin/globalwpsearch.pl?timeout=120&search=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + article, newWin:l.newWin,
title: tprintf('globalSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function googleLink(l) {
if(!saneLinkCheck(l)) return null;
var base='https://www.google.com/search?q=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + '%22' + article + '%22', newWin:l.newWin,
title: tprintf('googleSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function editorListLink(l) {
if(!saneLinkCheck(l)) return null;
var article= l.article.articleFromTalkPage() || l.article;
var url='https://xtools.wmflabs.org/articleinfo/' +
encodeURI( pg.wiki.hostname ) + '/' +
article.urlString() +
'?uselang=' + mw.config.get('wgUserLanguage');
return generalNavLink({url:url,
title: tprintf('editorListHint', [article]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function generalNavLink(l) {
l.className = (l.className === null) ? 'popupNavLink' : l.className;
return generalLink(l);
}
//////////////////////////////////////////////////
// magic history links
//
function getHistoryInfo(wikipage, whatNext) {
log('getHistoryInfo');
getHistory(wikipage, whatNext ? function(d){whatNext(processHistory(d));} : processHistory);
}
// FIXME eliminate pg.idNumber ... how? :-(
function getHistory(wikipage, onComplete) {
log('getHistory');
var url = pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&prop=revisions&titles=' +
new Title(wikipage).urlString() + '&rvlimit=' + getValueOf('popupHistoryLimit');
log('getHistory: url='+url);
return startDownload(url, pg.idNumber+'history', onComplete);
}
function processHistory(download) {
var jsobj = getJsObj(download.data);
try {
var revisions = anyChild(jsobj.query.pages).revisions;
var edits=[];
for (var i=0; i<revisions.length; ++i) {
edits.push({ oldid: revisions[i].revid, editor: revisions[i].user });
}
log('processed ' + edits.length + ' edits');
return finishProcessHistory( edits, mw.config.get('wgUserName') );
} catch (someError) {
log('Something went wrong with JSON business');
return finishProcessHistory([]);
}
}
function finishProcessHistory(edits, userName) {
var histInfo={};
histInfo.edits=edits;
histInfo.userName=userName;
for (var i=0; i<edits.length; ++i) {
if (typeof histInfo.myLastEdit === 'undefined' && userName && edits[i].editor==userName) {
histInfo.myLastEdit={index: i, oldid: edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
if (typeof histInfo.firstNewEditor === 'undefined' && edits[i].editor != edits[0].editor) {
histInfo.firstNewEditor={index:i, oldid:edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
}
//pg.misc.historyInfo=histInfo;
return histInfo;
}
//</NOLITE>
// ENDFILE: links.js
// STARTFILE: options.js
//////////////////////////////////////////////////
// options
// check for existing value, else use default
function defaultize(x) {
if (pg.option[x]===null || typeof pg.option[x]=='undefined') {
if (typeof window[x] != 'undefined' ) pg.option[x]=window[x];
else pg.option[x]=pg.optionDefault[x];
}
}
function newOption(x, def) {
pg.optionDefault[x]=def;
}
function setDefault(x, def) {
return newOption(x, def);
}
function getValueOf(varName) {
defaultize(varName);
return pg.option[varName];
}
/*eslint-disable */
function useDefaultOptions() { // for testing
for (var p in pg.optionDefault) {
pg.option[p]=pg.optionDefault[p];
if (typeof window[p]!='undefined') { delete window[p]; }
}
}
/*eslint-enable */
function setOptions() {
// user-settable parameters and defaults
var userIsSysop = false;
if ( mw.config.get('wgUserGroups') ) {
for ( var g = 0; g < mw.config.get('wgUserGroups').length; ++g ) {
if ( mw.config.get('wgUserGroups')[g] == "bảo quản viên" )
userIsSysop = true;
}
}
// Basic options
newOption('popupDelay', 0.5);
newOption('popupHideDelay', 0.5);
newOption('simplePopups', false);
newOption('popupStructure', 'shortmenus'); // see later - default for popupStructure is 'original' if simplePopups is true
newOption('popupActionsMenu', true);
newOption('popupSetupMenu', true);
newOption('popupAdminLinks', userIsSysop);
newOption('popupShortcutKeys', false);
newOption('popupHistoricalLinks', true);
newOption('popupOnlyArticleLinks', true);
newOption('removeTitles', true);
newOption('popupMaxWidth', 350);
newOption('popupSimplifyMainLink', true);
newOption('popupAppendRedirNavLinks', true);
newOption('popupTocLinks', false);
newOption('popupSubpopups', true);
newOption('popupDragHandle', false /* 'popupTopLinks'*/);
newOption('popupLazyPreviews', true);
newOption('popupLazyDownloads', true);
newOption('popupAllDabsStubs', false);
newOption('popupDebugging', false);
newOption('popupActiveNavlinks', true);
newOption('popupModifier', false); // ctrl, shift, alt or meta
newOption('popupModifierAction', 'enable'); // or 'disable'
newOption('popupDraggable', true);
newOption('popupReview', false);
//<NOLITE>
// images
newOption('popupImages', true);
newOption('imagePopupsForImages', true);
newOption('popupNeverGetThumbs', false);
//newOption('popupImagesToggleSize', true);
newOption('popupThumbAction', 'imagepage'); //'sizetoggle');
newOption('popupImageSize', 60);
newOption('popupImageSizeLarge', 200);
// redirs, dabs, reversion
newOption('popupFixRedirs', false);
newOption('popupRedirAutoClick', 'wpDiff');
newOption('popupFixDabs', false);
newOption('popupDabsAutoClick', 'wpDiff');
newOption('popupRevertSummaryPrompt', false);
newOption('popupMinorReverts', false);
newOption('popupRedlinkRemoval', false);
newOption('popupRedlinkAutoClick', 'wpDiff');
newOption('popupWatchDisambiggedPages', null);
newOption('popupWatchRedirredPages', null);
newOption('popupDabWiktionary', 'last');
// navlinks
newOption('popupNavLinks', true);
newOption('popupNavLinkSeparator', ' ⋅ ');
newOption('popupLastEditLink', true);
newOption('popupEditCounterTool', 'supercount');
newOption('popupEditCounterUrl', '');
//</NOLITE>
// previews etc
newOption('popupPreviews', true);
newOption('popupSummaryData', true);
newOption('popupMaxPreviewSentences', 9);
newOption('popupMaxPreviewCharacters', 900);
newOption('popupLastModified', true);
newOption('popupPreviewKillTemplates', true);
newOption('popupPreviewRawTemplates', true);
newOption('popupPreviewFirstParOnly', false);
newOption('popupPreviewCutHeadings', false);
newOption('popupPreviewButton', false);
newOption('popupPreviewButtonEvent', 'click');
//<NOLITE>
// diffs
newOption('popupPreviewDiffs', true);
newOption('popupDiffMaxLines', 100);
newOption('popupDiffContextLines', 2);
newOption('popupDiffContextCharacters', 40);
newOption('popupDiffDates', true);
newOption('popupDiffDatePrinter', 'toLocaleString');
// edit summaries. God, these are ugly.
newOption('popupReviewedSummary', popupString('defaultpopupReviewedSummary') );
newOption('popupFixDabsSummary', popupString('defaultpopupFixDabsSummary') );
newOption('popupExtendedRevertSummary', popupString('defaultpopupExtendedRevertSummary') );
newOption('popupRevertSummary', popupString('defaultpopupRevertSummary') );
newOption('popupRevertToPreviousSummary', popupString('defaultpopupRevertToPreviousSummary') );
newOption('popupQueriedRevertSummary', popupString('defaultpopupQueriedRevertSummary') );
newOption('popupQueriedRevertToPreviousSummary', popupString('defaultpopupQueriedRevertToPreviousSummary') );
newOption('popupFixRedirsSummary', popupString('defaultpopupFixRedirsSummary') );
newOption('popupRedlinkSummary', popupString('defaultpopupRedlinkSummary') );
newOption('popupRmDabLinkSummary', popupString('defaultpopupRmDabLinkSummary') );
//</NOLITE>
// misc
newOption('popupHistoryLimit', 50);
//<NOLITE>
newOption('popupFilters', [popupFilterStubDetect, popupFilterDisambigDetect,
popupFilterPageSize, popupFilterCountLinks,
popupFilterCountImages, popupFilterCountCategories,
popupFilterLastModified]);
newOption('extraPopupFilters', []);
newOption('popupOnEditSelection', 'cursor');
newOption('popupPreviewHistory', true);
newOption('popupImageLinks', true);
newOption('popupCategoryMembers', true);
newOption('popupUserInfo', true);
newOption('popupHistoryPreviewLimit', 25);
newOption('popupContribsPreviewLimit',25);
newOption('popupRevDelUrl', '//en.wikipedia.org/wiki/Wikipedia:Revision_deletion');
newOption('popupShowGender', true);
//</NOLITE>
// new windows
newOption('popupNewWindows', false);
newOption('popupLinksNewWindow', {'lastContrib': true, 'sinceMe': true});
// regexps
newOption('popupDabRegexp', '(\\{\\{\\s*disambig(?!uation needed)|disambig(uation|)\\s*\\}\\}|disamb\\s*\\}\\}|dab\\s*\\}\\})|\\{\\{\\s*(((geo|hn|road?|school|number)dis)|[234][lc][acw]|(road|ship)index)(\\s*[|][^}]*)?\\s*[}][}]|is a .*disambiguation.*page');
newOption('popupAnchorRegexp', 'anchors?'); //how to identify an anchors template
newOption('popupStubRegexp', '(sect)?stub[}][}]|This .*-related article is a .*stub');
newOption('popupImageVarsRegexp', 'image|image_(?:file|skyline|name|flag|seal)|cover|badge|logo');
}
// ENDFILE: options.js
// STARTFILE: strings.js
//<NOLITE>
//////////////////////////////////////////////////
// Translatable strings
//////////////////////////////////////////////////
//
// See instructions at
// https://en.wikipedia.org/wiki/Wikipedia:Tools/Navigation_popups/Translation
pg.string = {
/////////////////////////////////////
// summary data, searching etc.
/////////////////////////////////////
'article': 'article',
'category': 'category',
'categories': 'categories',
'image': 'image',
'images': 'images',
'stub': 'stub',
'section stub': 'section stub',
'Empty page': 'Empty page',
'kB': 'kB',
'bytes': 'bytes',
'day': 'day',
'days': 'days',
'hour': 'hour',
'hours': 'hours',
'minute': 'minute',
'minutes': 'minutes',
'second': 'second',
'seconds': 'seconds',
'week': 'week',
'weeks': 'weeks',
'search': 'search',
'SearchHint': 'Find English Wikipedia articles containing %s',
'web': 'web',
'global': 'global',
'globalSearchHint': 'Search across Wikipedias in different languages for %s',
'googleSearchHint': 'Google for %s',
/////////////////////////////////////
// article-related actions and info
// (some actions also apply to user pages)
/////////////////////////////////////
'actions': 'actions', ///// view articles and view talk
'popupsMenu': 'popups',
'togglePreviewsHint': 'Toggle preview generation in popups on this page',
'enable previews': 'enable previews',
'disable previews': 'disable previews',
'toggle previews': 'toggle previews',
'show preview': 'show preview',
'reset': 'reset',
'more...': 'more...',
'disable': 'disable popups',
'disablePopupsHint': 'Disable popups on this page. Reload page to re-enable.',
'historyfeedHint': 'RSS feed of recent changes to this page',
'purgePopupsHint': 'Reset popups, clearing all cached popup data.',
'PopupsHint': 'Reset popups, clearing all cached popup data.',
'spacebar': 'space',
'view': 'view',
'view article': 'view article',
'viewHint': 'Go to %s',
'talk': 'talk',
'talk page': 'talk page',
'this revision': 'this revision',
'revision %s of %s': 'revision %s of %s',
'Revision %s of %s': 'Revision %s of %s',
'the revision prior to revision %s of %s': 'the revision prior to revision %s of %s',
'Toggle image size': 'Click to toggle image size',
'del': 'del', ///// delete, protect, move
'delete': 'delete',
'deleteHint': 'Delete %s',
'undeleteShort': 'un',
'UndeleteHint': 'Show the deletion history for %s',
'protect': 'protect',
'protectHint': 'Restrict editing rights to %s',
'unprotectShort': 'un',
'unprotectHint': 'Allow %s to be edited by anyone again',
'send thanks': 'send thanks',
'ThanksHint': 'Send a thank you notification to this user',
'move': 'move',
'move page': 'move page',
'MovepageHint': 'Change the title of %s',
'edit': 'edit', ///// edit articles and talk
'edit article': 'edit article',
'editHint': 'Change the content of %s',
'edit talk': 'edit talk',
'new': 'new',
'new topic': 'new topic',
'newSectionHint': 'Start a new section on %s',
'null edit': 'null edit',
'nullEditHint': 'Submit an edit to %s, making no changes ',
'hist': 'hist', ///// history, diffs, editors, related
'history': 'history',
'historyHint': 'List the changes made to %s',
'last': 'prev', // For labelling the previous revision in history pages; the key is "last" for backwards compatibility
'lastEdit': 'lastEdit',
'mark patrolled': 'mark patrolled',
'markpatrolledHint': 'Mark this edit as patrolled',
'Could not marked this edit as patrolled': 'Could not marked this edit as patrolled',
'show last edit': 'most recent edit',
'Show the last edit': 'Show the effects of the most recent change',
'lastContrib': 'lastContrib',
'last set of edits': 'latest edits',
'lastContribHint': 'Show the net effect of changes made by the last editor',
'cur': 'cur',
'diffCur': 'diffCur',
'Show changes since revision %s': 'Show changes since revision %s',
'%s old': '%s old', // as in 4 weeks old
'oldEdit': 'oldEdit',
'purge': 'purge',
'purgeHint': 'Demand a fresh copy of %s',
'raw': 'source',
'rawHint': 'Download the source of %s',
'render': 'simple',
'renderHint': 'Show a plain HTML version of %s',
'Show the edit made to get revision': 'Show the edit made to get revision',
'sinceMe': 'sinceMe',
'changes since mine': 'diff my edit',
'sinceMeHint': 'Show changes since my last edit',
'Couldn\'t find an edit by %s\nin the last %s edits to\n%s': 'Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
'eds': 'eds',
'editors': 'editors',
'editorListHint': 'List the users who have edited %s',
'related': 'related',
'relatedChanges': 'relatedChanges',
'related changes': 'related changes',
'RecentchangeslinkedHint': 'Show changes in articles related to %s',
'editOld': 'editOld', ///// edit old version, or revert
'rv': 'rv',
'revert': 'revert',
'revertHint': 'Revert to %s',
'defaultpopupReviewedSummary': 'Accepted by reviewing the [[Special:diff/%s/%s|difference]] between this version and previously accepted version using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRedlinkSummary': 'Removing link to empty page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixDabsSummary': 'Disambiguate [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixRedirsSummary': 'Redirect bypass from [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupExtendedRevertSummary': 'Revert to revision dated %s by %s, oldid %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertToPreviousSummary': 'Revert to the revision prior to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertSummary': 'Revert to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertToPreviousSummary': 'Revert to the revision prior to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertSummary': 'Revert to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRmDabLinkSummary': 'Remove link to dab page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'Redirects': 'Redirects', // as in Redirects to ...
' to ': ' to ', // as in Redirects to ...
'Bypass redirect': 'Bypass redirect',
'Fix this redirect': 'Fix this redirect',
'disambig': 'disambig', ///// add or remove dab etc.
'disambigHint': 'Disambiguate this link to [[%s]]',
'Click to disambiguate this link to:': 'Click to disambiguate this link to:',
'remove this link': 'remove this link',
'remove all links to this page from this article': 'remove all links to this page from this article',
'remove all links to this disambig page from this article': 'remove all links to this disambig page from this article',
'mainlink': 'mainlink', ///// links, watch, unwatch
'wikiLink': 'wikiLink',
'wikiLinks': 'wikiLinks',
'links here': 'links here',
'whatLinksHere': 'whatLinksHere',
'what links here': 'what links here',
'WhatlinkshereHint': 'List the pages that are hyperlinked to %s',
'unwatchShort': 'un',
'watchThingy': 'watch', // called watchThingy because {}.watch is a function
'watchHint': 'Add %s to my watchlist',
'unwatchHint': 'Remove %s from my watchlist',
'Only found one editor: %s made %s edits': 'Only found one editor: %s made %s edits',
'%s seems to be the last editor to the page %s': '%s seems to be the last editor to the page %s',
'rss': 'rss',
/////////////////////////////////////
// diff previews
/////////////////////////////////////
'Diff truncated for performance reasons': 'Diff truncated for performance reasons',
'Old revision': 'Old revision',
'New revision': 'New revision',
'Something went wrong :-(': 'Something went wrong :-(',
'Empty revision, maybe non-existent': 'Empty revision, maybe non-existent',
'Unknown date': 'Unknown date',
/////////////////////////////////////
// other special previews
/////////////////////////////////////
'Empty category': 'Empty category',
'Category members (%s shown)': 'Category members (%s shown)',
'No image links found': 'No image links found',
'File links': 'File links',
'No image found': 'No image found',
'Image from Commons': 'Image from Commons',
'Description page': 'Description page',
'Alt text:': 'Alt text:',
'revdel':'Hidden revision',
/////////////////////////////////////
// user-related actions and info
/////////////////////////////////////
'user': 'user', ///// user page, talk, email, space
'user page': 'user page',
'user talk': 'user talk',
'edit user talk': 'edit user talk',
'leave comment': 'leave comment',
'email': 'email',
'email user': 'email user',
'EmailuserHint': 'Send an email to %s',
'space': 'space', // short form for userSpace link
'PrefixIndexHint': 'Show pages in the userspace of %s',
'count': 'count', ///// contributions, log
'edit counter': 'edit counter',
'editCounterLinkHint': 'Count the contributions made by %s',
'contribs': 'contribs',
'contributions': 'contributions',
'deletedContribs': 'deleted contributions',
'DeletedcontributionsHint': 'List deleted edits made by %s',
'ContributionsHint': 'List the contributions made by %s',
'log': 'log',
'user log': 'user log',
'userLogHint': 'Show %s\'s user log',
'arin': 'ARIN lookup', ///// ARIN lookup, block user or IP
'Look up %s in ARIN whois database': 'Look up %s in the ARIN whois database',
'unblockShort': 'un',
'block': 'block',
'block user': 'block user',
'IpblocklistHint': 'Unblock %s',
'BlockipHint': 'Prevent %s from editing',
'block log': 'block log',
'blockLogHint': 'Show the block log for %s',
'protectLogHint': 'Show the protection log for %s',
'pageLogHint': 'Show the page log for %s',
'deleteLogHint': 'Show the deletion log for %s',
'Invalid %s %s': 'The option %s is invalid: %s',
'No backlinks found': 'No backlinks found',
' and more': ' and more',
'undo': 'undo',
'undoHint': 'undo this edit',
'Download preview data': 'Download preview data',
'Invalid or IP user': 'Invalid or IP user',
'Not a registered username': 'Not a registered username',
'BLOCKED': 'BLOCKED',
'Has blocks': 'Has blocks',
' edits since: ': ' edits since: ',
'last edit on ': 'last edit on ',
/////////////////////////////////////
// Autoediting
/////////////////////////////////////
'Enter a non-empty edit summary or press cancel to abort': 'Enter a non-empty edit summary or press cancel to abort',
'Failed to get revision information, please edit manually.\n\n': 'Failed to get revision information, please edit manually.\n\n',
'The %s button has been automatically clicked. Please wait for the next page to load.': 'The %s button has been automatically clicked. Please wait for the next page to load.',
'Could not find button %s. Please check the settings in your javascript file.': 'Could not find button %s. Please check the settings in your javascript file.',
/////////////////////////////////////
// Popups setup
/////////////////////////////////////
'Open full-size image': 'Open full-size image',
'zxy': 'zxy',
'autoedit_version': 'np20140416'
};
function popupString(str) {
if (typeof popupStrings != 'undefined' && popupStrings && popupStrings[str]) { return popupStrings[str]; }
if (pg.string[str]) { return pg.string[str]; }
return str;
}
function tprintf(str,subs) {
if (typeof subs != typeof []) { subs = [subs]; }
return simplePrintf(popupString(str), subs);
}
//</NOLITE>
// ENDFILE: strings.js
// STARTFILE: run.js
////////////////////////////////////////////////////////////////////
// Run things
////////////////////////////////////////////////////////////////////
// For some reason popups requires a fully loaded page jQuery.ready(...) causes problems for some.
// The old addOnloadHook did something similar to the below
if (document.readyState=="complete")
autoEdit(); //will setup popups
else
$( window ).on( 'load', autoEdit );
// Support for MediaWiki's live preview, VisualEditor's saves and Echo's flyout.
( function () {
var once = true;
function dynamicContentHandler( $content ) {
// Try to detect the hook fired on initial page load and disregard
// it, we already hook to onload (possibly to different parts of
// page - it's configurable) and running twice might be bad. Ugly…
if ( $content.attr( 'id' ) == 'mw-content-text' ) {
if ( once ) {
once = false;
return;
}
}
function registerHooksForVisibleNavpops () {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
var navpop = pg.current.links[i].navpopup;
if (!navpop || !navpop.isVisible()) { continue; }
Navpopup.tracker.addHook(posCheckerHook(navpop));
}
}
function doIt () {
registerHooksForVisibleNavpops();
$content.each( function () {
this.ranSetupTooltipsAlready = false;
setupTooltips( this );
} );
}
setupPopups( doIt );
}
// This hook is also fired after page load.
mw.hook( 'wikipage.content' ).add( dynamicContentHandler );
mw.hook( 'ext.echo.overlay.beforeShowingOverlay' ).add( function($overlay){
dynamicContentHandler( $overlay.find(".mw-echo-state") );
});
} )();
// Forked from User:Connel MacKenzie/mess-with-popups.js
Previewmaker.prototype.firstBit = function() {
try{
if (mw.config.get("wgNamespaceNumber") !== 0){
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d = this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data = this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data = this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data = this.data.replace(RegExp('^[\\s\\n]*'), '');
stuff = (RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) {
d = stuff[0];
}
if (!getValueOf('popupPreviewFirstParOnly')) {
d = this.data;
}
/// now put \n\n after sections so that bullets and numbered lists work
d = d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["' + "'" + ']*\\s)', 'g'));
// leading space is bad, mmkay?
d[0] = d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds = RegExp('([^.][a-z][.][a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
var maxChars=getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd;
do {
dd = this.firstSentences(d, n);
--n;
}
while (dd.length > maxChars && n > 0);
this.data = dd;
} else {
//TODO: Limit this override to urls that start with http://*.wiktionary.org/
//TODO: extend the above section to also preview Wikipedia links
var charsWikt = 0;
var linesWikt = this.data.split('\n');
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[in]transitive}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[un]countable}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{cattag\|(.*?)}}/g, "");
linesWikt[jWikt] = linesWikt[jWikt].replace(/[\{\}]/g, "'");
if (linesWikt[jWikt].search(/^[#]/) == -1) {
if (linesWikt[jWikt].search(/^==[A-Z]/) == -1) {
linesWikt[jWikt] = '';
}
}
charsWikt += linesWikt[jWikt].length;
}
if (charsWikt < 2) {
linesWikt[0] = "No properly formatted content";
}
var maxChars = getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd = '';
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
if (linesWikt[jWikt] !== '') {
dd += linesWikt[jWikt] + '\n';
}
if (dd.length < maxChars) {
this.data = dd;
}
}
}
}
catch (e) {
console.log("error from mess with popups: ", e);
}
};
});
cjzg9n7b3b5x4fcokxhd6enhhmtvy05
2513949
2513948
2026-08-14T04:47:22Z
Hiyuune
50834
2513949
javascript
text/javascript
// {{documentation}}
// _________________________________________________________________________________________
// | |
// | === WARNING: GLOBAL GADGET FILE === |
// | Changes to this page affect many users. |
// | Please discuss changes on the [[Wiktionary:Grease Pit]] before editing. |
// |_________________________________________________________________________________________|
mw.loader.load('//en.wikipedia.org/w/index.php?action=raw&title=MediaWiki:Gadget-navpop.css&ctype=text/css', "text/css");
// Forked from [[:w:MediaWiki:Gadget-popups.js]]
$(function () {
//////////////////////////////////////////////////
// Globals
//
// Trying to shove as many of these as possible into the pg (popup globals) object
var pg = {
re: {}, // regexps
ns: {}, // namespaces
string: {}, // translatable strings
wiki: {}, // local site info
user: {}, // current user info
misc: {}, // YUCK PHOOEY
option: {}, // options, see newOption etc
optionDefault: {}, // default option values
flag: {}, // misc flags
cache: {}, // page and image cache
structures: {}, // navlink structures
timer: {}, // all sorts of timers (too damn many)
counter: {}, // .. and all sorts of counters
current: {}, // state info
fn: {}, // functions
endoflist: null
};
/* Bail if the gadget/script is being loaded twice */
if( window.pg ) {
return;
}
/* Export to global context */
window.pg = pg;
/// Local Variables: ///
/// mode:c ///
/// End: ///
// ENDFILE: main.js
// STARTFILE: actions.js
function setupTooltips(container, remove, force, popData) {
log('setupTooltips, container='+container+', remove='+remove);
if (!container) {
//<NOLITE>
// the main initial call
if (getValueOf('popupOnEditSelection') && document && document.editform && document.editform.wpTextbox1) {
document.editform.wpTextbox1.onmouseup=doSelectionPopup;
}
//</NOLITE>
// article/content is a structure-dependent thing
container = defaultPopupsContainer();
}
if (!remove && !force && container.ranSetupTooltipsAlready) { return; }
container.ranSetupTooltipsAlready = !remove;
var anchors;
anchors=container.getElementsByTagName('A');
setupTooltipsLoop(anchors, 0, 250, 100, remove, popData);
}
function defaultPopupsContainer() {
if (getValueOf('popupOnlyArticleLinks')) {
return document.getElementById('mw_content') ||
document.getElementById('content') ||
document.getElementById('article') || document;
}
return document;
}
function setupTooltipsLoop(anchors,begin,howmany,sleep, remove, popData) {
log(simplePrintf('setupTooltipsLoop(%s,%s,%s,%s,%s)', arguments));
var finish=begin+howmany;
var loopend = Math.min(finish, anchors.length);
var j=loopend - begin;
log ('setupTooltips: anchors.length=' + anchors.length + ', begin=' + begin +
', howmany=' + howmany + ', loopend=' + loopend + ', remove=' + remove);
var doTooltip= remove ? removeTooltip : addTooltip;
// try a faster (?) loop construct
if (j > 0) {
do {
var a=anchors[loopend - j];
if (typeof a==='undefined' || !a || !a.href) {
log('got null anchor at index ' + loopend - j);
continue;
}
doTooltip(a, popData);
} while (--j);
}
if (finish < anchors.length) {
setTimeout(function() {
setupTooltipsLoop(anchors,finish,howmany,sleep,remove,popData);},
sleep);
} else {
if ( !remove && ! getValueOf('popupTocLinks')) { rmTocTooltips(); }
pg.flag.finishedLoading=true;
}
}
// eliminate popups from the TOC
// This also kills any onclick stuff that used to be going on in the toc
function rmTocTooltips() {
var toc=document.getElementById('toc');
if (toc) {
var tocLinks=toc.getElementsByTagName('A');
var tocLen = tocLinks.length;
for (var j=0; j<tocLen; ++j) {
removeTooltip(tocLinks[j], true);
}
}
}
function addTooltip(a, popData) {
if ( !isPopupLink(a) ) { return; }
a.onmouseover=mouseOverWikiLink;
a.onmouseout= mouseOutWikiLink;
a.onmousedown = killPopup;
a.hasPopup = true;
a.popData = popData;
}
function removeTooltip(a) {
if ( !a.hasPopup ) { return; }
a.onmouseover = null;
a.onmouseout = null;
if (a.originalTitle) { a.title = a.originalTitle; }
a.hasPopup=false;
}
function removeTitle(a) {
if (!a.originalTitle) {
a.originalTitle=a.title;
}
a.title='';
}
function restoreTitle(a) {
if ( a.title || !a.originalTitle ) { return; }
a.title = a.originalTitle;
}
function registerHooks(np) {
var popupMaxWidth=getValueOf('popupMaxWidth');
if (typeof popupMaxWidth === 'number') {
var setMaxWidth = function () {
np.mainDiv.style.maxWidth = popupMaxWidth + 'px';
np.maxWidth = popupMaxWidth;
};
np.addHook(setMaxWidth, 'unhide', 'before');
}
//<NOLITE>
np.addHook(addPopupShortcuts, 'unhide', 'after');
np.addHook(rmPopupShortcuts, 'hide', 'before');
//</NOLITE>
}
function removeModifierKeyHandler(a) {
//remove listeners for modifier key if any that were added in mouseOverWikiLink
document.removeEventListener('keydown', a.modifierKeyHandler, false);
document.removeEventListener('keyup', a.modifierKeyHandler, false);
}
function mouseOverWikiLink(evt) {
if (!evt && window.event) {evt=window.event;}
// if the modifier is needed, listen for it,
// we will remove the listener when we mouseout of this link or kill popup.
if (getValueOf('popupModifier')) {
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
var key = action=='disable' ? 'keyup' : 'keydown';
var a = this;
a.modifierKeyHandler = function(evt) {
mouseOverWikiLink2(a, evt);
};
document.addEventListener(key, a.modifierKeyHandler, false);
}
return mouseOverWikiLink2(this, evt);
}
/**
* Gets the references list item that the provided footnote link targets. This
* is typically a li element within the ol.references element inside the reflist.
* @param {Element} a - A footnote link.
* @returns {Element|boolean} The targeted element, or false if one can't be found.
*/
function footnoteTarget(a) {
var aTitle=Title.fromAnchor(a);
// We want ".3A" rather than "%3A" or "?" here, so use the anchor property directly
var anch = aTitle.anchor;
if ( ! /^(cite_note-|_note-|endnote)/.test(anch) ) { return false; }
var lTitle=Title.fromURL(location.href);
if ( lTitle.toString(true) !== aTitle.toString(true) ) { return false; }
var el=document.getElementById(anch);
while ( el && typeof el.nodeName === 'string') {
var nt = el.nodeName.toLowerCase();
if ( nt === 'li' ) { return el; }
else if ( nt === 'body' ) { return false; }
else if ( el.parentNode ) { el=el.parentNode; }
else { return false; }
}
return false;
}
function footnotePreview(x, navpop) {
setPopupHTML('<hr />' + x.innerHTML, 'popupPreview', navpop.idNumber);
}
function modifierPressed(evt) {
var mod=getValueOf('popupModifier');
if (!mod) { return false; }
if (!evt && window.event) {evt=window.event;}
return ( evt && mod && evt[mod.toLowerCase() + 'Key'] );
}
// Checks if the correct modifier pressed/unpressed if needed
function isCorrectModifier(a,evt) {
if (!getValueOf('popupModifier')) { return true; }
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
return ( action == 'enable' && modifierPressed(evt) ||
action == 'disable' && !modifierPressed(evt) );
}
function mouseOverWikiLink2(a, evt) {
if (!isCorrectModifier(a,evt)) { return; }
if ( getValueOf('removeTitles') ) { removeTitle(a); }
if ( a==pg.current.link && a.navpopup && a.navpopup.isVisible() ) { return; }
pg.current.link=a;
if (getValueOf('simplePopups') && !pg.option.popupStructure) {
// reset *default value* of popupStructure
setDefault('popupStructure', 'original');
}
var article=(new Title()).fromAnchor(a);
// set global variable (ugh) to hold article (wikipage)
pg.current.article = article;
if (!a.navpopup) {
a.navpopup=newNavpopup(a, article);
pg.current.linksHash[a.href] = a.navpopup;
pg.current.links.push(a);
}
if (a.navpopup.pending === null || a.navpopup.pending !== 0) {
// either fresh popups or those with unfinshed business are redone from scratch
simplePopupContent(a, article);
}
a.navpopup.showSoonIfStable(a.navpopup.delay);
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=setInterval(checkPopupPosition, 600);
if(getValueOf('simplePopups')) {
if (getValueOf('popupPreviewButton') && !a.simpleNoMore) {
var d=document.createElement('div');
d.className='popupPreviewButtonDiv';
var s=document.createElement('span');
d.appendChild(s);
s.className='popupPreviewButton';
s['on' + getValueOf('popupPreviewButtonEvent')] = function() {
a.simpleNoMore=true;
d.style.display = "none";
nonsimplePopupContent(a,article);
};
s.innerHTML=popupString('show preview');
setPopupHTML(d, 'popupPreview', a.navpopup.idNumber);
}
}
if (a.navpopup.pending !== 0 ) {
nonsimplePopupContent(a, article);
}
}
// simplePopupContent: the content that do not require additional download
// (it is shown even when simplePopups is true)
function simplePopupContent(a, article) {
/* FIXME hack */ a.navpopup.hasPopupMenu=false;
a.navpopup.setInnerHTML(popupHTML(a));
fillEmptySpans({navpopup:a.navpopup});
if (getValueOf('popupDraggable'))
{
var dragHandle = getValueOf('popupDragHandle') || null;
if (dragHandle && dragHandle != 'all') {
dragHandle += a.navpopup.idNumber;
}
setTimeout(function(){a.navpopup.makeDraggable(dragHandle);}, 150);
}
//<NOLITE>
if (getValueOf('popupRedlinkRemoval') && a.className=='new') {
setPopupHTML('<br>'+popupRedlinkHTML(article), 'popupRedlink', a.navpopup.idNumber);
}
//</NOLITE>
}
function debugData(navpopup) {
if(getValueOf('popupDebugging') && navpopup.idNumber) {
setPopupHTML('idNumber='+navpopup.idNumber + ', pending=' + navpopup.pending,
'popupError', navpopup.idNumber);
}
}
function newNavpopup(a, article) {
var navpopup = new Navpopup();
navpopup.fuzz=5;
navpopup.delay=getValueOf('popupDelay')*1000;
// increment global counter now
navpopup.idNumber = ++pg.idNumber;
navpopup.parentAnchor = a;
navpopup.parentPopup = (a.popData && a.popData.owner);
navpopup.article = article;
registerHooks(navpopup);
return navpopup;
}
// Should we show nonsimple context?
// If simplePopups is set to true, then we do not show nonsimple context,
// but if a bottom "show preview" was clicked we do show nonsimple context
function shouldShowNonSimple(a) {
return !getValueOf('simplePopups') || a.simpleNoMore;
}
// Should we show nonsimple context govern by the option (e.g. popupUserInfo)?
// If the user explicitly asked for nonsimple context by setting the option to true,
// then we show it even in nonsimple mode.
function shouldShow(a,option) {
if (shouldShowNonSimple(a)) {
return getValueOf(option);
} else {
return (typeof window[option] != 'undefined' ) && window[option];
}
}
function nonsimplePopupContent(a, article) {
var diff=null, history=null;
var params=parseParams(a.href);
var oldid=(typeof params.oldid=='undefined' ? null : params.oldid);
//<NOLITE>
if(shouldShow(a,'popupPreviewDiffs')) {
diff=params.diff;
}
if(shouldShow(a,'popupPreviewHistory')) {
history=(params.action=='history');
}
//</NOLITE>
a.navpopup.pending=0;
var referenceElement = footnoteTarget(a);
if (referenceElement) {
footnotePreview(referenceElement, a.navpopup);
//<NOLITE>
} else if ( diff || diff === 0 ) {
loadDiff(article, oldid, diff, a.navpopup);
} else if ( history ) {
loadAPIPreview('history', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.contribs.test(a.href) ) {
loadAPIPreview('contribs', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.backlinks.test(a.href) ) {
loadAPIPreview('backlinks', article, a.navpopup);
} else if ( // FIXME should be able to get all preview combinations with options
article.namespaceId()==pg.nsImageId &&
( shouldShow(a,'imagePopupsForImages') || ! anchorContainsImage(a) )
) {
loadAPIPreview('imagepagepreview', article, a.navpopup);
loadImage(article, a.navpopup);
//</NOLITE>
} else {
if (article.namespaceId() == pg.nsCategoryId &&
shouldShow(a,'popupCategoryMembers')) {
loadAPIPreview('category', article, a.navpopup);
} else if ((article.namespaceId() == pg.nsUserId || article.namespaceId() == pg.nsUsertalkId) &&
shouldShow(a,'popupUserInfo')) {
loadAPIPreview('userinfo', article, a.navpopup);
}
if (shouldShowNonSimple(a)) startArticlePreview(article, oldid, a.navpopup);
}
}
function pendingNavpopTask(navpop) {
if (navpop && navpop.pending === null) { navpop.pending=0; }
++navpop.pending;
debugData(navpop);
}
function completedNavpopTask(navpop) {
if (navpop && navpop.pending) { --navpop.pending; }
debugData(navpop);
}
function startArticlePreview(article, oldid, navpop) {
navpop.redir=0;
loadPreview(article, oldid, navpop);
}
function loadPreview(article, oldid, navpop) {
if (!navpop.redir) { navpop.originalArticle=article; }
article.oldid = oldid;
loadAPIPreview('revision', article, navpop);
}
function loadPreviewFromRedir(redirMatch, navpop) {
// redirMatch is a regex match
var target = new Title().fromWikiText(redirMatch[2]);
// overwrite (or add) anchor from original target
// mediawiki does overwrite; eg User:Lupin/foo3#Done
if ( navpop.article.anchor ) { target.anchor = navpop.article.anchor; }
navpop.redir++;
navpop.redirTarget=target;
//<NOLITE>
var warnRedir = redirLink(target, navpop.article);
setPopupHTML(warnRedir, 'popupWarnRedir', navpop.idNumber);
//</NOLITE>
navpop.article=target;
fillEmptySpans({redir: true, redirTarget: target, navpopup:navpop});
return loadPreview(target, null, navpop);
}
function insertPreview(download) {
if (!download.owner) { return; }
var redirMatch = pg.re.redirect.exec(download.data);
if (download.owner.redir === 0 && redirMatch) {
loadPreviewFromRedir(redirMatch, download.owner);
return;
}
if (download.owner.visible || !getValueOf('popupLazyPreviews')) {
insertPreviewNow(download);
} else {
var id=(download.owner.redir) ? 'PREVIEW_REDIR_HOOK' : 'PREVIEW_HOOK';
download.owner.addHook( function(){insertPreviewNow(download); return true;},
'unhide', 'after', id );
}
}
function insertPreviewNow(download) {
if (!download.owner) { return; }
var wikiText=download.data;
var navpop=download.owner;
var art=navpop.redirTarget || navpop.originalArticle;
//<NOLITE>
makeFixDabs(wikiText, navpop);
if (getValueOf('popupSummaryData')) {
getPageInfo(wikiText, download);
setPopupTrailer(getPageInfo(wikiText, download), navpop.idNumber);
}
var imagePage='';
if (art.namespaceId()==pg.nsImageId) { imagePage=art.toString(); }
else { imagePage=getValidImageFromWikiText(wikiText); }
if(imagePage) { loadImage(Title.fromWikiText(imagePage), navpop); }
//</NOLITE>
if (getValueOf('popupPreviews')) { insertArticlePreview(download, art, navpop); }
}
function insertArticlePreview(download, art, navpop) {
if (download && typeof download.data == typeof ''){
if (art.namespaceId()==pg.nsTemplateId && getValueOf('popupPreviewRawTemplates')) {
// FIXME compare/consolidate with diff escaping code for wikitext
var h='<hr /><span style="font-family: monospace;">' + download.data.entify().split('\\n').join('<br />\\n') + '</span>';
setPopupHTML(h, 'popupPreview', navpop.idNumber);
}
else {
var p=prepPreviewmaker(download.data, art, navpop);
p.showPreview();
}
}
}
function prepPreviewmaker(data, article, navpop) {
// deal with tricksy anchors
var d=anchorize(data, article.anchorString());
var urlBase=joinPath([pg.wiki.articlebase, article.urlString()]);
var p=new Previewmaker(d, urlBase, navpop);
return p;
}
// Try to imitate the way mediawiki generates HTML anchors from section titles
function anchorize(d, anch) {
if (!anch) { return d; }
var anchRe=RegExp('(?:=+\\s*' + literalizeRegex(anch).replace(/[_ ]/g, '[_ ]') + '\\s*=+|\\{\\{\\s*'+getValueOf('popupAnchorRegexp')+'\\s*(?:\\|[^|}]*)*?\\s*'+literalizeRegex(anch)+'\\s*(?:\\|[^}]*)?}})');
var match=d.match(anchRe);
if(match && match.length > 0 && match[0]) { return d.substring(d.indexOf(match[0])); }
// now try to deal with == foo [[bar|baz]] boom == -> #foo_baz_boom
var lines=d.split('\n');
for (var i=0; i<lines.length; ++i) {
lines[i]=lines[i].replace(RegExp('[[]{2}([^|\\]]*?[|])?(.*?)[\\]]{2}', 'g'), '$2')
.replace(/'''([^'])/g, '$1').replace(RegExp("''([^'])", 'g'), '$1');
if (lines[i].match(anchRe)) {
return d.split('\n').slice(i).join('\n').replace(RegExp('^[^=]*'), '');
}
}
return d;
}
function killPopup() {
removeModifierKeyHandler(this);
if (getValueOf('popupShortcutKeys')) { rmPopupShortcuts(); }
if (!pg) { return; }
if (pg.current.link && pg.current.link.navpopup) { pg.current.link.navpopup.banish(); }
pg.current.link=null;
abortAllDownloads();
if (pg.timer.checkPopupPosition) {
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=null;
}
return true; // preserve default action
}
// ENDFILE: actions.js
// STARTFILE: domdrag.js
/**
@fileoverview
The {@link Drag} object, which enables objects to be dragged around.
<pre>
*************************************************
dom-drag.js
09.25.2001
www.youngpup.net
**************************************************
10.28.2001 - fixed minor bug where events
sometimes fired off the handle, not the root.
*************************************************
Pared down, some hooks added by User:Lupin
Copyright Aaron Boodman.
Saying stupid things daily since March 2001.
</pre>
*/
/**
Creates a new Drag object. This is used to make various DOM elements draggable.
@constructor
*/
function Drag () {
/**
Condition to determine whether or not to drag. This function should take one parameter, an Event.
To disable this, set it to <code>null</code>.
@type Function
*/
this.startCondition = null;
/**
Hook to be run when the drag finishes. This is passed the final coordinates of
the dragged object (two integers, x and y). To disables this, set it to <code>null</code>.
@type Function
*/
this.endHook = null;
}
/**
Gets an event in a cross-browser manner.
@param {Event} e
@private
*/
Drag.prototype.fixE = function(e) {
if (typeof e == 'undefined') { e = window.event; }
if (typeof e.layerX == 'undefined') { e.layerX = e.offsetX; }
if (typeof e.layerY == 'undefined') { e.layerY = e.offsetY; }
return e;
};
/**
Initialises the Drag instance by telling it which object you want to be draggable, and what you want to drag it by.
@param {DOMElement} o The "handle" by which <code>oRoot</code> is dragged.
@param {DOMElement} oRoot The object which moves when <code>o</code> is dragged, or <code>o</code> if omitted.
*/
Drag.prototype.init = function(o, oRoot) {
var dragObj = this;
this.obj = o;
o.onmousedown = function(e) { dragObj.start.apply( dragObj, [e]); };
o.dragging = false;
o.popups_draggable = true;
o.hmode = true;
o.vmode = true;
o.root = oRoot ? oRoot : o ;
if (isNaN(parseInt(o.root.style.left, 10))) { o.root.style.left = "0px"; }
if (isNaN(parseInt(o.root.style.top, 10))) { o.root.style.top = "0px"; }
o.root.onthisStart = function(){};
o.root.onthisEnd = function(){};
o.root.onthis = function(){};
};
/**
Starts the drag.
@private
@param {Event} e
*/
Drag.prototype.start = function(e) {
var o = this.obj; // = this;
e = this.fixE(e);
if (this.startCondition && !this.startCondition(e)) { return; }
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10);
o.root.onthisStart(x, y);
o.lastMouseX = e.clientX;
o.lastMouseY = e.clientY;
var dragObj = this;
o.onmousemoveDefault = document.onmousemove;
o.dragging = true;
document.onmousemove = function(e) { dragObj.drag.apply( dragObj, [e] ); };
document.onmouseup = function(e) { dragObj.end.apply( dragObj, [e] ); };
return false;
};
/**
Does the drag.
@param {Event} e
@private
*/
Drag.prototype.drag = function(e) {
e = this.fixE(e);
var o = this.obj;
var ey = e.clientY;
var ex = e.clientX;
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10 );
var nx, ny;
nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));
this.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
this.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
this.obj.lastMouseX = ex;
this.obj.lastMouseY = ey;
this.obj.root.onthis(nx, ny);
return false;
};
/**
Ends the drag.
@private
*/
Drag.prototype.end = function() {
document.onmousemove=this.obj.onmousemoveDefault;
document.onmouseup = null;
this.obj.dragging = false;
if (this.endHook) {
this.endHook( parseInt(this.obj.root.style[this.obj.hmode ? "left" : "right"], 10),
parseInt(this.obj.root.style[this.obj.vmode ? "top" : "bottom"], 10));
}
};
// ENDFILE: domdrag.js
// STARTFILE: structures.js
//<NOLITE>
pg.structures.original={};
pg.structures.original.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle',
'popupUserData', 'popupData', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.original.popupRedirSpans=function () {
return ['popupRedir', 'popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'];
};
pg.structures.original.popupTitle=function (x) {
log ('defaultstructure.popupTitle');
if (!getValueOf('popupNavLinks')) {
return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
}
return '';
};
pg.structures.original.popupTopLinks=function (x) {
log ('defaultstructure.popupTopLinks');
if (getValueOf('popupNavLinks')) { return navLinksHTML(x.article, x.hint, x.params); }
return '';
};
pg.structures.original.popupImage=function(x) {
log ('original.popupImage, x.article='+x.article+', x.navpop.idNumber='+x.navpop.idNumber);
return imageHTML(x.article, x.navpop.idNumber);
};
pg.structures.original.popupRedirTitle=pg.structures.original.popupTitle;
pg.structures.original.popupRedirTopLinks=pg.structures.original.popupTopLinks;
function copyStructure(oldStructure, newStructure) {
pg.structures[newStructure]={};
for (var prop in pg.structures[oldStructure]) {
pg.structures[newStructure][prop]=pg.structures[oldStructure][prop];
}
}
copyStructure('original', 'nostalgia');
pg.structures.nostalgia.popupTopLinks=function(x) {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>';
str+='if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' +
editstr + '}';
var historystr='<<history|shortcut=h>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str += '<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>';
str += 'if(admin){<br>}else{*}<<move|shortcut=m>>';
// admin links
str += 'if(admin){*<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>}';
return navlinkStringToHTML(str, x.article, x.params);
};
pg.structures.nostalgia.popupRedirTopLinks=pg.structures.nostalgia.popupTopLinks;
/** -- fancy -- **/
copyStructure('original', 'fancy');
pg.structures.fancy.popupTitle=function (x) {
return navlinkStringToHTML('<font size=+0><<mainlink>></font>',x.article,x.params);
};
pg.structures.fancy.popupTopLinks=function(x) {
var hist='<<history|shortcut=h|hist>>|<<lastEdit|shortcut=/|last>>|<<editors|shortcut=E|eds>>';
var watch='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
var move='<<move|shortcut=m|move>>';
return navlinkStringToHTML('if(talk){' +
'<<edit|shortcut=e>>|<<new|shortcut=+|+>>*' + hist + '*' +
'<<article|shortcut=a>>|<<editArticle|edit>>' + '*' + watch + '*' + move +
'}else{<<edit|shortcut=e>>*' + hist +
'*<<talk|shortcut=t|>>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>' +
'*' + watch + '*' + move+'}<br>', x.article, x.params);
};
pg.structures.fancy.popupOtherLinks=function(x) {
var admin='<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*<<undelete|undeleteShort>>|<<delete|shortcut=d|del>>';
var user='<<contribs|shortcut=c>>if(wikimedia){|<<count|shortcut=#|#>>}';
user+='if(ipuser){|<<arin>>}else{*<<email|shortcut=E|'+
popupString('email')+'>>}if(admin){*<<block|shortcut=b>>}';
var normal='<<whatLinksHere|shortcut=l|links here>>*<<relatedChanges|shortcut=r|related>>';
return navlinkStringToHTML('<br>if(user){' + user + '*}if(admin){'+admin+'if(user){<br>}else{*}}' + normal,
x.article, x.params);
};
pg.structures.fancy.popupRedirTitle=pg.structures.fancy.popupTitle;
pg.structures.fancy.popupRedirTopLinks=pg.structures.fancy.popupTopLinks;
pg.structures.fancy.popupRedirOtherLinks=pg.structures.fancy.popupOtherLinks;
/** -- fancy2 -- **/
// hack for User:MacGyverMagic
copyStructure('fancy', 'fancy2');
pg.structures.fancy2.popupTopLinks=function(x) { // hack out the <br> at the end and put one at the beginning
return '<br>'+pg.structures.fancy.popupTopLinks(x).replace(RegExp('<br>$','i'),'');
};
pg.structures.fancy2.popupLayout=function () { // move toplinks to after the title
return ['popupError', 'popupImage', 'popupTitle', 'popupUserData', 'popupData', 'popupTopLinks', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
/** -- menus -- **/
copyStructure('original', 'menus');
pg.structures.menus.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupUserData', 'popupData', 'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.menus.popupTopLinks = function (x, shorter) {
// FIXME maybe this stuff should be cached
var s=[];
var dropdiv='<div class="popup_drop">';
var enddiv='</div>';
var hist='<<history|shortcut=h>>';
if (!shorter) { hist = '<menurow>' + hist +
'|<<historyfeed|rss>>|<<editors|shortcut=E>></menurow>'; }
var lastedit='<<lastEdit|shortcut=/|show last edit>>';
var thank='if(diff){<<thank|send thanks>>}';
var jsHistory='<<lastContrib|last set of edits>><<sinceMe|changes since mine>>';
var linkshere='<<whatLinksHere|shortcut=l|what links here>>';
var related='<<relatedChanges|shortcut=r|related changes>>';
var search='<menurow><<search|shortcut=s>>if(wikimedia){|<<globalsearch|shortcut=g|global>>}' +
'|<<google|shortcut=G|web>></menurow>';
var watch='<menurow><<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>></menurow>';
var protect='<menurow><<unprotect|unprotectShort>>|' +
'<<protect|shortcut=p>>|<<protectlog|log>></menurow>';
var del='<menurow><<undelete|undeleteShort>>|<<delete|shortcut=d>>|' +
'<<deletelog|log>></menurow>';
var move='<<move|shortcut=m|move page>>';
var nullPurge='<menurow><<nullEdit|shortcut=n|null edit>>|<<purge|shortcut=P>></menurow>';
var viewOptions='<menurow><<view|shortcut=v>>|<<render|shortcut=S>>|<<raw>></menurow>';
var editRow='if(oldid){' +
'<menurow><<edit|shortcut=e>>|<<editOld|shortcut=e|this revision>></menurow>' +
'<menurow><<revert|shortcut=v>>|<<undo>></menurow>' + '}else{<<edit|shortcut=e>>}';
var markPatrolled='if(rcid){<<markpatrolled|mark patrolled>>}';
var newTopic='if(talk){<<new|shortcut=+|new topic>>}';
var protectDelete='if(admin){' + protect + del + '}';
if (getValueOf('popupActionsMenu')) {
s.push( '<<mainlink>>*' + dropdiv + menuTitle('actions'));
} else {
s.push( dropdiv + '<<mainlink>>');
}
s.push( '<menu>');
s.push( editRow + markPatrolled + newTopic + hist + lastedit + thank );
if (!shorter) { s.push(jsHistory); }
s.push( move + linkshere + related);
if (!shorter) { s.push(nullPurge + search); }
if (!shorter) { s.push(viewOptions); }
s.push('<hr />' + watch + protectDelete);
s.push('<hr />' +
'if(talk){<<article|shortcut=a|view article>><<editArticle|edit article>>}' +
'else{<<talk|shortcut=t|talk page>><<editTalk|edit talk>>' +
'<<newTalk|shortcut=+|new topic>>}</menu>' + enddiv);
// user menu starts here
var email='<<email|shortcut=E|email user>>';
var contribs= 'if(wikimedia){<menurow>}<<contribs|shortcut=c|contributions>>if(wikimedia){</menurow>}' +
'if(admin){<menurow><<deletedContribs>></menurow>}';
s.push('if(user){*' + dropdiv + menuTitle('user'));
s.push('<menu>');
s.push('<menurow><<userPage|shortcut=u|user page>>|<<userSpace|space>></menurow>');
s.push('<<userTalk|shortcut=t|user talk>><<editUserTalk|edit user talk>>' +
'<<newUserTalk|shortcut=+|leave comment>>');
if(!shorter) { s.push( 'if(ipuser){<<arin>>}else{' + email + '}' ); }
else { s.push( 'if(ipuser){}else{' + email + '}' ); }
s.push('<hr />' + contribs + '<<userlog|shortcut=L|user log>>');
s.push('if(wikimedia){<<count|shortcut=#|edit counter>>}');
s.push('if(admin){<menurow><<unblock|unblockShort>>|<<block|shortcut=b|block user>></menurow>}');
s.push('<<blocklog|shortcut=B|block log>>');
s.push('</menu>' + enddiv + '}');
// popups menu starts here
if (getValueOf('popupSetupMenu') && !x.navpop.hasPopupMenu /* FIXME: hack */) {
x.navpop.hasPopupMenu=true;
s.push('*' + dropdiv + menuTitle('popupsMenu') + '<menu>');
s.push('<<togglePreviews|toggle previews>>');
s.push('<<purgePopups|reset>>');
s.push('<<disablePopups|disable>>');
s.push('</menu>'+enddiv);
}
return navlinkStringToHTML(s.join(''), x.article, x.params);
};
function menuTitle(s) {
return '<a href="#" noPopup=1>' + popupString(s) + '</a>';
}
pg.structures.menus.popupRedirTitle=pg.structures.menus.popupTitle;
pg.structures.menus.popupRedirTopLinks=pg.structures.menus.popupTopLinks;
copyStructure('menus', 'shortmenus');
pg.structures.shortmenus.popupTopLinks=function(x) {
return pg.structures.menus.popupTopLinks(x,true);
};
pg.structures.shortmenus.popupRedirTopLinks=pg.structures.shortmenus.popupTopLinks;
//</NOLITE>
pg.structures.lite={};
pg.structures.lite.popupLayout=function () {
return ['popupTitle', 'popupPreview' ];
};
pg.structures.lite.popupTitle=function (x) {
log (x.article + ': structures.lite.popupTitle');
//return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
return '<div><span class="popup_mainlink"><b>' + x.article.toString() + '</b></span></div>';
};
// ENDFILE: structures.js
// STARTFILE: autoedit.js
//<NOLITE>
function substitute(data,cmdBody) {
// alert('sub\nfrom: '+cmdBody.from+'\nto: '+cmdBody.to+'\nflags: '+cmdBody.flags);
var fromRe=RegExp(cmdBody.from, cmdBody.flags);
return data.replace(fromRe, cmdBody.to);
}
function execCmds(data, cmdList) {
for (var i=0; i<cmdList.length; ++i) {
data=cmdList[i].action(data, cmdList[i]);
}
return data;
}
function parseCmd(str) {
// returns a list of commands
if (!str.length) { return []; }
var p=false;
switch (str.charAt(0)) {
case 's':
p=parseSubstitute(str);
break;
default:
return false;
}
if (p) { return [p].concat(parseCmd(p.remainder)); }
return false;
}
function unEscape(str, sep) {
return str.split('\\\\').join('\\').split('\\'+sep).join(sep).split('\\n').join('\n');
}
function parseSubstitute(str) {
// takes a string like s/a/b/flags;othercmds and parses it
var from,to,flags,tmp;
if (str.length<4) { return false; }
var sep=str.charAt(1);
str=str.substring(2);
tmp=skipOver(str,sep);
if (tmp) { from=tmp.segment; str=tmp.remainder; }
else { return false; }
tmp=skipOver(str,sep);
if (tmp) { to=tmp.segment; str=tmp.remainder; }
else { return false; }
flags='';
if (str.length) {
tmp=skipOver(str,';') || skipToEnd(str, ';');
if (tmp) {flags=tmp.segment; str=tmp.remainder; }
}
return {action: substitute, from: from, to: to, flags: flags, remainder: str};
}
function skipOver(str,sep) {
var endSegment=findNext(str,sep);
if (endSegment<0) { return false; }
var segment=unEscape(str.substring(0,endSegment), sep);
return {segment: segment, remainder: str.substring(endSegment+1)};
}
/*eslint-disable*/
function skipToEnd(str,sep) {
return {segment: str, remainder: ''};
}
/*eslint-enable */
function findNext(str, ch) {
for (var i=0; i<str.length; ++i) {
if (str.charAt(i)=='\\') { i+=2; }
if (str.charAt(i)==ch) { return i; }
}
return -1;
}
function setCheckbox(param, box) {
var val=mw.util.getParamValue(param);
if (val) {
switch (val) {
case '1': case 'yes': case 'true':
box.checked=true;
break;
case '0': case 'no': case 'false':
box.checked=false;
}
}
}
function autoEdit() {
setupPopups( function () {
if (mw.util.getParamValue('autoimpl') !== popupString('autoedit_version') ) { return false; }
if (mw.util.getParamValue('autowatchlist') && mw.util.getParamValue('actoken')===autoClickToken()) {
pg.fn.modifyWatchlist(mw.util.getParamValue('title'), mw.util.getParamValue('action'));
}
if (!document.editform) { return false; }
if (autoEdit.alreadyRan) { return false; }
autoEdit.alreadyRan=true;
var cmdString=mw.util.getParamValue('autoedit');
if (cmdString) {
try {
var editbox=document.editform.wpTextbox1;
var cmdList=parseCmd(cmdString);
var input=editbox.value;
var output=execCmds(input, cmdList);
editbox.value=output;
} catch (dang) { return; }
// wikEd user script compatibility
if (typeof(wikEdUseWikEd) != 'undefined') {
if (wikEdUseWikEd === true) {
WikEdUpdateFrame();
}
}
}
setCheckbox('autominor', document.editform.wpMinoredit);
setCheckbox('autowatch', document.editform.wpWatchthis);
var rvid = mw.util.getParamValue('autorv');
if (rvid) {
var url=pg.wiki.apiwikibase + '?action=query&format=json&formatversion=2&prop=revisions&revids='+rvid;
startDownload(url, null, autoEdit2);
} else { autoEdit2(); }
} );
}
function autoEdit2(d) {
var summary=mw.util.getParamValue('autosummary');
var summaryprompt=mw.util.getParamValue('autosummaryprompt');
var summarynotice='';
if (d && d.data && mw.util.getParamValue('autorv')) {
var s = getRvSummary(summary, d.data);
if (s === false) {
summaryprompt=true;
summarynotice=popupString('Failed to get revision information, please edit manually.\n\n');
summary = simplePrintf(summary, [mw.util.getParamValue('autorv'), '(unknown)', '(unknown)']);
} else { summary = s; }
}
if (summaryprompt) {
var txt= summarynotice +
popupString('Enter a non-empty edit summary or press cancel to abort');
var response=prompt(txt, summary);
if (response) { summary=response; }
else { return; }
}
if (summary) { document.editform.wpSummary.value=summary; }
// Attempt to avoid possible premature clicking of the save button
// (maybe delays in updates to the DOM are to blame?? or a red herring)
setTimeout(autoEdit3, 100);
}
function autoClickToken() {
return mw.user.sessionId();
}
function autoEdit3() {
if( mw.util.getParamValue('actoken') != autoClickToken()) { return; }
var btn=mw.util.getParamValue('autoclick');
if (btn) {
if (document.editform && document.editform[btn]) {
var button=document.editform[btn];
var msg=tprintf('The %s button has been automatically clicked. Please wait for the next page to load.',
[ button.value ]);
bannerMessage(msg);
document.title='('+document.title+')';
button.click();
} else {
alert(tprintf('Could not find button %s. Please check the settings in your javascript file.',
[ btn ]));
}
}
}
function bannerMessage(s) {
var headings=document.getElementsByTagName('h1');
if (headings) {
var div=document.createElement('div');
div.innerHTML='<font size=+1><b>' + s + '</b></font>';
headings[0].parentNode.insertBefore(div, headings[0]);
}
}
function getRvSummary(template, json) {
try {
var o=getJsObj(json);
var edit = anyChild(o.query.pages).revisions[0];
var timestamp = edit.timestamp.split(/[A-Z]/g).join(' ').replace(/^ *| *$/g, '');
return simplePrintf(template, [edit.revid, timestamp, edit.userhidden ? '(hidden)' : edit.user ]);
} catch (badness) {
return false;
}
}
//</NOLITE>
// ENDFILE: autoedit.js
// STARTFILE: downloader.js
/**
@fileoverview
{@link Downloader}, a xmlhttprequest wrapper, and helper functions.
*/
/**
Creates a new Downloader
@constructor
@class The Downloader class. Create a new instance of this class to download stuff.
@param {String} url The url to download. This can be omitted and supplied later.
*/
function Downloader(url) {
if (typeof XMLHttpRequest!='undefined') { this.http = new XMLHttpRequest(); }
/**
The url to download
@type String
*/
this.url = url;
/**
A universally unique ID number
@type integer
*/
this.id=null;
/**
Modification date, to be culled from the incoming headers
@type Date
@private
*/
this.lastModified = null;
/**
What to do when the download completes successfully
@type Function
@private
*/
this.callbackFunction = null;
/**
What to do on failure
@type Function
@private
*/
this.onFailure = null;
/**
Flag set on <code>abort</code>
@type boolean
*/
this.aborted = false;
/**
HTTP method. See https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html for possibilities.
@type String
*/
this.method='GET';
/**
Async flag.
@type boolean
*/
this.async=true;
}
new Downloader();
/** Submits the http request. */
Downloader.prototype.send = function (x) {
if (!this.http) { return null; }
return this.http.send(x);
};
/** Aborts the download, setting the <code>aborted</code> field to true. */
Downloader.prototype.abort = function () {
if (!this.http) { return null; }
this.aborted=true;
return this.http.abort();
};
/** Returns the downloaded data. */
Downloader.prototype.getData = function () {if (!this.http) { return null; } return this.http.responseText;};
/** Prepares the download. */
Downloader.prototype.setTarget = function () {
if (!this.http) { return null; }
this.http.open(this.method, this.url, this.async);
this.http.setRequestHeader( 'Api-User-Agent', pg.misc.userAgent );
};
/** Gets the state of the download. */
Downloader.prototype.getReadyState=function () {if (!this.http) { return null; } return this.http.readyState;};
pg.misc.downloadsInProgress = { };
/** Starts the download.
Note that setTarget {@link Downloader#setTarget} must be run first
*/
Downloader.prototype.start=function () {
if (!this.http) { return; }
pg.misc.downloadsInProgress[this.id] = this;
this.http.send(null);
};
/** Gets the 'Last-Modified' date from the download headers.
Should be run after the download completes.
Returns <code>null</code> on failure.
@return {Date}
*/
Downloader.prototype.getLastModifiedDate=function () {
if(!this.http) { return null; }
var lastmod=null;
try {
lastmod=this.http.getResponseHeader('Last-Modified');
} catch (err) {}
if (lastmod) { return new Date(lastmod); }
return null;
};
/** Sets the callback function.
@param {Function} f callback function, called as <code>f(this)</code> on success
*/
Downloader.prototype.setCallback = function (f) {
if(!this.http) { return; }
this.http.onreadystatechange = f;
};
Downloader.prototype.getStatus = function() { if (!this.http) { return null; } return this.http.status; };
//////////////////////////////////////////////////
// helper functions
/** Creates a new {@link Downloader} and prepares it for action.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function newDownload(url, id, callback, onfailure) {
var d=new Downloader(url);
if (!d.http) { return 'ohdear'; }
d.id=id;
d.setTarget();
if (!onfailure) {
onfailure=2;
}
var f = function () {
if (d.getReadyState() == 4) {
delete pg.misc.downloadsInProgress[this.id];
try {
if ( d.getStatus() == 200 ) {
d.data=d.getData();
d.lastModified=d.getLastModifiedDate();
callback(d);
} else if (typeof onfailure == typeof 1) {
if (onfailure > 0) {
// retry
newDownload(url, id, callback, onfailure - 1);
}
} else if (typeof onfailure === 'function') {
onfailure(d,url,id,callback);
}
} catch (somerr) { /* ignore it */ }
}
};
d.setCallback(f);
return d;
}
/** Simulates a download from cached data.
The supplied data is put into a {@link Downloader} as if it had downloaded it.
@param {String} url The url.
@param {integer} id The ID.
@param {Function} callback The callback, which is invoked immediately as <code>callback(d)</code>,
where <code>d</code> is the new {@link Downloader}.
@param {String} data The (cached) data.
@param {Date} lastModified The (cached) last modified date.
*/
function fakeDownload(url, id, callback, data, lastModified, owner) {
var d=newDownload(url,callback);
d.owner=owner;
d.id=id; d.data=data;
d.lastModified=lastModified;
return callback(d);
}
/**
Starts a download.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function startDownload(url, id, callback) {
var d=newDownload(url, id, callback);
if (typeof d == typeof '' ) { return d; }
d.start();
return d;
}
/**
Aborts all downloads which have been started.
*/
function abortAllDownloads() {
for ( var x in pg.misc.downloadsInProgress ) {
try {
pg.misc.downloadsInProgress[x].aborted=true;
pg.misc.downloadsInProgress[x].abort();
delete pg.misc.downloadsInProgress[x];
} catch (e) {}
}
}
// ENDFILE: downloader.js
// STARTFILE: livepreview.js
// TODO: location is often not correct (eg relative links in previews)
// NOTE: removed md5 and image and math parsing. was broken, lots of bytes.
/**
* InstaView - a Mediawiki to HTML converter in JavaScript
* Version 0.6.1
* Copyright (C) Pedro Fayolle 2005-2006
* https://en.wikipedia.org/wiki/User:Pilaf
* Distributed under the BSD license
*
* Changelog:
*
* 0.6.1
* - Fixed problem caused by \r characters
* - Improved inline formatting parser
*
* 0.6
* - Changed name to InstaView
* - Some major code reorganizations and factored out some common functions
* - Handled conversion of relative links (i.e. [[/foo]])
* - Fixed misrendering of adjacent definition list items
* - Fixed bug in table headings handling
* - Changed date format in signatures to reflect Mediawiki's
* - Fixed handling of [[:Image:...]]
* - Updated MD5 function (hopefully it will work with UTF-8)
* - Fixed bug in handling of links inside images
*
* To do:
* - Better support for math tags
* - Full support for <nowiki>
* - Parser-based (as opposed to RegExp-based) inline wikicode handling (make it one-pass and bullet-proof)
* - Support for templates (through AJAX)
* - Support for coloured links (AJAX)
*/
var Insta = {};
function setupLivePreview() {
// options
Insta.conf =
{
baseUrl: '',
user: {},
wiki: {
lang: pg.wiki.lang,
interwiki: pg.wiki.interwiki,
default_thumb_width: 180
},
paths: {
articles: pg.wiki.articlePath + '/',
// Only used for Insta previews with images. (not in popups)
math: '/math/',
images: '//upload.wikimedia.org/wikipedia/en/', // FIXME getImageUrlStart(pg.wiki.hostname),
images_fallback: '//upload.wikimedia.org/wikipedia/commons/',
},
locale: {
user: mw.config.get('wgFormattedNamespaces')[pg.nsUserId],
image: mw.config.get('wgFormattedNamespaces')[pg.nsImageId],
category: mw.config.get('wgFormattedNamespaces')[pg.nsCategoryId],
// shouldn't be used in popup previews, i think
months: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
}
};
// options with default values or backreferences
Insta.conf.user.name = Insta.conf.user.name || 'Wikipedian';
Insta.conf.user.signature = '[['+Insta.conf.locale.user+':'+Insta.conf.user.name+'|'+Insta.conf.user.name+']]';
//Insta.conf.paths.images = '//upload.wikimedia.org/wikipedia/' + Insta.conf.wiki.lang + '/';
// define constants
Insta.BLOCK_IMAGE = new RegExp('^\\[\\[(?:File|Image|'+Insta.conf.locale.image+
'):.*?\\|.*?(?:frame|thumbnail|thumb|none|right|left|center)', 'i');
}
Insta.dump = function(from, to)
{
if (typeof from == 'string') { from = document.getElementById(from); }
if (typeof to == 'string') { to = document.getElementById(to); }
to.innerHTML = this.convert(from.value);
};
Insta.convert = function(wiki)
{
var ll = (typeof wiki == 'string')? wiki.replace(/\r/g,'').split(/\n/): wiki, // lines of wikicode
o = '', // output
p = 0, // para flag
$r; // result of passing a regexp to $()
// some shorthands
function remain() { return ll.length; }
function sh() { return ll.shift(); } // shift
function ps(s) { o += s; } // push
// similar to C's printf, uses ? as placeholders, ?? to escape question marks
function f()
{
var i=1, a=arguments, f=a[0], o='', c, p;
for (; i<a.length; i++) {
if ((p=f.indexOf('?'))+1) {
// allow character escaping
i -= c = f.charAt(p+1)=='?' ? 1 : 0;
o += f.substring(0,p) + (c ? '?' : a[i]);
f = f.substr(p+1+c);
} else { break; }
}
return o+f;
}
function html_entities(s) {
return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
}
// Wiki text parsing to html is a nightmare.
// The below functions deliberately don't escape the ampersand since this would make it more difficult,
// and we don't absolutely need to for how we need it.
// This means that any unescaped ampersands in wikitext will remain unescaped and can cause invalid HTML.
// Browsers should all be able to handle it though.
// We also escape significant wikimarkup characters to prevent further matching on the processed text
function htmlescape_text(s) {
return s.replace(/</g,"<").replace(/>/g,">").replace(/:/g,":").replace(/\[/g,"[").replace(/]/g,"]");
}
function htmlescape_attr(s) {
return htmlescape_text(s).replace(/'/g,"'").replace(/"/g,""");
}
// return the first non matching character position between two strings
function str_imatch(a, b)
{
for (var i=0, l=Math.min(a.length, b.length); i<l; i++) {
if (a.charAt(i)!=b.charAt(i)) { break; }
}
return i;
}
// compare current line against a string or regexp
// if passed a string it will compare only the first string.length characters
// if passed a regexp the result is stored in $r
function $(c) { return (typeof c == 'string') ? (ll[0].substr(0,c.length)==c) : ($r = ll[0].match(c)); }
function $$(c) { return ll[0]==c; } // compare current line against a string
function _(p) { return ll[0].charAt(p); } // return char at pos p
function endl(s) { ps(s); sh(); }
function parse_list()
{
var prev='';
while (remain() && $(/^([*#:;]+)(.*)$/)) {
var l_match = $r;
sh();
var ipos = str_imatch(prev, l_match[1]);
// close uncontinued lists
for (var prevPos=prev.length-1; prevPos >= ipos; prevPos--) {
var pi = prev.charAt(prevPos);
if (pi=='*') { ps('</ul>'); }
else if (pi=='#') { ps('</ol>'); }
// close a dl only if the new item is not a dl item (:, ; or empty)
else if($.inArray(l_match[1].charAt(prevPos), ['','*','#'])) { ps('</dl>'); }
}
// open new lists
for (var matchPos=ipos; matchPos<l_match[1].length; matchPos++) {
var li = l_match[1].charAt(matchPos);
if (li=='*') { ps('<ul>'); }
else if (li=='#') { ps('<ol>'); }
// open a new dl only if the prev item is not a dl item (:, ; or empty)
else if ($.inArray(prev.charAt(matchPos), ['','*','#'])) { ps('<dl>'); }
}
switch (l_match[1].charAt(l_match[1].length-1)) {
case '*': case '#':
ps('<li>' + parse_inline_nowiki(l_match[2]));
break;
case ';':
ps('<dt>');
var dt_match = l_match[2].match(/(.*?)(:.*?)$/);
// handle ;dt :dd format
if (dt_match) {
ps(parse_inline_nowiki(dt_match[1]));
ll.unshift(dt_match[2]);
} else ps(parse_inline_nowiki(l_match[2]));
break;
case ':':
ps('<dd>' + parse_inline_nowiki(l_match[2]));
}
prev=l_match[1];
}
// close remaining lists
for (var i=prev.length-1; i>=0; i--) {
ps(f('</?>', (prev.charAt(i)=='*')? 'ul': ((prev.charAt(i)=='#')? 'ol': 'dl')));
}
}
function parse_table()
{
endl(f('<table>', $(/^\{\|( .*)$/)? $r[1]: ''));
for (;remain();) if ($('|')) switch (_(1)) {
case '}':
endl('</table>');
return;
case '-':
endl(f('<tr>', $(/\|-*(.*)/)[1]));
break;
default:
parse_table_data();
}
else if ($('!')) { parse_table_data(); }
else { sh(); }
}
function parse_table_data()
{
var td_line, match_i;
// 1: "|+", '|' or '+'
// 2: ??
// 3: attributes ??
// TODO: finish commenting this regexp
var td_match = sh().match(/^(\|\+|\||!)((?:([^[|]*?)\|(?!\|))?(.*))$/);
if (td_match[1] == '|+') ps('<caption');
else ps('<t' + ((td_match[1]=='|')?'d':'h'));
if (typeof td_match[3] != 'undefined') {
//ps(' ' + td_match[3])
match_i = 4;
} else match_i = 2;
ps('>');
if (td_match[1] != '|+') {
// use || or !! as a cell separator depending on context
// NOTE: when split() is passed a regexp make sure to use non-capturing brackets
td_line = td_match[match_i].split((td_match[1] == '|')? '||': /(?:\|\||!!)/);
ps(parse_inline_nowiki(td_line.shift()));
while (td_line.length) ll.unshift(td_match[1] + td_line.pop());
} else ps(td_match[match_i]);
var tc = 0, td = [];
while (remain()) {
td.push(sh());
if ($('|')) {
if (!tc) break; // we're at the outer-most level (no nested tables), skip to td parse
else if (_(1)=='}') tc--;
}
else if (!tc && $('!')) break;
else if ($('{|')) tc++;
}
if (td.length) ps(Insta.convert(td));
}
function parse_pre()
{
ps('<pre>');
do {
endl(parse_inline_nowiki(ll[0].substring(1)) + "\n");
} while (remain() && $(' '));
ps('</pre>');
}
function parse_block_image()
{
ps(parse_image(sh()));
}
function parse_image(str)
{
//<NOLITE>
// get what's in between "[[Image:" and "]]"
var tag = str.substring(str.indexOf(':') + 1, str.length - 2);
/* eslint-disable no-unused-vars */
var width;
var attr = [], filename, caption = '';
var thumb=0, frame=0, center=0;
var align='';
/* eslint-enable no-unused-vars */
if (tag.match(/\|/)) {
// manage nested links
var nesting = 0;
var last_attr;
for (var i = tag.length-1; i > 0; i--) {
if (tag.charAt(i) == '|' && !nesting) {
last_attr = tag.substr(i+1);
tag = tag.substring(0, i);
break;
} else switch (tag.substr(i-1, 2)) {
case ']]':
nesting++;
i--;
break;
case '[[':
nesting--;
i--;
}
}
attr = tag.split(/\s*\|\s*/);
attr.push(last_attr);
filename = attr.shift();
var w_match;
for (;attr.length; attr.shift()) {
w_match = attr[0].match(/^(\d*)(?:[px]*\d*)?px$/);
if (w_match) width = w_match[1];
else switch(attr[0]) {
case 'thumb':
case 'thumbnail':
thumb=true;
frame=true;
break;
case 'frame':
frame=true;
break;
case 'none':
case 'right':
case 'left':
center=false;
align=attr[0];
break;
case 'center':
center=true;
align='none';
break;
default:
if (attr.length == 1) caption = attr[0];
}
}
} else filename = tag;
return '';
//</NOLITE>
}
function parse_inline_nowiki(str)
{
var start, lastend=0;
var substart=0, nestlev=0, open, close, subloop;
var html='';
while (-1 != (start = str.indexOf('<nowiki>', substart))) {
html += parse_inline_wiki(str.substring(lastend, start));
start += 8;
substart = start;
subloop = true;
do {
open = str.indexOf('<nowiki>', substart);
close = str.indexOf('</nowiki>', substart);
if (close<=open || open==-1) {
if (close==-1) {
return html + html_entities(str.substr(start));
}
substart = close+9;
if (nestlev) {
nestlev--;
} else {
lastend = substart;
html += html_entities(str.substring(start, lastend-9));
subloop = false;
}
} else {
substart = open+8;
nestlev++;
}
} while (subloop);
}
return html + parse_inline_wiki(str.substr(lastend));
}
function parse_inline_images(str)
{
//<NOLITE>
var start, substart=0, nestlev=0;
var loop, close, open, wiki, html;
while (-1 != (start=str.indexOf('[[', substart))) {
if(str.substr(start+2).match(RegExp('^(Image|File|' + Insta.conf.locale.image + '):','i'))) {
loop=true;
substart=start;
do {
substart+=2;
close=str.indexOf(']]',substart);
open=str.indexOf('[[',substart);
if (close<=open||open==-1) {
if (close==-1) return str;
substart=close;
if (nestlev) {
nestlev--;
} else {
wiki=str.substring(start,close+2);
html=parse_image(wiki);
str=str.replace(wiki,html);
substart=start+html.length;
loop=false;
}
} else {
substart=open;
nestlev++;
}
} while (loop);
} else break;
}
//</NOLITE>
return str;
}
// the output of this function doesn't respect the FILO structure of HTML
// but since most browsers can handle it I'll save myself the hassle
function parse_inline_formatting(str)
{
var em,st,i,li,o='';
while ((i=str.indexOf("''",li))+1) {
o += str.substring(li,i);
li=i+2;
if (str.charAt(i+2)=="'") {
li++;
st=!st;
o+=st?'<strong>':'</strong>';
} else {
em=!em;
o+=em?'<em>':'</em>';
}
}
return o+str.substr(li);
}
function parse_inline_wiki(str)
{
str = parse_inline_images(str);
str = parse_inline_formatting(str);
// math
str = str.replace(/<(?:)math>(.*?)<\/math>/ig, '');
// Build a Mediawiki-formatted date string
var date = new Date();
var minutes = date.getUTCMinutes();
if (minutes < 10) minutes = '0' + minutes;
date = f("?:?, ? ? ? (UTC)", date.getUTCHours(), minutes, date.getUTCDate(), Insta.conf.locale.months[date.getUTCMonth()], date.getUTCFullYear());
// text formatting
return str.
// signatures
replace(/~{5}(?!~)/g, date).
replace(/~{4}(?!~)/g, Insta.conf.user.name+' '+date).
replace(/~{3}(?!~)/g, Insta.conf.user.name).
// [[:Category:...]], [[:Image:...]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):[^|]*?)\\]\\](\\w*)','gi'), function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2));}).
// remove straight category and interwiki tags
replace(RegExp('\\[\\[(?:'+Insta.conf.locale.category+'|'+Insta.conf.wiki.interwiki+'):.*?\\]\\]','gi'),'').
// [[:Category:...|Links]], [[:Image:...|Links]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):.*?)\\|([^\\]]+?)\\]\\](\\w*)','gi'), function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3));}).
// [[/Relative links]]
replace(/\[\[(\/[^|]*?)\]\]/g, function($0,$1){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($1)); }).
// [[/Replaced|Relative links]]
replace(/\[\[(\/.*?)\|(.+?)\]\]/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($2)); }).
// [[Common links]]
replace(/\[\[([^[|]*?)\]\](\w*)/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2)); }).
// [[Replaced|Links]]
replace(/\[\[([^[]*?)\|([^\]]+?)\]\](\w*)/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3)); }).
// [[Stripped:Namespace|Namespace]]
replace(/\[\[([^\]]*?:)?(.*?)( *\(.*?\))?\|\]\]/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1) + htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($2)); }).
// External links
replace(/\[(https?|news|ftp|mailto|gopher|irc):(\/*)([^\]]*?) (.*?)\]/g, function($0,$1,$2,$3,$4){return f("<a class='external' href='?:?'>?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($4)); }).
replace(/\[http:\/\/(.*?)\]/g, function($0,$1){return f("<a class='external' href='http://?'>[#]</a>", htmlescape_attr($1)); }).
replace(/\[(news|ftp|mailto|gopher|irc):(\/*)(.*?)\]/g, function($0,$1,$2,$3){return f("<a class='external' href='?:?'>?:?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($1), htmlescape_text($2) + htmlescape_text($3)); }).
replace(/(^| )(https?|news|ftp|mailto|gopher|irc):(\/*)([^ $]*[^.,!?;: $])/g, function($0,$1,$2,$3,$4){return f("?<a class='external' href='?:?'>?:?</a>", htmlescape_text($1), htmlescape_attr($2), htmlescape_attr($3) + htmlescape_attr($4), htmlescape_text($2), htmlescape_text($3) + htmlescape_text($4)); }).
replace('__NOTOC__','').
replace('__NOEDITSECTION__','');
}
// begin parsing
for (;remain();) if ($(/^(={1,6})(.*)\1(.*)$/)) {
p=0;
endl(f('<h?>?</h?>?', $r[1].length, parse_inline_nowiki($r[2]), $r[1].length, $r[3]));
} else if ($(/^[*#:;]/)) {
p=0;
parse_list();
} else if ($(' ')) {
p=0;
parse_pre();
} else if ($('{|')) {
p=0;
parse_table();
} else if ($(/^----+$/)) {
p=0;
endl('<hr />');
} else if ($(Insta.BLOCK_IMAGE)) {
p=0;
parse_block_image();
} else {
// handle paragraphs
if ($$('')) {
p = (remain()>1 && ll[1]===(''));
if (p) endl('<p><br>');
} else {
if(!p) {
ps('<p>');
p=1;
}
ps(parse_inline_nowiki(ll[0]) + ' ');
}
sh();
}
return o;
};
function wiki2html(txt,baseurl) {
Insta.conf.baseUrl=baseurl;
return Insta.convert(txt);
}
// ENDFILE: livepreview.js
// STARTFILE: pageinfo.js
//<NOLITE>
function popupFilterPageSize(data) {
return formatBytes(data.length);
}
function popupFilterCountLinks(data) {
var num=countLinks(data);
return String(num) + ' ' + ((num!=1)?popupString('wikiLinks'):popupString('wikiLink'));
}
function popupFilterCountImages(data) {
var num=countImages(data);
return String(num) + ' ' + ((num!=1)?popupString('images'):popupString('image'));
}
function popupFilterCountCategories(data) {
var num=countCategories(data);
return String(num) + ' ' + ((num!=1)?popupString('categories'):popupString('category'));
}
function popupFilterLastModified(data,download) {
var lastmod=download.lastModified;
var now=new Date();
var age=now-lastmod;
if (lastmod && getValueOf('popupLastModified')) {
return (tprintf('%s old', [formatAge(age)])).replace(RegExp(' ','g'), ' ');
}
return '';
}
function formatAge(age) {
// coerce into a number
var a=0+age, aa=a;
var seclen = 1000;
var minlen = 60*seclen;
var hourlen = 60*minlen;
var daylen = 24*hourlen;
var weeklen = 7*daylen;
var numweeks = (a-a%weeklen)/weeklen; a = a-numweeks*weeklen; var sweeks = addunit(numweeks, 'week');
var numdays = (a-a%daylen)/daylen; a = a-numdays*daylen; var sdays = addunit(numdays, 'day');
var numhours = (a-a%hourlen)/hourlen; a = a-numhours*hourlen; var shours = addunit(numhours,'hour');
var nummins = (a-a%minlen)/minlen; a = a-nummins*minlen; var smins = addunit(nummins, 'minute');
var numsecs = (a-a%seclen)/seclen; a = a-numsecs*seclen; var ssecs = addunit(numsecs, 'second');
if (aa > 4*weeklen) { return sweeks; }
if (aa > weeklen) { return sweeks + ' ' + sdays; }
if (aa > daylen) { return sdays + ' ' + shours; }
if (aa > 6*hourlen) { return shours; }
if (aa > hourlen) { return shours + ' ' + smins; }
if (aa > 10*minlen) { return smins; }
if (aa > minlen) { return smins + ' ' + ssecs; }
return ssecs;
}
function addunit(num,str) { return '' + num + ' ' + ((num!=1) ? popupString(str+'s') : popupString(str)) ;}
function runPopupFilters(list, data, download) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (list[i] && typeof list[i] == 'function') {
var s=list[i](data, download, download.owner.article);
if (s) { ret.push(s); }
}
}
return ret;
}
function getPageInfo(data, download) {
if (!data || data.length === 0) { return popupString('Empty page'); }
var popupFilters=getValueOf('popupFilters') || [];
var extraPopupFilters = getValueOf('extraPopupFilters') || [];
var pageInfoArray = runPopupFilters(popupFilters.concat(extraPopupFilters), data, download);
var pageInfo=pageInfoArray.join(', ');
if (pageInfo !== '' ) { pageInfo = upcaseFirst(pageInfo); }
return pageInfo;
}
// this could be improved!
function countLinks(wikiText) { return wikiText.split('[[').length - 1; }
// if N = # matches, n = # brackets, then
// String.parenSplit(regex) intersperses the N+1 split elements
// with Nn other elements. So total length is
// L= N+1 + Nn = N(n+1)+1. So N=(L-1)/(n+1).
function countImages(wikiText) {
return (wikiText.parenSplit(pg.re.image).length - 1) / (pg.re.imageBracketCount + 1);
}
function countCategories(wikiText) {
return (wikiText.parenSplit(pg.re.category).length - 1) / (pg.re.categoryBracketCount + 1);
}
function popupFilterStubDetect(data, download, article) {
var counts=stubCount(data, article);
if (counts.real) { return popupString('stub'); }
if (counts.sect) { return popupString('section stub'); }
return '';
}
function popupFilterDisambigDetect(data, download, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return ''; }
return (isDisambig(data, article)) ? popupString('disambig') : '';
}
function formatBytes(num) {
return (num > 949) ? (Math.round(num/100)/10+popupString('kB')) : (num +' ' + popupString('bytes')) ;
}
//</NOLITE>
// ENDFILE: pageinfo.js
// STARTFILE: titles.js
/**
@fileoverview Defines the {@link Title} class, and associated crufty functions.
<code>Title</code> deals with article titles and their various
forms. {@link Stringwrapper} is the parent class of
<code>Title</code>, which exists simply to make things a little
neater.
*/
/**
Creates a new Stringwrapper.
@constructor
@class the Stringwrapper class. This base class is not really
useful on its own; it just wraps various common string operations.
*/
function Stringwrapper() {
/**
Wrapper for this.toString().indexOf()
@param {String} x
@type integer
*/
this.indexOf=function(x){return this.toString().indexOf(x);};
/**
Returns this.value.
@type String
*/
this.toString=function(){return this.value;};
/**
Wrapper for {@link String#parenSplit} applied to this.toString()
@param {RegExp} x
@type Array
*/
this.parenSplit=function(x){return this.toString().parenSplit(x);};
/**
Wrapper for this.toString().substring()
@param {String} x
@param {String} y (optional)
@type String
*/
this.substring=function(x,y){
if (typeof y=='undefined') { return this.toString().substring(x); }
return this.toString().substring(x,y);
};
/**
Wrapper for this.toString().split()
@param {String} x
@type Array
*/
this.split=function(x){return this.toString().split(x);};
/**
Wrapper for this.toString().replace()
@param {String} x
@param {String} y
@type String
*/
this.replace=function(x,y){ return this.toString().replace(x,y); };
}
/**
Creates a new <code>Title</code>.
@constructor
@class The Title class. Holds article titles and converts them into
various forms. Also deals with anchors, by which we mean the bits
of the article URL after a # character, representing locations
within an article.
@param {String} value The initial value to assign to the
article. This must be the canonical title (see {@link
Title#value}. Omit this in the constructor and use another function
to set the title if this is unavailable.
*/
function Title(val) {
/**
The canonical article title. This must be in UTF-8 with no
entities, escaping or nasties. Also, underscores should be
replaced with spaces.
@type String
@private
*/
this.value=null;
/**
The canonical form of the anchor. This should be exactly as
it appears in the URL, i.e. with the .C3.0A bits in.
@type String
*/
this.anchor='';
this.setUtf(val);
}
Title.prototype=new Stringwrapper();
/**
Returns the canonical representation of the article title, optionally without anchor.
@param {boolean} omitAnchor
@fixme Decide specs for anchor
@return String The article title and the anchor.
*/
Title.prototype.toString=function(omitAnchor) {
return this.value + ( (!omitAnchor && this.anchor) ? '#' + this.anchorString() : '' );
};
Title.prototype.anchorString=function() {
if (!this.anchor) { return ''; }
var split=this.anchor.parenSplit(/((?:[.][0-9A-F]{2})+)/);
var len=split.length;
for (var j=1; j<len; j+=2) {
// FIXME s/decodeURI/decodeURIComponent/g ?
split[j]=decodeURIComponent(split[j].split('.').join('%')).split('_').join(' ');
}
return split.join('');
};
Title.prototype.urlAnchor=function() {
var split=this.anchor.parenSplit('/((?:[%][0-9A-F]{2})+)/');
var len=split.length;
for (var j=1; j<len; j+=2) {
split[j]=split[j].split('%').join('.');
}
return split.join('');
};
Title.prototype.anchorFromUtf=function(str) {
this.anchor=encodeURIComponent(str.split(' ').join('_'))
.split('%3A').join(':').split("'").join('%27').split('%').join('.');
};
Title.fromURL=function(h) {
return new Title().fromURL(h);
};
Title.prototype.fromURL=function(h) {
if (typeof h != 'string') {
this.value=null;
return this;
}
// NOTE : playing with decodeURI, encodeURI, escape, unescape,
// we seem to be able to replicate the IE borked encoding
// IE doesn't do this new-fangled utf-8 thing.
// and it's worse than that.
// IE seems to treat the query string differently to the rest of the url
// the query is treated as bona-fide utf8, but the first bit of the url is pissed around with
// we fix up & for all browsers, just in case.
var splitted=h.split('?');
splitted[0]=splitted[0].split('&').join('%26');
h=splitted.join('?');
var contribs=pg.re.contribs.exec(h);
if (contribs) {
if (contribs[1]=='title=') { contribs[3]=contribs[3].split('+').join(' '); }
var u=new Title(contribs[3]);
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + u.stripNamespace()));
return this;
}
var email=pg.re.email.exec(h);
if (email) {
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(email[3]).stripNamespace()));
return this;
}
var backlinks=pg.re.backlinks.exec(h);
if (backlinks) {
this.setUtf(this.decodeNasties(new Title(backlinks[3])));
return this;
}
//A dummy title object for a Special:Diff link.
var specialdiff=pg.re.specialdiff.exec(h);
if (specialdiff) {
this.setUtf(this.decodeNasties(new Title(mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Diff')));
return this;
}
// no more special cases to check --
// hopefully it's not a disguised user-related or specially treated special page
var m=pg.re.main.exec(h);
if(m === null) { this.value=null; }
else {
var fromBotInterface = /[?](.+[&])?title=/.test(h);
if (fromBotInterface) {
m[2]=m[2].split('+').join('_');
}
var extracted = m[2] + (m[3] ? '#' + m[3] : '');
if (pg.flag.isSafari && /%25[0-9A-Fa-f]{2}/.test(extracted)) {
// Fix Safari issue
// Safari sometimes encodes % as %25 in UTF-8 encoded strings like %E5%A3 -> %25E5%25A3.
this.setUtf(decodeURIComponent(unescape(extracted)));
} else {
this.setUtf(this.decodeNasties(extracted));
}
}
return this;
};
Title.prototype.decodeNasties=function(txt) {
var ret= this.decodeEscapes(decodeURI(txt));
ret = ret.replace(/[_ ]*$/, '');
return ret;
};
Title.prototype.decodeEscapes=function(txt) {
var split=txt.parenSplit(/((?:[%][0-9A-Fa-f]{2})+)/);
var len=split.length;
for (var i=1; i<len; i=i+2) {
// FIXME is decodeURIComponent better?
split[i]=unescape(split[i]);
}
return split.join('');
};
Title.fromAnchor=function(a) {
return new Title().fromAnchor(a);
};
Title.prototype.fromAnchor=function(a) {
if (!a) { this.value=null; return this; }
return this.fromURL(a.href);
};
Title.fromWikiText=function(txt) {
return new Title().fromWikiText(txt);
};
Title.prototype.fromWikiText=function(txt) {
// FIXME - testing needed
txt=myDecodeURI(txt);
this.setUtf(txt);
return this;
};
Title.prototype.hintValue=function(){
if(!this.value) { return ''; }
return safeDecodeURI(this.value);
};
//<NOLITE>
Title.prototype.toUserName=function(withNs) {
if (this.namespaceId() != pg.nsUserId && this.namespaceId() != pg.nsUsertalkId) {
this.value=null;
return;
}
this.value = (withNs ? mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' : '') + this.stripNamespace().split('/')[0];
};
Title.prototype.userName=function(withNs) {
var t=(new Title(this.value));
t.toUserName(withNs);
if (t.value) { return t; }
return null;
};
Title.prototype.toTalkPage=function() {
// convert article to a talk page, or if we can't, return null
// In other words: return null if this ALREADY IS a talk page
// and return the corresponding talk page otherwise
//
// Per https://www.mediawiki.org/wiki/Manual:Namespace#Subject_and_talk_namespaces
// * All discussion namespaces have odd-integer indices
// * The discussion namespace index for a specific namespace with index n is n + 1
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId>=0 && namespaceId % 2 === 0) //non-special and subject namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId+1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
//</NOLITE>
// Return canonical, localized namespace
Title.prototype.namespace=function() {
return mw.config.get('wgFormattedNamespaces')[this.namespaceId()];
};
Title.prototype.namespaceId=function() {
var n=this.value.indexOf(':');
if (n<0) { return 0; } //mainspace
var namespaceId = mw.config.get('wgNamespaceIds')[this.value.substring(0,n).split(' ').join('_').toLowerCase()];
if (typeof namespaceId=='undefined') return 0; //mainspace
return namespaceId;
};
//<NOLITE>
Title.prototype.talkPage=function() {
var t=new Title(this.value);
t.toTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.isTalkPage=function() {
if (this.talkPage()===null) { return true; }
return false;
};
Title.prototype.toArticleFromTalkPage=function() {
//largely copy/paste from toTalkPage above.
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId >= 0 && namespaceId % 2 == 1) //non-special and talk namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId-1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
Title.prototype.articleFromTalkPage=function() {
var t=new Title(this.value);
t.toArticleFromTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.articleFromTalkOrArticle=function() {
var t=new Title(this.value);
if ( t.toArticleFromTalkPage() ) { return t; }
return this;
};
Title.prototype.isIpUser=function() {
return pg.re.ipUser.test(this.userName());
};
//</NOLITE>
Title.prototype.stripNamespace=function(){ // returns a string, not a Title
var n=this.value.indexOf(':');
if (n<0) { return this.value; }
var namespaceId = this.namespaceId();
if (namespaceId === pg.nsMainspaceId) return this.value;
return this.value.substring(n+1);
};
Title.prototype.setUtf=function(value){
if (!value) { this.value=''; return; }
var anch=value.indexOf('#');
if(anch < 0) { this.value=value.split('_').join(' '); this.anchor=''; return; }
this.value=value.substring(0,anch).split('_').join(' ');
this.anchor=value.substring(anch+1);
this.ns=null; // wait until namespace() is called
};
Title.prototype.setUrl=function(urlfrag) {
var anch=urlfrag.indexOf('#');
this.value=safeDecodeURI(urlfrag.substring(0,anch));
this.anchor=this.value.substring(anch+1);
};
Title.prototype.append=function(x){
this.setUtf(this.value + x);
};
Title.prototype.urlString=function(x) {
if(!x) { x={}; }
var v=this.toString(true);
if (!x.omitAnchor && this.anchor) { v+= '#' + this.urlAnchor(); }
if (!x.keepSpaces) { v=v.split(' ').join('_'); }
return encodeURI(v).split('&').join('%26').split('?').join('%3F').split('+').join('%2B');
};
Title.prototype.removeAnchor=function() {
return new Title(this.toString(true));
};
Title.prototype.toUrl=function() {
return pg.wiki.titlebase + this.urlString();
};
function parseParams(url) {
var specialDiff = pg.re.specialdiff.exec(url);
if (specialDiff)
{
var split= specialDiff[1].split('/');
if (split.length==1) return {oldid:split[0], diff: 'prev'};
else if (split.length==2) return {oldid: split[0], diff: split[1]};
}
var ret={};
if (url.indexOf('?')==-1) { return ret; }
url = url.split('#')[0];
var s=url.split('?').slice(1).join();
var t=s.split('&');
for (var i=0; i<t.length; ++i) {
var z=t[i].split('=');
z.push(null);
ret[z[0]]=z[1];
}
//Diff revision with no oldid is interpreted as a diff to the previous revision by MediaWiki
if (ret.diff && typeof(ret.oldid)==='undefined')
{
ret.oldid = "prev";
}
//Documentation seems to say something different, but oldid can also accept prev/next, and Echo is emitting such URLs. Simple fixup during parameter decoding:
if (ret.oldid && (ret.oldid==='prev' || ret.oldid==='next' || ret.oldid==='cur'))
{
var helper = ret.diff;
ret.diff = ret.oldid;
ret.oldid = helper;
}
return ret;
}
// (a) myDecodeURI (first standard decodeURI, then pg.re.urlNoPopup)
// (b) change spaces to underscores
// (c) encodeURI (just the straight one, no pg.re.urlNoPopup)
function myDecodeURI (str) {
var ret;
// FIXME decodeURIComponent??
try { ret=decodeURI(str.toString()); }
catch (summat) { return str; }
for (var i=0; i<pg.misc.decodeExtras.length; ++i) {
var from=pg.misc.decodeExtras[i].from;
var to=pg.misc.decodeExtras[i].to;
ret=ret.split(from).join(to);
}
return ret;
}
function safeDecodeURI(str) { var ret=myDecodeURI(str); return ret || str; }
///////////
// TESTS //
///////////
//<NOLITE>
function isDisambig(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
return ! article.isTalkPage() && pg.re.disambig.test(data);
}
function stubCount(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
var sectStub=0;
var realStub=0;
if (pg.re.stub.test(data)) {
var s=data.parenSplit(pg.re.stub);
for (var i=1; i<s.length; i=i+2) {
if (s[i]) { ++sectStub; }
else { ++realStub; }
}
}
return { real: realStub, sect: sectStub };
}
function isValidImageName(str){ // extend as needed...
return ( str.indexOf('{') == -1 );
}
function isInStrippableNamespace(article) {
// Does the namespace allow subpages
// Note, would be better if we had access to wgNamespacesWithSubpages
return ( article.namespaceId() !== 0 );
}
function isInMainNamespace(article) { return article.namespaceId() === 0; }
function anchorContainsImage(a) {
// iterate over children of anchor a
// see if any are images
if (a === null) { return false; }
var kids=a.childNodes;
for (var i=0; i<kids.length; ++i) { if (kids[i].nodeName=='IMG') { return true; } }
return false;
}
//</NOLITE>
function isPopupLink(a) {
// NB for performance reasons, TOC links generally return true
// they should be stripped out later
if (!markNopopupSpanLinks.done) { markNopopupSpanLinks(); }
if (a.inNopopupSpan) { return false; }
// FIXME is this faster inline?
if (a.onmousedown || a.getAttribute('nopopup')) { return false; }
var h=a.href;
if (h === document.location.href+'#') { return false; }
if (!pg.re.basenames.test(h)) { return false; }
if (!pg.re.urlNoPopup.test(h)) { return true; }
return (
(pg.re.email.test(h) || pg.re.contribs.test(h) || pg.re.backlinks.test(h) || pg.re.specialdiff.test(h)) &&
h.indexOf('&limit=') == -1 );
}
function markNopopupSpanLinks() {
if( !getValueOf('popupOnlyArticleLinks'))
fixVectorMenuPopups();
var s = $('.nopopups').toArray();
for (var i=0; i<s.length; ++i) {
var as=s[i].getElementsByTagName('a');
for (var j=0; j<as.length; ++j) {
as[j].inNopopupSpan=true;
}
}
markNopopupSpanLinks.done=true;
}
function fixVectorMenuPopups() {
$('div.vectorMenu h3:first a:first').prop('inNopopupSpan', true);
}
// ENDFILE: titles.js
// STARTFILE: getpage.js
//////////////////////////////////////////////////
// Wiki-specific downloading
//
// Schematic for a getWiki call
//
// getPageWithCaching
// |
// false | true
// getPage<-[findPictureInCache]->-onComplete(a fake download)
// \.
// (async)->addPageToCache(download)->-onComplete(download)
// check cache to see if page exists
function getPageWithCaching(url, onComplete, owner) {
log('getPageWithCaching, url='+url);
var i=findInPageCache(url);
var d;
if (i > -1) {
d=fakeDownload(url, owner.idNumber, onComplete,
pg.cache.pages[i].data, pg.cache.pages[i].lastModified,
owner);
} else {
d=getPage(url, onComplete, owner);
if (d && owner && owner.addDownload) {
owner.addDownload(d);
d.owner=owner;
}
}
}
function getPage(url, onComplete, owner) {
log('getPage');
var callback= function (d) { if (!d.aborted) {addPageToCache(d); onComplete(d);} };
return startDownload(url, owner.idNumber, callback);
}
function findInPageCache(url) {
for (var i=0; i<pg.cache.pages.length; ++i) {
if (url==pg.cache.pages[i].url) { return i; }
}
return -1;
}
function addPageToCache(download) {
log('addPageToCache '+download.url);
var page = {url: download.url, data: download.data, lastModified: download.lastModified};
return pg.cache.pages.push(page);
}
// ENDFILE: getpage.js
// STARTFILE: parensplit.js
//////////////////////////////////////////////////
// parenSplit
// String.prototype.parenSplit should do what ECMAscript says String.prototype.split does,
// interspersing paren matches (regex capturing groups) between the split elements.
// i.e. 'abc'.split(/(b)/)) should return ['a','b','c'], not ['a','c']
if (String('abc'.split(/(b)/))!='a,b,c') {
// broken String.split, e.g. konq, IE < 10
String.prototype.parenSplit=function (re) {
re=nonGlobalRegex(re);
var s=this;
var m=re.exec(s);
var ret=[];
while (m && s) {
// without the following loop, we have
// 'ab'.parenSplit(/a|(b)/) != 'ab'.split(/a|(b)/)
for(var i=0; i<m.length; ++i) {
if (typeof m[i]=='undefined') m[i]='';
}
ret.push(s.substring(0,m.index));
ret = ret.concat(m.slice(1));
s=s.substring(m.index + m[0].length);
m=re.exec(s);
}
ret.push(s);
return ret;
};
} else {
String.prototype.parenSplit=function (re) { return this.split(re); };
String.prototype.parenSplit.isNative=true;
}
function nonGlobalRegex(re) {
var s=re.toString();
var flags='';
for (var j=s.length; s.charAt(j) != '/'; --j) {
if (s.charAt(j) != 'g') { flags += s.charAt(j); }
}
var t=s.substring(1,j);
return RegExp(t,flags);
}
// ENDFILE: parensplit.js
// STARTFILE: tools.js
// IE madness with encoding
// ========================
//
// suppose throughout that the page is in utf8, like wikipedia
//
// if a is an anchor DOM element and a.href should consist of
//
// http://host.name.here/wiki/foo?bar=baz
//
// then IE gives foo as "latin1-encoded" utf8; we have foo = decode_utf8(decodeURI(foo_ie))
// but IE gives bar=baz correctly as plain utf8
//
// ---------------------------------
//
// IE's xmlhttp doesn't understand utf8 urls. Have to use encodeURI here.
//
// ---------------------------------
//
// summat else
// Source: http://aktuell.de.selfhtml.org/artikel/javascript/utf8b64/utf8.htm
//<NOLITE>
function getJsObj(json) {
try {
var json_ret = JSON.parse(json);
if( json_ret.warnings ) {
for( var w=0; w < json_ret.warnings.length; w++ ) {
if( json_ret.warnings[w]['*'] ) {
log( json_ret.warnings[w]['*'] );
} else {
log( json_ret.warnings[w]['warnings'] );
}
}
} else if ( json_ret.error ) {
errlog( json_ret.error.code + ': ' + json_ret.error.info );
}
return json_ret;
} catch (someError) {
errlog('Something went wrong with getJsObj, json='+json);
return 1;
}
}
function anyChild(obj) {
for (var p in obj) {
return obj[p];
}
return null;
}
//</NOLITE>
function upcaseFirst(str) {
if (typeof str != typeof '' || str === '') return '';
return str.charAt(0).toUpperCase() + str.substring(1);
}
function findInArray(arr, foo) {
if (!arr || !arr.length) { return -1; }
var len=arr.length;
for (var i=0; i<len; ++i) { if (arr[i]==foo) { return i; } }
return -1;
}
/* eslint-disable no-unused-vars */
function nextOne (array, value) {
// NB if the array has two consecutive entries equal
// then this will loop on successive calls
var i=findInArray(array, value);
if (i<0) { return null; }
return array[i+1];
}
/* eslint-enable no-unused-vars */
function literalizeRegex(str){
return mw.util.escapeRegExp(str);
}
String.prototype.entify=function() {
//var shy='­';
return this.split('&').join('&').split('<').join('<').split('>').join('>'/*+shy*/).split('"').join('"');
};
// Array filter function
function removeNulls(val) { return val !== null; }
function joinPath(list) {
return list.filter(removeNulls).join('/');
}
function simplePrintf(str, subs) {
if (!str || !subs) { return str; }
var ret=[];
var s=str.parenSplit(/(%s|\$[0-9]+)/);
var i=0;
do {
ret.push(s.shift());
if ( !s.length ) { break; }
var cmd=s.shift();
if (cmd == '%s') {
if ( i < subs.length ) { ret.push(subs[i]); } else { ret.push(cmd); }
++i;
} else {
var j=parseInt( cmd.replace('$', ''), 10 ) - 1;
if ( j > -1 && j < subs.length ) { ret.push(subs[j]); } else { ret.push(cmd); }
}
} while (s.length > 0);
return ret.join('');
}
/* eslint-disable no-unused-vars */
function isString(x) { return (typeof x === 'string' || x instanceof String); }
function isNumber(x) { return (typeof x === 'number' || x instanceof Number); }
function isRegExp(x) { return x instanceof RegExp; }
function isArray (x) { return x instanceof Array; }
function isObject(x) { return x instanceof Object; }
function isFunction(x) {
return !isRegExp(x) && (typeof x === 'function' || x instanceof Function);
}
/* eslint-enable no-unused-vars */
function repeatString(s,mult) {
var ret='';
for (var i=0; i<mult; ++i) { ret += s; }
return ret;
}
function zeroFill(s, min) {
min = min || 2;
var t=s.toString();
return repeatString('0', min - t.length) + t;
}
function map(f, o) {
if (isArray(o)) { return map_array(f,o); }
return map_object(f,o);
}
function map_array(f,o) {
var ret=[];
for (var i=0; i<o.length; ++i) {
ret.push(f(o[i]));
}
return ret;
}
function map_object(f,o) {
var ret={};
for (var i in o) { ret[o]=f(o[i]); }
return ret;
}
pg.escapeQuotesHTML = function ( text ) {
return text
.replace(/&/g, "&")
.replace(/"/g, """)
.replace(/</g, "<")
.replace(/>/g, ">");
};
// ENDFILE: tools.js
// STARTFILE: dab.js
//<NOLITE>
//////////////////////////////////////////////////
// Dab-fixing code
//
function retargetDab(newTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) {
log('retargetDab: newTarget='+newTarget + ' oldTarget=' + oldTarget);
return changeLinkTargetLink(
{newTarget: newTarget,
text: newTarget.split(' ').join(' '),
hint: tprintf('disambigHint', [newTarget]),
summary: simplePrintf(
getValueOf('popupFixDabsSummary'), [friendlyCurrentArticleName, newTarget ]),
clickButton: getValueOf('popupDabsAutoClick'), minor: true, oldTarget: oldTarget,
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit});
}
function listLinks(wikitext, oldTarget, titleToEdit) {
// mediawiki strips trailing spaces, so we do the same
// testcase: https://en.wikipedia.org/w/index.php?title=Radial&oldid=97365633
var reg=RegExp('\\[\\[([^|]*?) *(\\||\\]\\])', 'gi');
var ret=[];
var splitted=wikitext.parenSplit(reg);
// ^[a-z]+ should match interwiki links, hopefully (case-insensitive)
// and ^[a-z]* should match those and [[:Category...]] style links too
var omitRegex=RegExp('^[a-z]*:|^[Ss]pecial:|^[Ii]mage|^[Cc]ategory');
var friendlyCurrentArticleName= oldTarget.toString();
var wikPos = getValueOf('popupDabWiktionary');
for (var i=1; i<splitted.length; i=i+3) {
if (typeof splitted[i] == typeof 'string' && splitted[i].length>0 && !omitRegex.test(splitted[i])) {
ret.push( retargetDab(splitted[i], oldTarget, friendlyCurrentArticleName, titleToEdit) );
} /* if */
} /* for loop */
ret = rmDupesFromSortedList(ret.sort());
if (wikPos) {
var wikTarget='wiktionary:' +
friendlyCurrentArticleName.replace( RegExp('^(.+)\\s+[(][^)]+[)]\\s*$'), '$1' );
var meth;
if (wikPos.toLowerCase() == 'first') { meth = 'unshift'; }
else { meth = 'push'; }
ret[meth]( retargetDab(wikTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) );
}
ret.push(changeLinkTargetLink(
{ newTarget: null,
text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this disambig page from this article"),
clickButton: getValueOf('popupDabsAutoClick'), oldTarget: oldTarget,
summary: simplePrintf(getValueOf('popupRmDabLinkSummary'), [friendlyCurrentArticleName]),
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit
}));
return ret;
}
function rmDupesFromSortedList(list) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (ret.length === 0 || list[i]!=ret[ret.length-1]) { ret.push(list[i]); }
}
return ret;
}
function makeFixDab(data, navpop) {
// grab title from parent popup if there is one; default exists in changeLinkTargetLink
var titleToEdit=(navpop.parentPopup && navpop.parentPopup.article.toString());
var list=listLinks(data, navpop.originalArticle, titleToEdit);
if (list.length === 0) { log('listLinks returned empty list'); return null; }
var html='<hr />' + popupString('Click to disambiguate this link to:') + '<br>';
html+=list.join(', ');
return html;
}
function makeFixDabs(wikiText, navpop) {
if (getValueOf('popupFixDabs') && isDisambig(wikiText, navpop.article) &&
Title.fromURL(location.href).namespaceId() != pg.nsSpecialId &&
navpop.article.talkPage() ) {
setPopupHTML(makeFixDab(wikiText, navpop), 'popupFixDab', navpop.idNumber);
}
}
function popupRedlinkHTML(article) {
return changeLinkTargetLink(
{ newTarget: null, text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this page from this article"),
clickButton: getValueOf('popupRedlinkAutoClick'),
oldTarget: article.toString(),
summary: simplePrintf(getValueOf('popupRedlinkSummary'), [article.toString()])});
}
//</NOLITE>
// ENDFILE: dab.js
// STARTFILE: htmloutput.js
// this has to use a timer loop as we don't know if the DOM element exists when we want to set the text
function setPopupHTML (str, elementId, popupId, onSuccess, append) {
if (typeof popupId === 'undefined') {
//console.error('popupId is not defined in setPopupHTML, html='+str.substring(0,100));
popupId = pg.idNumber;
}
var popupElement=document.getElementById(elementId+popupId);
if (popupElement) {
if (!append) { popupElement.innerHTML=''; }
if (isString(str)) {
popupElement.innerHTML+=str;
} else {
popupElement.appendChild(str);
}
if (onSuccess) { onSuccess(); }
setTimeout(checkPopupPosition, 100);
return true;
} else {
// call this function again in a little while...
setTimeout(function(){
setPopupHTML(str,elementId,popupId,onSuccess);
}, 600);
}
return null;
}
//<NOLITE>
function setPopupTrailer(str,id) {return setPopupHTML(str, 'popupData', id);}
//</NOLITE>
// args.navpopup is mandatory
// optional: args.redir, args.redirTarget
// FIXME: ye gods, this is ugly stuff
function fillEmptySpans(args) {
// if redir is present and true then redirTarget is mandatory
var redir=true;
var rcid;
if (typeof args != 'object' || typeof args.redir == 'undefined' || !args.redir) { redir=false; }
var a=args.navpopup.parentAnchor;
var article, hint=null, oldid=null, params={};
if (redir && typeof args.redirTarget == typeof {}) {
article=args.redirTarget;
//hint=article.hintValue();
} else {
article=(new Title()).fromAnchor(a);
hint=a.originalTitle || article.hintValue();
params=parseParams(a.href);
oldid=(getValueOf('popupHistoricalLinks')) ? params.oldid : null;
rcid=params.rcid;
}
var x={ article:article, hint: hint, oldid: oldid, rcid: rcid, navpop:args.navpopup, params:params };
var structure=pg.structures[getValueOf('popupStructure')];
if (typeof structure != 'object') {
setPopupHTML('popupError', 'Unknown structure (this should never happen): '+
pg.option.popupStructure, args.navpopup.idNumber);
return;
}
var spans=flatten(pg.misc.layout);
var numspans = spans.length;
var redirs=pg.misc.redirSpans;
for (var i=0; i<numspans; ++i) {
var found = redirs && (redirs.indexOf( spans[i] ) !== -1);
//log('redir='+redir+', found='+found+', spans[i]='+spans[i]);
if ( (found && !redir) || (!found && redir) ) {
//log('skipping this set of the loop');
continue;
}
var structurefn=structure[spans[i]];
var setfn = setPopupHTML;
if (getValueOf('popupActiveNavlinks') &&
(spans[i].indexOf('popupTopLinks')===0 || spans[i].indexOf('popupRedirTopLinks')===0)
) {
setfn = setPopupTipsAndHTML;
}
switch (typeof structurefn) {
case 'function':
log('running '+spans[i]+'({article:'+x.article+', hint:'+x.hint+', oldid: '+x.oldid+'})');
setfn(structurefn(x), spans[i], args.navpopup.idNumber);
break;
case 'string':
setfn(structurefn, spans[i], args.navpopup.idNumber);
break;
default:
errlog('unknown thing with label '+spans[i] + ' (span index was ' + i + ')');
break;
}
}
}
// flatten an array
function flatten(list, start) {
var ret=[];
if (typeof start == 'undefined') { start=0; }
for (var i=start; i<list.length; ++i) {
if (typeof list[i] == typeof []) {
return ret.concat(flatten(list[i])).concat(flatten(list, i+1));
}
else { ret.push(list[i]); }
}
return ret;
}
// Generate html for whole popup
function popupHTML (a) {
getValueOf('popupStructure');
var structure=pg.structures[pg.option.popupStructure];
if (typeof structure != 'object') {
//return 'Unknown structure: '+pg.option.popupStructure;
// override user choice
pg.option.popupStructure=pg.optionDefault.popupStructure;
return popupHTML(a);
}
if (typeof structure.popupLayout != 'function') { return 'Bad layout'; }
pg.misc.layout=structure.popupLayout();
if (typeof structure.popupRedirSpans === 'function') { pg.misc.redirSpans=structure.popupRedirSpans(); }
else { pg.misc.redirSpans=[]; }
return makeEmptySpans(pg.misc.layout, a.navpopup);
}
function makeEmptySpans (list, navpop) {
var ret='';
for (var i=0; i<list.length; ++i) {
if (typeof list[i] == typeof '') {
ret += emptySpanHTML(list[i], navpop.idNumber, 'div');
} else if (typeof list[i] == typeof [] && list[i].length > 0 ) {
ret = ret.parenSplit(RegExp('(</[^>]*?>$)')).join(makeEmptySpans(list[i], navpop));
} else if (typeof list[i] == typeof {} && list[i].nodeType ) {
ret += emptySpanHTML(list[i].name, navpop.idNumber, list[i].nodeType);
}
}
return ret;
}
function emptySpanHTML(name, id, tag, classname) {
tag = tag || 'span';
if (!classname) { classname = emptySpanHTML.classAliases[name]; }
classname = classname || name;
if (name == getValueOf('popupDragHandle')) { classname += ' popupDragHandle'; }
return simplePrintf('<%s id="%s" class="%s"></%s>', [tag, name + id, classname, tag]);
}
emptySpanHTML.classAliases={ 'popupSecondPreview': 'popupPreview' };
// generate html for popup image
// <a id="popupImageLinkn"><img id="popupImagen">
// where n=idNumber
function imageHTML(article, idNumber) {
return simplePrintf('<a id="popupImageLink$1">' +
'<img align="right" valign="top" id="popupImg$1" style="display: none;"></img>' +
'</a>', [ idNumber ]);
}
function popTipsSoonFn(id, when, popData) {
if (!when) { when=250; }
var popTips=function(){ setupTooltips(document.getElementById(id), false, true, popData); };
return function() { setTimeout( popTips, when, popData ); };
}
function setPopupTipsAndHTML(html, divname, idnumber, popData) {
setPopupHTML(html, divname, idnumber,
getValueOf('popupSubpopups') ?
popTipsSoonFn(divname + idnumber, null, popData) :
null);
}
// ENDFILE: htmloutput.js
// STARTFILE: mouseout.js
//////////////////////////////////////////////////
// fuzzy checks
function fuzzyCursorOffMenus(x,y, fuzz, parent) {
if (!parent) { return null; }
var uls=parent.getElementsByTagName('ul');
for (var i=0; i<uls.length; ++i) {
if (uls[i].className=='popup_menu') {
if (uls[i].offsetWidth > 0) return false;
} // else {document.title+='.';}
}
return true;
}
function checkPopupPosition () { // stop the popup running off the right of the screen
// FIXME avoid pg.current.link
if (pg.current.link && pg.current.link.navpopup)
pg.current.link.navpopup.limitHorizontalPosition();
}
function mouseOutWikiLink () {
//console ('mouseOutWikiLink');
var a=this;
removeModifierKeyHandler(a);
if (a.navpopup === null || typeof a.navpopup === 'undefined') return;
if ( ! a.navpopup.isVisible() ) {
a.navpopup.banish();
return;
}
restoreTitle(a);
Navpopup.tracker.addHook(posCheckerHook(a.navpopup));
}
function posCheckerHook(navpop) {
return function() {
if (!navpop.isVisible()) { return true; /* remove this hook */ }
if (Navpopup.tracker.dirty) {
return false;
}
var x=Navpopup.tracker.x, y=Navpopup.tracker.y;
var mouseOverNavpop = navpop.isWithin(x,y,navpop.fuzz, navpop.mainDiv) ||
!fuzzyCursorOffMenus(x,y,navpop.fuzz, navpop.mainDiv);
// FIXME it'd be prettier to do this internal to the Navpopup objects
var t=getValueOf('popupHideDelay');
if (t) { t = t * 1000; }
if (!t) {
if(!mouseOverNavpop) {
if(navpop.parentAnchor) {
restoreTitle( navpop.parentAnchor );
}
navpop.banish();
return true; /* remove this hook */
}
return false;
}
// we have a hide delay set
var d=+(new Date());
if ( !navpop.mouseLeavingTime ) {
navpop.mouseLeavingTime = d;
return false;
}
if ( mouseOverNavpop ) {
navpop.mouseLeavingTime=null;
return false;
}
if (d - navpop.mouseLeavingTime > t) {
navpop.mouseLeavingTime=null;
navpop.banish(); return true; /* remove this hook */
}
return false;
};
}
function runStopPopupTimer(navpop) {
// at this point, we should have left the link but remain within the popup
// so we call this function again until we leave the popup.
if (!navpop.stopPopupTimer) {
navpop.stopPopupTimer=setInterval(posCheckerHook(navpop), 500);
navpop.addHook(function(){clearInterval(navpop.stopPopupTimer);},
'hide', 'before');
}
}
// ENDFILE: mouseout.js
// STARTFILE: previewmaker.js
/**
@fileoverview
Defines the {@link Previewmaker} object, which generates short previews from wiki markup.
*/
/**
Creates a new Previewmaker
@constructor
@class The Previewmaker class. Use an instance of this to generate short previews from Wikitext.
@param {String} wikiText The Wikitext source of the page we wish to preview.
@param {String} baseUrl The url we should prepend when creating relative urls.
@param {Navpopup} owner The navpop associated to this preview generator
*/
function Previewmaker(wikiText, baseUrl, owner) {
/** The wikitext which is manipulated to generate the preview. */
this.originalData=wikiText;
this.baseUrl=baseUrl;
this.owner=owner;
this.maxCharacters=getValueOf('popupMaxPreviewCharacters');
this.maxSentences=getValueOf('popupMaxPreviewSentences');
this.setData();
}
Previewmaker.prototype.setData=function() {
var maxSize=Math.max(10000, 2*this.maxCharacters);
this.data=this.originalData.substring(0,maxSize);
};
/** Remove HTML comments
@private
*/
Previewmaker.prototype.killComments = function () {
// this also kills one trailing newline, eg [[diamyo]]
this.data=this.data.replace(RegExp('^<!--[^$]*?-->\\n|\\n<!--[^$]*?-->(?=\\n)|<!--[^$]*?-->', 'g'), '');
};
/**
@private
*/
Previewmaker.prototype.killDivs = function () {
// say goodbye, divs (can be nested, so use * not *?)
this.data=this.data.replace(RegExp('< *div[^>]* *>[\\s\\S]*?< */ *div *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killGalleries = function () {
this.data=this.data.replace(RegExp('< *gallery[^>]* *>[\\s\\S]*?< */ *gallery *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.kill = function(opening, closing, subopening, subclosing, repl) {
var oldk=this.data;
var k=this.killStuff(this.data, opening, closing, subopening, subclosing, repl);
while (k.length < oldk.length) {
oldk=k;
k=this.killStuff(k, opening, closing, subopening, subclosing, repl);
}
this.data=k;
};
/**
@private
*/
Previewmaker.prototype.killStuff = function (txt, opening, closing, subopening, subclosing, repl) {
var op=this.makeRegexp(opening);
var cl=this.makeRegexp(closing, '^');
var sb=subopening ? this.makeRegexp(subopening, '^') : null;
var sc=subclosing ? this.makeRegexp(subclosing, '^') : cl;
if (!op || !cl) {
alert('Navigation Popups error: op or cl is null! something is wrong.');
return;
}
if (!op.test(txt)) { return txt; }
var ret='';
var opResult = op.exec(txt);
ret = txt.substring(0,opResult.index);
txt=txt.substring(opResult.index+opResult[0].length);
var depth = 1;
while (txt.length > 0) {
var removal=0;
if (depth==1 && cl.test(txt)) {
depth--;
removal=cl.exec(txt)[0].length;
} else if (depth > 1 && sc.test(txt)) {
depth--;
removal=sc.exec(txt)[0].length;
}else if (sb && sb.test(txt)) {
depth++;
removal=sb.exec(txt)[0].length;
}
if ( !removal ) { removal = 1; }
txt=txt.substring(removal);
if (depth === 0) { break; }
}
return ret + (repl || '') + txt;
};
/**
@private
*/
Previewmaker.prototype.makeRegexp = function (x, prefix, suffix) {
prefix = prefix || '';
suffix = suffix || '';
var reStr='';
var flags='';
if (isString(x)) {
reStr=prefix + literalizeRegex(x) + suffix;
} else if (isRegExp(x)) {
var s=x.toString().substring(1);
var sp=s.split('/');
flags=sp[sp.length-1];
sp[sp.length-1]='';
s=sp.join('/');
s=s.substring(0,s.length-1);
reStr= prefix + s + suffix;
} else {
log ('makeRegexp failed');
}
log ('makeRegexp: got reStr=' + reStr + ', flags=' + flags);
return RegExp(reStr, flags);
};
/**
@private
*/
Previewmaker.prototype.killBoxTemplates = function () {
// taxobox removal... in fact, there's a saudiprincebox_begin, so let's be more general
// also, have float_begin, ... float_end
this.kill(RegExp('[{][{][^{}\\s|]*?(float|box)[_ ](begin|start)', 'i'), /[}][}]\s*/, '{{');
// infoboxes etc
// from User:Zyxw/popups.js: kill frames too
this.kill(RegExp('[{][{][^{}\\s|]*?(infobox|elementbox|frame)[_ ]', 'i'), /[}][}]\s*/, '{{');
};
/**
@private
*/
Previewmaker.prototype.killTemplates = function () {
// the plus sign is to avoid transcluding [[Template:', ']]
this.kill('{'+'{', '}'+'}', '{', '}', ' ');
};
/**
@private
*/
Previewmaker.prototype.killTables = function () {
// tables are bad, too
// this can be slow, but it's an inprovement over a browser hang
// torture test: [[Comparison_of_Intel_Central_Processing_Units]]
this.kill('{|', /[|]}\s*/, '{|');
this.kill(/<table.*?>/i, /<\/table.*?>/i, /<table.*?>/i);
// remove lines starting with a pipe for the hell of it (?)
this.data=this.data.replace(RegExp('^[|].*$', 'mg'), '');
};
/**
@private
*/
Previewmaker.prototype.killImages = function () {
var forbiddenNamespaceAliases = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=pg.nsImageId && _namespaceId!=pg.nsCategoryId) return;
forbiddenNamespaceAliases.push(_localizedNamespaceLc.split(' ').join('[ _]')); //todo: escape regexp fragments!
});
// images and categories are a nono
this.kill(RegExp('[[][[]\\s*(' + forbiddenNamespaceAliases.join('|') + ')\\s*:', 'i'),
/\]\]\s*/, '[', ']');
};
/**
@private
*/
Previewmaker.prototype.killHTML = function () {
// kill <ref ...>...</ref>
this.kill(/<ref\b[^/>]*?>/i, /<\/ref>/i);
// let's also delete entire lines starting with <. it's worth a try.
this.data=this.data.replace(RegExp('(^|\\n) *<.*', 'g'), '\n');
// and those pesky html tags, but not <nowiki> or <blockquote>
var splitted=this.data.parenSplit(/(<[\w\W]*?(?:>|$|(?=<)))/);
var len=splitted.length;
for (var i=1; i<len; i=i+2) {
switch (splitted[i]) {
case '<nowiki>':
case '</nowiki>':
case '<blockquote>':
case '</blockquote>':
break;
default:
splitted[i]='';
}
}
this.data=splitted.join('');
};
/**
@private
*/
Previewmaker.prototype.killChunks = function() { // heuristics alert
// chunks of italic text? you crazy, man?
var italicChunkRegex=new RegExp
("((^|\\n)\\s*:*\\s*''[^']([^']|'''|'[^']){20}(.|\\n[^\\n])*''[.!?\\s]*\\n)+", 'g');
// keep stuff separated, though, so stick in \n (fixes [[Union Jack]]?
this.data=this.data.replace(italicChunkRegex, '\n');
};
/**
@private
*/
Previewmaker.prototype.mopup = function () {
// we simply *can't* be doing with horizontal rules right now
this.data=this.data.replace(RegExp('^-{4,}','mg'),'');
// no indented lines
this.data=this.data.replace(RegExp('(^|\\n) *:[^\\n]*','g'), '');
// replace __TOC__, __NOTOC__ and whatever else there is
// this'll probably do
this.data=this.data.replace(RegExp('^__[A-Z_]*__ *$', 'gmi'),'');
};
/**
@private
*/
Previewmaker.prototype.firstBit = function () {
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d=this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data=this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data=this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data=this.data.replace(RegExp('^[\\s\\n]*'), '');
var stuff=(RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) { d = stuff[0]; }
if (!getValueOf('popupPreviewFirstParOnly')) { d = this.data; }
/// now put \n\n after sections so that bullets and numbered lists work
d=d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// Split sentences. Superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["'+"'"+']*\\s)','g'));
// leading space is bad, mmkay?
d[0]=d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds=RegExp('([^.][a-z][.] *[a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|op|cit|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
this.fullLength=d.join('').length;
var n=this.maxSentences;
var dd=this.firstSentences(d,n);
do {
dd=this.firstSentences(d,n); --n;
} while ( dd.length > this.maxCharacters && n !== 0 );
this.data = dd;
};
/**
@private
*/
Previewmaker.prototype.fixSentenceEnds = function(strs, reg) {
// take an array of strings, strs
// join strs[i] to strs[i+1] & strs[i+2] if strs[i] matches regex reg
for (var i=0; i<strs.length-2; ++i) {
if (reg.test(strs[i])) {
var a=[];
for (var j=0; j<strs.length; ++j) {
if (j<i) a[j]=strs[j];
if (j==i) a[i]=strs[i]+strs[i+1]+strs[i+2];
if (j>i+2) a[j-2]=strs[j];
}
return this.fixSentenceEnds(a,reg);
}
}
return strs;
};
/**
@private
*/
Previewmaker.prototype.firstSentences = function(strs, howmany) {
var t=strs.slice(0, 2*howmany);
return t.join('');
};
/**
@private
*/
Previewmaker.prototype.killBadWhitespace = function() {
// also cleans up isolated '''', eg [[Suntory Sungoliath]]
this.data=this.data.replace(RegExp('^ *\'+ *$', 'gm'), '');
};
/**
Runs the various methods to generate the preview.
The preview is stored in the <code>html</html> field.
@private
*/
Previewmaker.prototype.makePreview = function() {
if (this.owner.article.namespaceId()!=pg.nsTemplateId &&
this.owner.article.namespaceId()!=pg.nsImageId ) {
this.killComments();
this.killDivs();
this.killGalleries();
this.killBoxTemplates();
if (getValueOf('popupPreviewKillTemplates')) {
this.killTemplates();
} else {
this.killMultilineTemplates();
}
this.killTables();
this.killImages();
this.killHTML();
this.killChunks();
this.mopup();
this.firstBit();
this.killBadWhitespace();
}
else
{
this.killHTML();
}
this.html=wiki2html(this.data, this.baseUrl); // needs livepreview
this.fixHTML();
this.stripLongTemplates();
};
/**
@private
*/
Previewmaker.prototype.esWiki2HtmlPart = function(data) {
var reLinks = /(?:\[\[([^|\]]*)(?:\|([^|\]]*))*]]([a-z]*))/gi; //match a wikilink
reLinks.lastIndex = 0; //reset regex
var match;
var result = "";
var postfixIndex = 0;
while ((match = reLinks.exec(data))) //match all wikilinks
{
//FIXME: the way that link is built here isn't perfect. It is clickable, but popups preview won't recognize it in some cases.
result += pg.escapeQuotesHTML(data.substring(postfixIndex, match.index)) +
'<a href="'+Insta.conf.paths.articles+pg.escapeQuotesHTML(match[1])+'">'+pg.escapeQuotesHTML((match[2]?match[2]:match[1])+match[3])+"</a>";
postfixIndex = reLinks.lastIndex;
}
//append the rest
result += pg.escapeQuotesHTML(data.substring(postfixIndex));
return result;
};
Previewmaker.prototype.editSummaryPreview=function() {
var reAes = /\/\* *(.*?) *\*\//g; //match the first section marker
reAes.lastIndex = 0; //reset regex
var match;
match = reAes.exec(this.data);
if (match)
{
//we have a section link. Split it, process it, combine it.
var prefix = this.data.substring(0,match.index-1);
var section = match[1];
var postfix = this.data.substring(reAes.lastIndex);
var start = "<span class='autocomment'>";
var end = "</span>";
if (prefix.length>0) start = this.esWiki2HtmlPart(prefix) + " " + start + "- ";
if (postfix.length>0) end = ": " + end + this.esWiki2HtmlPart(postfix);
var t=new Title().fromURL(this.baseUrl);
t.anchorFromUtf(section);
var sectionLink = Insta.conf.paths.articles + pg.escapeQuotesHTML(t.toString(true)) + '#' + pg.escapeQuotesHTML(t.anchor);
return start + '<a href="'+sectionLink+'">→</a> '+pg.escapeQuotesHTML(section) + end;
}
//else there's no section link, htmlify the whole thing.
return this.esWiki2HtmlPart(this.data);
};
//<NOLITE>
/** Test function for debugging preview problems one step at a time.
*/
/*eslint-disable */
function previewSteps(txt) {
try {
txt=txt || document.editform.wpTextbox1.value;
} catch (err) {
if (pg.cache.pages.length > 0) {
txt=pg.cache.pages[pg.cache.pages.length-1].data;
} else {
alert('provide text or use an edit page');
}
}
txt=txt.substring(0,10000);
var base=pg.wiki.articlebase + Title.fromURL(document.location.href).urlString();
var p=new Previewmaker(txt, base, pg.current.link.navpopup);
if (this.owner.article.namespaceId() != pg.nsTemplateId) {
p.killComments(); if (!confirm('done killComments(). Continue?\n---\n' + p.data)) { return; }
p.killDivs(); if (!confirm('done killDivs(). Continue?\n---\n' + p.data)) { return; }
p.killGalleries(); if (!confirm('done killGalleries(). Continue?\n---\n' + p.data)) { return; }
p.killBoxTemplates(); if (!confirm('done killBoxTemplates(). Continue?\n---\n' + p.data)) { return; }
if (getValueOf('popupPreviewKillTemplates')) {
p.killTemplates(); if (!confirm('done killTemplates(). Continue?\n---\n' + p.data)) { return; }
} else {
p.killMultilineTemplates(); if (!confirm('done killMultilineTemplates(). Continue?\n---\n' + p.data)) { return; }
}
p.killTables(); if (!confirm('done killTables(). Continue?\n---\n' + p.data)) { return; }
p.killImages(); if (!confirm('done killImages(). Continue?\n---\n' + p.data)) { return; }
p.killHTML(); if (!confirm('done killHTML(). Continue?\n---\n' + p.data)) { return; }
p.killChunks(); if (!confirm('done killChunks(). Continue?\n---\n' + p.data)) { return; }
p.mopup(); if (!confirm('done mopup(). Continue?\n---\n' + p.data)) { return; }
p.firstBit(); if (!confirm('done firstBit(). Continue?\n---\n' + p.data)) { return; }
p.killBadWhitespace(); if (!confirm('done killBadWhitespace(). Continue?\n---\n' + p.data)) { return; }
}
p.html=wiki2html(p.data, base); // needs livepreview
p.fixHTML(); if (!confirm('done fixHTML(). Continue?\n---\n' + p.html)) { return; }
p.stripLongTemplates(); if (!confirm('done stripLongTemplates(). Continue?\n---\n' + p.html)) { return; }
alert('finished preview - end result follows.\n---\n' + p.html);
}
/*eslint-enable */
//</NOLITE>
/**
Works around livepreview bugs.
@private
*/
Previewmaker.prototype.fixHTML = function() {
if(!this.html) return;
var ret = this.html;
// fix question marks in wiki links
// maybe this'll break some stuff :-(
ret=ret.replace(RegExp('(<a href="' + pg.wiki.articlePath + '/[^"]*)[?](.*?")', 'g'), '$1%3F$2');
ret=ret.replace(RegExp('(<a href=\'' + pg.wiki.articlePath + '/[^\']*)[?](.*?\')', 'g'), '$1%3F$2');
// FIXME fix up % too
this.html=ret;
};
/**
Generates the preview and displays it in the current popup.
Does nothing if the generated preview is invalid or consists of whitespace only.
Also activates wikilinks in the preview for subpopups if the popupSubpopups option is true.
*/
Previewmaker.prototype.showPreview = function () {
this.makePreview();
if (typeof this.html != typeof '') return;
if (RegExp('^\\s*$').test(this.html)) return;
setPopupHTML('<hr />', 'popupPrePreviewSep', this.owner.idNumber);
setPopupTipsAndHTML(this.html, 'popupPreview', this.owner.idNumber, { owner: this.owner });
var more = (this.fullLength > this.data.length) ? this.moreLink() : '';
setPopupHTML(more, 'popupPreviewMore', this.owner.idNumber);
};
/**
@private
*/
Previewmaker.prototype.moreLink=function() {
var a=document.createElement('a');
a.className='popupMoreLink';
a.innerHTML=popupString('more...');
var savedThis=this;
a.onclick=function() {
savedThis.maxCharacters+=2000;
savedThis.maxSentences+=20;
savedThis.setData();
savedThis.showPreview();
};
return a;
};
/**
@private
*/
Previewmaker.prototype.stripLongTemplates = function() {
// operates on the HTML!
this.html=this.html.replace(RegExp('^.{0,1000}[{][{][^}]*?(<(p|br)( /)?>\\s*){2,}([^{}]*?[}][}])?', 'gi'), '');
this.html=this.html.split('\n').join(' '); // workaround for <pre> templates
this.html=this.html.replace(RegExp('[{][{][^}]*<pre>[^}]*[}][}]','gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killMultilineTemplates = function() {
this.kill('{{{', '}}}');
this.kill(RegExp('\\s*[{][{][^{}]*\\n'), '}}', '{{');
};
// ENDFILE: previewmaker.js
// STARTFILE: querypreview.js
function loadAPIPreview(queryType, article, navpop) {
var art=new Title(article).urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
var htmlGenerator=function(/*a, d*/){alert('invalid html generator');};
var usernameart = '';
switch (queryType) {
case 'history':
url += 'titles=' + art + '&prop=revisions&rvlimit=' +
getValueOf('popupHistoryPreviewLimit');
htmlGenerator=APIhistoryPreviewHTML;
break;
case 'category':
url += 'list=categorymembers&cmtitle=' + art;
htmlGenerator=APIcategoryPreviewHTML;
break;
case 'userinfo':
var username = new Title( article ).userName();
usernameart = encodeURIComponent( username );
if (pg.re.ipUser.test(username)) {
url += 'list=blocks&bkprop=range|restrictions&bkip=' + usernameart;
} else {
url += 'list=users|usercontribs&usprop=blockinfo|groups|editcount|registration|gender&ususers=' + usernameart + "&meta=globaluserinfo&guiprop=groups|unattached&guiuser="+ usernameart + "&uclimit=1&ucprop=timestamp&ucuser=" + usernameart;
}
htmlGenerator=APIuserInfoPreviewHTML;
break;
case 'contribs':
usernameart = encodeURIComponent( new Title( article ).userName() );
url += 'list=usercontribs&ucuser=' + usernameart +
'&uclimit=' + getValueOf('popupContribsPreviewLimit');
htmlGenerator=APIcontribsPreviewHTML;
break;
case 'imagepagepreview':
var trail='';
if (getValueOf('popupImageLinks')) { trail = '&list=imageusage&iutitle=' + art; }
url += 'titles=' + art + '&prop=revisions|imageinfo&rvprop=content' + trail;
htmlGenerator=APIimagepagePreviewHTML;
break;
case 'backlinks':
url += 'list=backlinks&bltitle=' + art;
htmlGenerator=APIbacklinksPreviewHTML;
break;
case 'revision':
if (article.oldid) {
url += 'revids=' + article.oldid;
} else {
url += 'titles=' + article.removeAnchor().urlString();
}
url += '&prop=revisions|pageprops|info|images|categories&rvprop=ids|timestamp|flags|comment|user|content&cllimit=max&imlimit=max';
htmlGenerator=APIrevisionPreviewHTML;
break;
}
pendingNavpopTask(navpop);
var callback=function(d){
log( "callback of API functions was hit" );
showAPIPreview(queryType, htmlGenerator(article,d,navpop), navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_'+queryType+'_QUERY_DATA'); }
}
function linkList(list) {
list.sort(function(x,y) { return (x==y ? 0 : (x<y ? -1 : 1)); });
var buf=[];
for (var i=0; i<list.length; ++i) {
buf.push(wikiLink({article: new Title(list[i]),
text: list[i].split(' ').join(' '),
action: 'view'}));
}
return buf.join(', ');
}
function getTimeOffset() {
var tz = mw.user.options.get('timecorrection');
if(tz) {
if( tz.indexOf('|') > -1 ) {
// New format
return parseInt(tz.split('|')[1],10);
} else if ( tz.indexOf(':') > -1 ) {
// Old format
return( parseInt(tz,10)*60 + parseInt(tz.split(':')[1],10) );
}
}
return 0;
}
/*
* Creates a HTML table that's shown in the history and user-contribs popups.
* @param {Object[]} h - a list of revisions, returned from the API
* @param {boolean} reallyContribs - true only if we're displaying user contributions
*/
function editPreviewTable(article, h, reallyContribs, timeOffset) {
var html=['<table>'];
var day=null;
var curart=article;
var page=null;
var makeFirstColumnLinks;
if(reallyContribs) {
// We're showing user contributions, so make (diff | hist) links
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&diff=prev' +
'&oldid=' + currentRevision.revid + '">' + popupString('diff') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&action=history">' +
popupString('hist') + '</a>';
result += ')';
return result;
};
} else {
// It's a regular history page, so make (cur | last) links
var firstRevid = h[0].revid;
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=' + firstRevid + '&oldid=' + currentRevision.revid + '">' + popupString('cur') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=prev&oldid=' + currentRevision.revid + '">' + popupString('last') + '</a>';
result += ')';
return result;
};
}
for (var i=0; i<h.length; ++i) {
if (reallyContribs) {
page = h[i].title;
curart = new Title(page);
}
var minor = h[i].minor ? '<b>m </b>' : '';
var editDate = adjustDate(getDateFromTimestamp(h[i].timestamp), timeOffset);
var thisDay = dayFormat(editDate);
var thisTime = timeFormat(editDate);
if (thisDay == day) {
thisDay = '';
} else {
day = thisDay;
}
if (thisDay) {
html.push( '<tr><td colspan=3><span class="popup_history_date">' +
thisDay+'</span></td></tr>' );
}
html.push('<tr class="popup_history_row_' + ( (i%2) ? 'odd' : 'even') + '">');
html.push('<td>' + makeFirstColumnLinks(h[i]) + '</td>');
html.push('<td>' +
'<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&oldid=' + h[i].revid + '">' + thisTime + '</a></td>');
var col3url='', col3txt='';
if (!reallyContribs) {
var user=h[i].user;
if( !h[i].userhidden ) {
if( pg.re.ipUser.test(user) ) {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Contributions&target=' + new Title(user).urlString();
} else {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(user).urlString();
}
col3txt=pg.escapeQuotesHTML(user);
} else {
col3url=getValueOf('popupRevDelUrl');
col3txt=pg.escapeQuotesHTML( popupString('revdel'));
}
} else {
col3url=pg.wiki.titlebase + curart.urlString();
col3txt=pg.escapeQuotesHTML(page);
}
html.push('<td>' + (reallyContribs ? minor : '') +
'<a href="' + col3url + '">' + col3txt + '</a></td>');
var comment='';
var c=h[i].comment || h[i].content;
if (c) {
comment=new Previewmaker(c, new Title(curart).toUrl()).editSummaryPreview();
} else if ( h[i].commenthidden ) {
comment=popupString('revdel');
}
html.push('<td>' + (!reallyContribs ? minor : '') + comment + '</td>');
html.push('</tr>');
html=[html.join('')];
}
html.push('</table>');
return html.join('');
}
function getDateFromTimestamp(t) {
var s=t.split(/[^0-9]/);
switch(s.length) {
case 0: return null;
case 1: return new Date(s[0]);
case 2: return new Date(s[0], s[1]-1);
case 3: return new Date(s[0], s[1]-1, s[2]);
case 4: return new Date(s[0], s[1]-1, s[2], s[3]);
case 5: return new Date(s[0], s[1]-1, s[2], s[3], s[4]);
case 6: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5]);
default: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5], s[6]);
}
}
function adjustDate(d, offset) {
// offset is in minutes
var o=offset * 60 * 1000;
return new Date( +d + o);
}
function dayFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCFullYear(), editDate.getUTCMonth()+1, editDate.getUTCDate()]).join('-'); }
return map(zeroFill, [editDate.getFullYear(), editDate.getMonth()+1, editDate.getDate()]).join('-');
}
function timeFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCHours(), editDate.getUTCMinutes(), editDate.getUTCSeconds()]).join(':'); }
return map(zeroFill, [editDate.getHours(), editDate.getMinutes(), editDate.getSeconds()]).join(':');
}
function showAPIPreview(queryType, html, id, navpop, download) {
// DJ: done
var target='popupPreview';
completedNavpopTask(navpop);
switch (queryType) {
case 'imagelinks':
case 'category':
target='popupPostPreview'; break;
case 'userinfo':
target='popupUserData'; break;
case 'revision':
insertPreview(download);
return;
}
setPopupTipsAndHTML(html, target, id);
}
function APIrevisionPreviewHTML(article, download) {
try{
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
if( page.missing ) {
// TODO we need to fix this proper later on
download.owner = null;
return;
}
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
download.data = content;
download.lastModified = new Date(page.revisions[0].timestamp);
}
} catch(someError) {
return 'Revision preview failed :(';
}
}
function APIbacklinksPreviewHTML(article, download/*, navpop*/ ) {
try {
var jsObj=getJsObj(download.data);
var list=jsObj.query.backlinks;
var html=[];
if (!list) { return popupString('No backlinks found'); }
for ( var i=0; i < list.length; i++ ) {
var t=new Title(list[i].title);
html.push('<a href="' + pg.wiki.titlebase + t.urlString() + '">' + t + '</a>');
}
html=html.join(', ');
if (jsObj['continue'] && jsObj['continue'].blcontinue) {
html += popupString(' and more');
}
return html;
} catch (someError) {
return 'backlinksPreviewHTML went wonky';
}
}
pg.fn.APIsharedImagePagePreviewHTML = function APIsharedImagePagePreviewHTML(obj) {
log( "APIsharedImagePagePreviewHTML" );
var popupid = obj.requestid;
if( obj.query && obj.query.pages )
{
var page=anyChild(obj.query.pages );
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
/* Not entirely safe, but the best we can do */
var p=new Previewmaker(content, pg.current.link.navpopup.article, pg.current.link.navpopup);
p.makePreview();
setPopupHTML( p.html, "popupSecondPreview", popupid );
}
}
};
function APIimagepagePreviewHTML(article, download, navpop) {
try {
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
var content= (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
var ret='';
var alt='';
try{alt=navpop.parentAnchor.childNodes[0].alt;} catch(e){}
if (alt) {
ret = ret + '<hr /><b>' + popupString('Alt text:') + '</b> ' + pg.escapeQuotesHTML(alt);
}
if (typeof content === 'string') {
var p=prepPreviewmaker(content, article, navpop);
p.makePreview();
if (p.html) { ret += '<hr />' + p.html; }
if (getValueOf('popupSummaryData')) {
var info=getPageInfo(content, download);
log(info);
setPopupTrailer(info, navpop.idNumber);
}
}
if (page && page.imagerepository == "shared" ) {
var art=new Title(article);
var encart = encodeURIComponent( "File:" + art.stripNamespace() );
var shared_url = pg.wiki.apicommonsbase + '?format=json&formatversion=2' +
'&callback=pg.fn.APIsharedImagePagePreviewHTML' +
'&requestid=' + navpop.idNumber +
'&action=query&prop=revisions&rvprop=content&titles=' + encart;
ret = ret +'<hr />' + popupString( 'Image from Commons') +
': <a href="' + pg.wiki.commonsbase + '?title=' + encart + '">' +
popupString( 'Description page') + '</a>';
mw.loader.load( shared_url );
}
showAPIPreview('imagelinks', APIimagelinksPreviewHTML(article,download), navpop.idNumber, download);
return ret;
} catch (someError) {
return 'API imagepage preview failed :(';
}
}
function APIimagelinksPreviewHTML(article, download) {
try {
var jsobj=getJsObj(download.data);
var list=jsobj.query.imageusage;
if (list) {
var ret=[];
for (var i=0; i < list.length; i++) {
ret.push(list[i].title);
}
if (ret.length === 0) { return popupString('No image links found'); }
return '<h2>' + popupString('File links') + '</h2>' + linkList(ret);
} else {
return popupString('No image links found');
}
} catch(someError) {
return 'Image links preview generation failed :(';
}
}
function APIcategoryPreviewHTML(article, download) {
try{
var jsobj=getJsObj(download.data);
var list=jsobj.query.categorymembers;
var ret=[];
for (var p=0; p < list.length; p++) {
ret.push(list[p].title);
}
if (ret.length === 0) { return popupString('Empty category'); }
ret = '<h2>' + tprintf('Category members (%s shown)', [ret.length]) + '</h2>' +linkList(ret);
if (jsobj['continue'] && jsobj['continue'].cmcontinue) {
ret += popupString(' and more');
}
return ret;
} catch(someError) {
return 'Category preview failed :(';
}
}
function APIuserInfoPreviewHTML(article, download) {
var ret=[];
var queryobj = {};
try{
queryobj=getJsObj(download.data).query;
} catch(someError) { return 'Userinfo preview failed :('; }
var user=anyChild(queryobj.users);
if (user) {
var globaluserinfo=queryobj.globaluserinfo;
if (user.invalid === '') {
ret.push( popupString( 'Invalid user') );
} else if (user.missing === '') {
ret.push( popupString( 'Not a registered username') );
}
if( user.blockedby ) {
if( user.blockpartial ) {
ret.push('<b>' + popupString('Has blocks') + '</b>');
} else {
ret.push('<b>' + popupString('CẤM') + '</b>');
}
}
if( globaluserinfo && ( 'locked' in globaluserinfo || 'hidden' in globaluserinfo ) ) {
var lockedSulAccountIsAttachedToThis = true;
for( var i=0; globaluserinfo.unattached && i < globaluserinfo.unattached.length; i++) {
if ( globaluserinfo.unattached[i].wiki === mw.config.get('wgDBname') ) {
lockedSulAccountIsAttachedToThis=false;
break;
}
}
if (lockedSulAccountIsAttachedToThis) {
if ( 'locked' in globaluserinfo ) ret.push('<b><i>' + popupString('LOCKED') + '</i></b>');
if ( 'hidden' in globaluserinfo ) ret.push('<b><i>' + popupString('HIDDEN') + '</i></b>');
}
}
if( getValueOf('popupShowGender') && user.gender ) {
switch( user.gender ) {
case "male": ret.push( popupString( "\u2642" ) ); break;
case "female": ret.push( popupString( "\u2640" ) ); break;
}
}
if( user.groups ) {
for( var j=0; j < user.groups.length; j++) {
var currentGroup = user.groups[j];
if( ["*", "user", "autoconfirmed", "extendedconfirmed"].indexOf( currentGroup ) === -1 ) {
ret.push( pg.escapeQuotesHTML(user.groups[j]) );
}
}
}
if( globaluserinfo && globaluserinfo.groups ) {
for( var k=0; k < globaluserinfo.groups.length; k++) {
ret.push( '<i>'+pg.escapeQuotesHTML(globaluserinfo.groups[k])+'</i>' );
}
}
if( user.registration )
ret.push( pg.escapeQuotesHTML((user.editcount?user.editcount:'0') + popupString(' sửa đổi từ: ') + (user.registration?dayFormat(getDateFromTimestamp(user.registration)):'')) );
}
if (queryobj.usercontribs && queryobj.usercontribs.length) {
ret.push( popupString('last edit on ') + dayFormat(getDateFromTimestamp(queryobj.usercontribs[0].timestamp)) );
}
if (queryobj.blocks) {
ret.push( popupString( 'IP user') ); //we only request list=blocks for IPs
for (var l=0; l<queryobj.blocks.length; l++) {
console.log(queryobj);
var rbstr = queryobj.blocks[l].rangestart === queryobj.blocks[l].rangeend ? 'BLOCK' : 'RANGEBLOCK';
rbstr = (!Array.isArray(queryobj.blocks[l].restrictions) ? 'Has ' + rbstr.toLowerCase() + 's' : rbstr + 'ED');
ret.push('<b>' + popupString(rbstr) + '</b>' );
}
}
ret = '<hr />' + ret.join( ', ' );
return ret;
}
function APIcontribsPreviewHTML(article, download, navpop) {
return APIhistoryPreviewHTML(article, download, navpop, true);
}
function APIhistoryPreviewHTML(article, download, navpop, reallyContribs) {
try {
var jsobj=getJsObj(download.data);
var edits = [];
if( reallyContribs ) {
edits=jsobj.query.usercontribs;
} else {
edits=anyChild(jsobj.query.pages).revisions;
}
var ret=editPreviewTable(article, edits, reallyContribs, getTimeOffset());
return ret;
} catch (someError) {
return 'History preview failed :-(';
}
}
//</NOLITE>
// ENDFILE: querypreview.js
// STARTFILE: debug.js
////////////////////////////////////////////////////////////////////
// Debugging functions
////////////////////////////////////////////////////////////////////
function setupDebugging() {
//<NOLITE>
if (window.popupDebug) { // popupDebug is set from .version
window.log=function(x) { //if(gMsg!='')gMsg += '\n'; gMsg+=time() + ' ' + x; };
window.console.log(x);
};
window.errlog=function(x) {
window.console.error(x);
};
log('Initializing logger');
} else {
//</NOLITE>
window.log = function() {};
window.errlog = function() {};
//<NOLITE>
}
//</NOLITE>
}
// ENDFILE: debug.js
// STARTFILE: images.js
// load image of type Title.
function loadImage(image, navpop) {
if (typeof image.stripNamespace != 'function') { alert('loadImages bad'); }
// API call to retrieve image info.
if ( !getValueOf('popupImages') ) return;
if ( !isValidImageName(image) ) return false;
var art=image.urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query';
url += '&prop=imageinfo&iiprop=url|mime&iiurlwidth=' + getValueOf('popupImageSizeLarge');
url += '&titles=' + art;
pendingNavpopTask(navpop);
var callback=function(d){
popupsInsertImage(navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'after', 'DOWNLOAD_IMAGE_QUERY_DATA'); }
}
function popupsInsertImage(id, navpop, download) {
log( "popupsInsertImage");
var imageinfo;
try {
var jsObj=getJsObj(download.data);
var imagepage=anyChild(jsObj.query.pages);
if (typeof imagepage.imageinfo === 'undefined') return;
imageinfo = imagepage.imageinfo[0];
} catch (someError) {
log( "popupsInsertImage failed :(" );
return;
}
var popupImage = document.getElementById("popupImg"+id);
if (!popupImage) {
log( "could not find insertion point for image");
return;
}
popupImage.width=getValueOf('popupImageSize');
popupImage.style.display='inline';
// Set the source for the image.
if( imageinfo.thumburl )
popupImage.src=imageinfo.thumburl;
else if( imageinfo.mime.indexOf("image") === 0 ){
popupImage.src=imageinfo.url;
log( "a thumb could not be found, using original image" );
} else log( "fullsize imagethumb, but not sure if it's an image");
var a=document.getElementById("popupImageLink"+id);
if (a === null) { return null; }
// Determine the action of the surrouding imagelink.
switch (getValueOf('popupThumbAction')) {
case 'imagepage':
if (pg.current.article.namespaceId()!=pg.nsImageId) {
a.href=imageinfo.descriptionurl;
// FIXME: unreliable pg.idNumber
popTipsSoonFn('popupImage' + id)();
break;
}
/* falls through */
case 'sizetoggle':
a.onclick=toggleSize;
a.title=popupString('Toggle image size');
return;
case 'linkfull':
a.href = imageinfo.url;
a.title=popupString('Open full-size image');
return;
}
}
// Toggles the image between inline small and navpop fullwidth.
// It's the same image, no actual sizechange occurs, only display width.
function toggleSize() {
var imgContainer=this;
if (!imgContainer) {
alert('imgContainer is null :/');
return;
}
var img=imgContainer.firstChild;
if (!img) {
alert('img is null :/');
return;
}
if (!img.style.width || img.style.width==='') {
img.style.width='100%';
} else {
img.style.width='';
}
}
// Returns one title of an image from wikiText.
function getValidImageFromWikiText(wikiText) {
// nb in pg.re.image we're interested in the second bracketed expression
// this may change if the regex changes :-(
//var match=pg.re.image.exec(wikiText);
var matched=null;
var match;
// strip html comments, used by evil bots :-(
var t = removeMatchesUnless(wikiText, RegExp('(<!--[\\s\\S]*?-->)'), 1,
RegExp('^<!--[^[]*popup', 'i'));
while ( ( match = pg.re.image.exec(t) ) ) {
// now find a sane image name - exclude templates by seeking {
var m = match[2] || match[6];
if ( isValidImageName(m) ) {
matched=m;
break;
}
}
pg.re.image.lastIndex=0;
if (!matched) { return null; }
return mw.config.get('wgFormattedNamespaces')[pg.nsImageId]+':'+upcaseFirst(matched);
}
function removeMatchesUnless(str, re1, parencount, re2) {
var split=str.parenSplit(re1);
var c=parencount + 1;
for (var i=0; i<split.length; ++i) {
if ( i%c === 0 || re2.test(split[i]) ) { continue; }
split[i]='';
}
return split.join('');
}
//</NOLITE>
// ENDFILE: images.js
// STARTFILE: namespaces.js
// Set up namespaces and other non-strings.js localization
// (currently that means redirs too)
function setNamespaces() {
pg.nsSpecialId = -1;
pg.nsMainspaceId = 0;
pg.nsImageId = 6;
pg.nsUserId = 2;
pg.nsUsertalkId = 3;
pg.nsCategoryId = 14;
pg.nsTemplateId = 10;
}
function setRedirs() {
var r='redirect';
var R='REDIRECT';
var redirLists={
//<NOLITE>
'ar': [ R, 'تحويل' ],
'be': [ r, 'перанакіраваньне' ],
'bg': [ r, 'пренасочване', 'виж' ],
'bs': [ r, 'Preusmjeri', 'preusmjeri', 'PREUSMJERI' ],
'cs': [ R, 'PŘESMĚRUJ' ],
'cy': [ r, 'ail-cyfeirio' ],
'de': [ R, 'WEITERLEITUNG' ],
'el': [ R, 'ΑΝΑΚΑΤΕΥΘΥΝΣΗ'],
'eo': [ R, 'ALIDIREKTU', 'ALIDIREKTI' ],
'es': [ R, 'REDIRECCIÓN' ],
'et': [ r, 'suuna' ],
'ga': [ r, 'athsheoladh' ],
'gl': [ r, 'REDIRECCIÓN', 'REDIRECIONAMENTO'],
'he': [ R, 'הפניה' ],
'hu': [ R, 'ÁTIRÁNYÍTÁS' ],
'is': [ r, 'tilvísun', 'TILVÍSUN' ],
'it': [ R, 'RINVIA', 'Rinvia'],
'ja': [ R, '転送' ],
'mk': [ r, 'пренасочување', 'види' ],
'nds': [ r, 'wiederleiden' ],
'nl': [ R, 'DOORVERWIJZING' ],
'nn': [ r, 'omdiriger' ],
'pl': [ R, 'PATRZ', 'PRZEKIERUJ', 'TAM' ],
'pt': [ R, 'redir' ],
'ru': [ R, 'ПЕРЕНАПРАВЛЕНИЕ', 'ПЕРЕНАПР' ],
'sk': [ r, 'presmeruj' ],
'sr': [ r, 'Преусмери', 'преусмери', 'ПРЕУСМЕРИ', 'Preusmeri', 'preusmeri', 'PREUSMERI' ],
'tt': [ R, 'yünältü', 'перенаправление', 'перенапр' ],
'uk': [ R, 'ПЕРЕНАПРАВЛЕННЯ', 'ПЕРЕНАПР' ],
'vi': [ r, 'đổi' ],
'zh': [ R, '重定向'] // no comma
//</NOLITE>
};
var redirList=redirLists[ pg.wiki.lang ] || [r, R];
// Mediawiki is very tolerant about what comes after the #redirect at the start
pg.re.redirect=RegExp('^\\s*[#](' + redirList.join('|') + ').*?\\[{2}([^\\|\\]]*)(|[^\\]]*)?\\]{2}\\s*(.*)', 'i');
}
function setInterwiki() {
if (pg.wiki.wikimedia) {
// From https://meta.wikimedia.org/wiki/List_of_Wikipedias
pg.wiki.interwiki='aa|ab|ace|af|ak|als|am|an|ang|ar|arc|arz|as|ast|av|ay|az|ba|bar|bat-smg|bcl|be|be-x-old|bg|bh|bi|bjn|bm|bn|bo|bpy|br|bs|bug|bxr|ca|cbk-zam|cdo|ce|ceb|ch|cho|chr|chy|ckb|co|cr|crh|cs|csb|cu|cv|cy|da|de|diq|dsb|dv|dz|ee|el|eml|en|eo|es|et|eu|ext|fa|ff|fi|fiu-vro|fj|fo|fr|frp|frr|fur|fy|ga|gag|gan|gd|gl|glk|gn|got|gu|gv|ha|hak|haw|he|hi|hif|ho|hr|hsb|ht|hu|hy|hz|ia|id|ie|ig|ii|ik|ilo|io|is|it|iu|ja|jbo|jv|ka|kaa|kab|kbd|kg|ki|kj|kk|kl|km|kn|ko|koi|kr|krc|ks|ksh|ku|kv|kw|ky|la|lad|lb|lbe|lg|li|lij|lmo|ln|lo|lt|ltg|lv|map-bms|mdf|mg|mh|mhr|mi|mk|ml|mn|mo|mr|mrj|ms|mt|mus|mwl|my|myv|mzn|na|nah|nap|nds|nds-nl|ne|new|ng|nl|nn|no|nov|nrm|nv|ny|oc|om|or|os|pa|pag|pam|pap|pcd|pdc|pfl|pi|pih|pl|pms|pnb|pnt|ps|pt|qu|rm|rmy|rn|ro|roa-rup|roa-tara|ru|rue|rw|sa|sah|sc|scn|sco|sd|se|sg|sh|si|simple|sk|sl|sm|sn|so|sq|sr|srn|ss|st|stq|su|sv|sw|szl|ta|te|tet|tg|th|ti|tk|tl|tn|to|tpi|tr|ts|tt|tum|tw|ty|udm|ug|uk|ur|uz|ve|vec|vi|vls|vo|wa|war|wo|wuu|xal|xh|yi|yo|za|zea|zh|zh-classical|zh-min-nan|zh-yue|zu';
pg.re.interwiki=RegExp('^'+pg.wiki.interwiki+':');
} else {
pg.wiki.interwiki=null;
pg.re.interwiki=RegExp('^$');
}
}
// return a regexp pattern matching all variants to write the given namespace
function nsRe(namespaceId) {
var imageNamespaceVariants = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=namespaceId) return;
_localizedNamespaceLc = upcaseFirst(_localizedNamespaceLc);
imageNamespaceVariants.push(mw.util.escapeRegExp(_localizedNamespaceLc).split(' ').join('[ _]'));
imageNamespaceVariants.push(mw.util.escapeRegExp(encodeURI(_localizedNamespaceLc)));
});
return '(?:' + imageNamespaceVariants.join('|') + ')';
}
function nsReImage() {
return nsRe(pg.nsImageId);
}
// ENDFILE: namespaces.js
// STARTFILE: selpop.js
//<NOLITE>
function getEditboxSelection() {
// see http://www.webgurusforum.com/8/12/0
var editbox;
try {
editbox=document.editform.wpTextbox1;
} catch (dang) { return; }
// IE, Opera
if (document.selection) { return document.selection.createRange().text; }
// Mozilla
var selStart = editbox.selectionStart;
var selEnd = editbox.selectionEnd;
return (editbox.value).substring(selStart, selEnd);
}
function doSelectionPopup() {
// popup if the selection looks like [[foo|anything afterwards at all
// or [[foo|bar]]text without ']]'
// or [[foo|bar]]
var sel=getEditboxSelection();
var open=sel.indexOf('[[');
var pipe=sel.indexOf('|');
var close=sel.indexOf(']]');
if (open == -1 || ( pipe == -1 && close == -1) ) { return; }
if (pipe != -1 && open > pipe || close != -1 && open > close) { return; }
if (getValueOf('popupOnEditSelection')=='boxpreview') {
return doSeparateSelectionPopup(sel);
}
var article=new Title(sel.substring(open+2, (pipe < 0) ? close : pipe)).urlString();
if (close > 0 && sel.substring(close+2).indexOf('[[') >= 0) {
return;
}
var a=document.createElement('a');
a.href=pg.wiki.titlebase + article;
mouseOverWikiLink2(a);
if (a.navpopup) {
a.navpopup.addHook(function(){runStopPopupTimer(a.navpopup);}, 'unhide', 'after');
}
}
function doSeparateSelectionPopup(str) {
var div=document.getElementById('selectionPreview');
if (!div) {
div = document.createElement('div');
div.id='selectionPreview';
try {
var box=document.editform.wpTextbox1;
box.parentNode.insertBefore(div, box);
} catch (error) {
return;
}
}
div.innerHTML=wiki2html(str);
div.ranSetupTooltipsAlready = false;
popTipsSoonFn('selectionPreview')();
}
//</NOLITE>
// ENDFILE: selpop.js
// STARTFILE: navpopup.js
/**
@fileoverview Defines two classes: {@link Navpopup} and {@link Mousetracker}.
<code>Navpopup</code> describes popups: when they appear, where, what
they look like and so on.
<code>Mousetracker</code> "captures" the mouse using
<code>document.onmousemove</code>.
*/
/**
Creates a new Mousetracker.
@constructor
@class The Mousetracker class. This monitors mouse movements and manages associated hooks.
*/
function Mousetracker() {
/**
Interval to regularly run the hooks anyway, in milliseconds.
@type Integer
*/
this.loopDelay=400;
/**
Timer for the loop.
@type Timer
*/
this.timer=null;
/**
Flag - are we switched on?
@type Boolean
*/
this.active=false;
/**
Flag - are we probably inaccurate, i.e. not reflecting the actual mouse position?
*/
this.dirty=true;
/**
Array of hook functions.
@private
@type Array
*/
this.hooks=[];
}
/**
Adds a hook, to be called when we get events.
@param {Function} f A function which is called as
<code>f(x,y)</code>. It should return <code>true</code> when it
wants to be removed, and <code>false</code> otherwise.
*/
Mousetracker.prototype.addHook = function (f) {
this.hooks.push(f);
};
/**
Runs hooks, passing them the x
and y coords of the mouse. Hook functions that return true are
passed to {@link Mousetracker#removeHooks} for removal.
@private
*/
Mousetracker.prototype.runHooks = function () {
if (!this.hooks || !this.hooks.length) { return; }
//log('Mousetracker.runHooks; we got some hooks to run');
var remove=false;
var removeObj={};
// this method gets called a LOT -
// pre-cache some variables
var x=this.x, y=this.y, len = this.hooks.length;
for (var i=0; i<len; ++i) {
//~ run the hook function, and remove it if it returns true
if (this.hooks[i](x, y)===true) {
remove=true;
removeObj[i]=true;
}
}
if (remove) { this.removeHooks(removeObj); }
};
/**
Removes hooks.
@private
@param {Object} removeObj An object whose keys are the index
numbers of functions for removal, with values that evaluate to true
*/
Mousetracker.prototype.removeHooks = function(removeObj) {
var newHooks=[];
var len = this.hooks.length;
for (var i=0; i<len; ++i) {
if (! removeObj[i]) { newHooks.push(this.hooks[i]); }
}
this.hooks=newHooks;
};
/**
Event handler for mouse wiggles.
We simply grab the event, set x and y and run the hooks.
This makes the cpu all hot and bothered :-(
@private
@param {Event} e Mousemove event
*/
Mousetracker.prototype.track=function (e) {
//~ Apparently this is needed in IE.
e = e || window.event;
var x, y;
if (e) {
if (e.pageX) { x=e.pageX; y=e.pageY; }
else if (typeof e.clientX!='undefined') {
var left, top, docElt = document.documentElement;
if (docElt) { left=docElt.scrollLeft; }
left = left || document.body.scrollLeft || document.scrollLeft || 0;
if (docElt) { top=docElt.scrollTop; }
top = top || document.body.scrollTop || document.scrollTop || 0;
x=e.clientX + left;
y=e.clientY + top;
} else { return; }
this.setPosition(x,y);
}
};
/**
Sets the x and y coordinates stored and takes appropriate action,
running hooks as appropriate.
@param {Integer} x, y Screen coordinates to set
*/
Mousetracker.prototype.setPosition=function(x,y) {
this.x = x;
this.y = y;
if (this.dirty || this.hooks.length === 0) { this.dirty=false; return; }
if (typeof this.lastHook_x != 'number') { this.lastHook_x = -100; this.lastHook_y=-100; }
var diff = (this.lastHook_x - x)*(this.lastHook_y - y);
diff = (diff >= 0) ? diff : -diff;
if ( diff > 1 ) {
this.lastHook_x=x;
this.lastHook_y=y;
if (this.dirty) { this.dirty = false; }
else { this.runHooks(); }
}
};
/**
Sets things in motion, unless they are already that is, registering an event handler on <code>document.onmousemove</code>.
A half-hearted attempt is made to preserve the old event handler if there is one.
*/
Mousetracker.prototype.enable = function () {
if (this.active) { return; }
this.active=true;
//~ Save the current handler for mousemove events. This isn't too
//~ robust, of course.
this.savedHandler=document.onmousemove;
//~ Gotta save @tt{this} again for the closure, and use apply for
//~ the member function.
var savedThis=this;
document.onmousemove=function (e) {savedThis.track.apply(savedThis, [e]);};
if (this.loopDelay) { this.timer = setInterval(function() { //log('loop delay in mousetracker is working');
savedThis.runHooks();}, this.loopDelay); }
};
/**
Disables the tracker, removing the event handler.
*/
Mousetracker.prototype.disable = function () {
if (!this.active) { return; }
if (typeof this.savedHandler === 'function') {
document.onmousemove=this.savedHandler;
} else { delete document.onmousemove; }
if (this.timer) { clearInterval(this.timer); }
this.active=false;
};
/**
Creates a new Navpopup.
Gets a UID for the popup and
@param init Contructor object. If <code>init.draggable</code> is true or absent, the popup becomes draggable.
@constructor
@class The Navpopup class. This generates popup hints, and does some management of them.
*/
function Navpopup(/*init*/) {
//alert('new Navpopup(init)');
/** UID for each Navpopup instance.
Read-only.
@type integer
*/
this.uid=Navpopup.uid++;
/**
Read-only flag for current visibility of the popup.
@type boolean
@private
*/
this.visible=false;
/** Flag to be set when we want to cancel a previous request to
show the popup in a little while.
@private
@type boolean
*/
this.noshow=false;
/** Categorised list of hooks.
@see #runHooks
@see #addHook
@private
@type Object
*/
this.hooks={
'create': [],
'unhide': [],
'hide': []
};
/** list of unique IDs of hook functions, to avoid duplicates
@private
*/
this.hookIds={};
/** List of downloads associated with the popup.
@private
@type Array
*/
this.downloads=[];
/** Number of uncompleted downloads.
@type integer
*/
this.pending=null;
/** Tolerance in pixels when detecting whether the mouse has left the popup.
@type integer
*/
this.fuzz=5;
/** Flag to toggle running {@link #limitHorizontalPosition} to regulate the popup's position.
@type boolean
*/
this.constrained=true;
/** The popup width in pixels.
@private
@type integer
*/
this.width=0;
/** The popup width in pixels.
@private
@type integer
*/
this.height=0;
/** The main content DIV element.
@type HTMLDivElement
*/
this.mainDiv=null;
this.createMainDiv();
// if (!init || typeof init.popups_draggable=='undefined' || init.popups_draggable) {
// this.makeDraggable(true);
// }
}
/**
A UID for each Navpopup. This constructor property is just a counter.
@type integer
@private
*/
Navpopup.uid=0;
/**
Retrieves the {@link #visible} attribute, indicating whether the popup is currently visible.
@type boolean
*/
Navpopup.prototype.isVisible=function() {
return this.visible;
};
/**
Repositions popup using CSS style.
@private
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@param {boolean} noLimitHor Don't call {@link #limitHorizontalPosition}
*/
Navpopup.prototype.reposition= function (x,y, noLimitHor) {
log ('reposition('+x+','+y+','+noLimitHor+')');
if (typeof x != 'undefined' && x !== null) { this.left=x; }
if (typeof y != 'undefined' && y !== null) { this.top=y; }
if (typeof this.left != 'undefined' && typeof this.top != 'undefined') {
this.mainDiv.style.left=this.left + 'px';
this.mainDiv.style.top=this.top + 'px';
}
if (!noLimitHor) { this.limitHorizontalPosition(); }
//console.log('navpop'+this.uid+' - (left,top)=(' + this.left + ',' + this.top + '), css=('
//+ this.mainDiv.style.left + ',' + this.mainDiv.style.top + ')');
};
/**
Prevents popups from being in silly locations. Hopefully.
Should not be run if {@link #constrained} is true.
@private
*/
Navpopup.prototype.limitHorizontalPosition=function() {
if (!this.constrained || this.tooWide) { return; }
this.updateDimensions();
var x=this.left;
var w=this.width;
var cWidth=document.body.clientWidth;
// log('limitHorizontalPosition: x='+x+
// ', this.left=' + this.left +
// ', this.width=' + this.width +
// ', cWidth=' + cWidth);
if ( (x+w) >= cWidth ||
( x > 0 &&
this.maxWidth &&
this.width < this.maxWidth &&
this.height > this.width &&
x > cWidth - this.maxWidth ) ) {
// This is a very nasty hack. There has to be a better way!
// We find the "natural" width of the div by positioning it at the far left
// then reset it so that it should be flush right (well, nearly)
this.mainDiv.style.left='-10000px';
this.mainDiv.style.width = this.maxWidth + 'px';
var naturalWidth=parseInt(this.mainDiv.offsetWidth, 10);
var newLeft=cWidth - naturalWidth - 1;
if (newLeft < 0) { newLeft = 0; this.tooWide=true; } // still unstable for really wide popups?
log ('limitHorizontalPosition: moving to ('+newLeft + ','+ this.top+');' + ' naturalWidth=' + naturalWidth + ', clientWidth=' + cWidth);
this.reposition(newLeft, null, true);
}
};
/**
Counter indicating the z-order of the "highest" popup.
We start the z-index at 1000 so that popups are above everything
else on the screen.
@private
@type integer
*/
Navpopup.highest=1000;
/**
Brings popup to the top of the z-order.
We increment the {@link #highest} property of the contructor here.
@private
*/
Navpopup.prototype.raise = function () {
this.mainDiv.style.zIndex=Navpopup.highest + 1;
++Navpopup.highest;
};
/**
Shows the popup provided {@link #noshow} is not true.
Updates the position, brings the popup to the top of the z-order and unhides it.
*/
Navpopup.prototype.show = function () {
//document.title+='s';
if (this.noshow) { return; }
//document.title+='t';
this.reposition();
this.raise();
this.unhide();
};
/**
Checks to see if the mouse pointer has
stabilised (checking every <code>time</code>/2 milliseconds) and runs the
{@link #show} method if it has.
@param {integer} time The minimum time (ms) before the popup may be shown.
*/
Navpopup.prototype.showSoonIfStable = function (time) {
log ('showSoonIfStable, time='+time);
if (this.visible) { return; }
this.noshow = false;
//~ initialize these variables so that we never run @tt{show} after
//~ just half the time
this.stable_x = -10000; this.stable_y = -10000;
var stableShow = function() {
log('stableShow called');
var new_x = Navpopup.tracker.x, new_y = Navpopup.tracker.y;
var dx = savedThis.stable_x - new_x, dy = savedThis.stable_y - new_y;
var fuzz2 = 0; // savedThis.fuzz * savedThis.fuzz;
//document.title += '[' + [savedThis.stable_x,new_x, savedThis.stable_y,new_y, dx, dy, fuzz2].join(',') + '] ';
if ( dx * dx <= fuzz2 && dy * dy <= fuzz2 ) {
log ('mouse is stable');
clearInterval(savedThis.showSoonStableTimer);
savedThis.reposition.apply(savedThis, [new_x + 2, new_y + 2]);
savedThis.show.apply(savedThis, []);
savedThis.limitHorizontalPosition.apply(savedThis, []);
return;
}
savedThis.stable_x = new_x; savedThis.stable_y = new_y;
};
var savedThis = this;
this.showSoonStableTimer = setInterval(stableShow, time/2);
};
/**
Sets the {@link #noshow} flag and hides the popup. This should be called
when the mouse leaves the link before
(or after) it's actually been displayed.
*/
Navpopup.prototype.banish = function () {
log ('banish called');
// hide and prevent showing with showSoon in the future
this.noshow=true;
if (this.showSoonStableTimer) {
log('clearing showSoonStableTimer');
clearInterval(this.showSoonStableTimer);
}
this.hide();
};
/**
Runs hooks added with {@link #addHook}.
@private
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
*/
Navpopup.prototype.runHooks = function (key, when) {
if (!this.hooks[key]) { return; }
var keyHooks=this.hooks[key];
var len=keyHooks.length;
for (var i=0; i< len; ++i) {
if (keyHooks[i] && keyHooks[i].when == when) {
if (keyHooks[i].hook.apply(this, [])) {
// remove the hook
if (keyHooks[i].hookId) {
delete this.hookIds[keyHooks[i].hookId];
}
keyHooks[i]=null;
}
}
}
};
/**
Adds a hook to the popup. Hook functions are run with <code>this</code> set to refer to the Navpopup instance, and no arguments.
@param {Function} hook The hook function. Functions that return true are deleted.
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
@param {String} uid A truthy string identifying the hook function; if it matches another hook in this position, it won't be added again.
*/
Navpopup.prototype.addHook = function ( hook, key, when, uid ) {
when = when || 'after';
if (!this.hooks[key]) { return; }
// if uid is specified, don't add duplicates
var hookId=null;
if (uid) {
hookId=[key,when,uid].join('|');
if (this.hookIds[hookId]) {
return;
}
this.hookIds[hookId]=true;
}
this.hooks[key].push( {hook: hook, when: when, hookId: hookId} );
};
/**
Creates the main DIV element, which contains all the actual popup content.
Runs hooks with key 'create'.
@private
*/
Navpopup.prototype.createMainDiv = function () {
if (this.mainDiv) { return; }
this.runHooks('create', 'before');
var mainDiv=document.createElement('div');
var savedThis=this;
mainDiv.onclick=function(e) {savedThis.onclickHandler(e);};
mainDiv.className=(this.className) ? this.className : 'navpopup_maindiv';
mainDiv.id=mainDiv.className + this.uid;
mainDiv.style.position='absolute';
mainDiv.style.minWidth = '350px';
mainDiv.style.display='none';
mainDiv.className='navpopup';
// easy access to javascript object through DOM functions
mainDiv.navpopup=this;
this.mainDiv=mainDiv;
document.body.appendChild(mainDiv);
this.runHooks('create', 'after');
};
/**
Calls the {@link #raise} method.
@private
*/
Navpopup.prototype.onclickHandler=function(/*e*/) {
this.raise();
};
/**
Makes the popup draggable, using a {@link Drag} object.
@private
*/
Navpopup.prototype.makeDraggable=function(handleName) {
if (!this.mainDiv) { this.createMainDiv(); }
var drag=new Drag();
if (!handleName) {
drag.startCondition=function(e) {
try { if (!e.shiftKey) { return false; } } catch (err) { return false; }
return true;
};
}
var dragHandle;
if (handleName) dragHandle = document.getElementById(handleName);
if (!dragHandle) dragHandle = this.mainDiv;
var np=this;
drag.endHook=function(x,y) {
Navpopup.tracker.dirty=true;
np.reposition(x,y);
};
drag.init(dragHandle,this.mainDiv);
};
/** Hides the popup using CSS. Runs hooks with key 'hide'.
Sets {@link #visible} appropriately. {@link #banish} should be called externally instead of this method.
@private
*/
Navpopup.prototype.hide = function () {
this.runHooks('hide', 'before');
this.abortDownloads();
if (typeof this.visible != 'undefined' && this.visible) {
this.mainDiv.style.display='none';
this.visible=false;
}
this.runHooks('hide', 'after');
};
/** Shows the popup using CSS. Runs hooks with key 'unhide'.
Sets {@link #visible} appropriately. {@link #show} should be called externally instead of this method.
@private
*/
Navpopup.prototype.unhide = function () {
this.runHooks('unhide', 'before');
if (typeof this.visible != 'undefined' && !this.visible) {
this.mainDiv.style.display='inline';
this.visible=true;
}
this.runHooks('unhide', 'after');
};
/**
Sets the <code>innerHTML</code> attribute of the main div containing the popup content.
@param {String} html The HTML to set.
*/
Navpopup.prototype.setInnerHTML = function (html) {
this.mainDiv.innerHTML = html;
};
/**
Updates the {@link #width} and {@link #height} attributes with the CSS properties.
@private
*/
Navpopup.prototype.updateDimensions = function () {
this.width=parseInt(this.mainDiv.offsetWidth, 10);
this.height=parseInt(this.mainDiv.offsetHeight, 10);
};
/**
Checks if the point (x,y) is within {@link #fuzz} of the
{@link #mainDiv}.
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@type boolean
*/
Navpopup.prototype.isWithin = function(x,y) {
//~ If we're not even visible, no point should be considered as
//~ being within the popup.
if (!this.visible) { return false; }
this.updateDimensions();
var fuzz=this.fuzz || 0;
//~ Use a simple box metric here.
return (x+fuzz >= this.left && x-fuzz <= this.left + this.width &&
y+fuzz >= this.top && y-fuzz <= this.top + this.height);
};
/**
Adds a download to {@link #downloads}.
@param {Downloader} download
*/
Navpopup.prototype.addDownload=function(download) {
if (!download) { return; }
this.downloads.push(download);
};
/**
Aborts the downloads listed in {@link #downloads}.
@see Downloader#abort
*/
Navpopup.prototype.abortDownloads=function() {
for(var i=0; i<this.downloads.length; ++i) {
var d=this.downloads[i];
if (d && d.abort) { d.abort(); }
}
this.downloads=[];
};
/**
A {@link Mousetracker} instance which is a property of the constructor (pseudo-global).
*/
Navpopup.tracker=new Mousetracker();
// ENDFILE: navpopup.js
// STARTFILE: diff.js
//<NOLITE>
/*
* Javascript Diff Algorithm
* By John Resig (http://ejohn.org/) and [[:en:User:Lupin]]
*
* More Info:
* http://ejohn.org/projects/javascript-diff-algorithm/
*/
function delFmt(x) {
if (!x.length) { return ''; }
return "<del class='popupDiff'>" + x.join('') +"</del>";
}
function insFmt(x) {
if (!x.length) { return ''; }
return "<ins class='popupDiff'>" + x.join('') +"</ins>";
}
function countCrossings(a, b, i, eject) {
// count the crossings on the edge starting at b[i]
if (!b[i].row && b[i].row !== 0) { return -1; }
var count=0;
for (var j=0; j<a.length; ++j) {
if (!a[j].row && a[j].row !== 0) { continue; }
if ( (j-b[i].row)*(i-a[j].row) > 0) {
if(eject) { return true; }
count++;
}
}
return count;
}
function shortenDiffString(str, context) {
var re=RegExp('(<del[\\s\\S]*?</del>|<ins[\\s\\S]*?</ins>)');
var splitted=str.parenSplit(re);
var ret=[''];
for (var i=0; i<splitted.length; i+=2) {
if (splitted[i].length < 2*context) {
ret[ret.length-1] += splitted[i];
if (i+1<splitted.length) { ret[ret.length-1] += splitted[i+1]; }
continue;
}
else {
if (i > 0) { ret[ret.length-1] += splitted[i].substring(0,context); }
if (i+1 < splitted.length) {
ret.push(splitted[i].substring(splitted[i].length-context) +
splitted[i+1]);
}
}
}
while (ret.length > 0 && !ret[0]) { ret = ret.slice(1); }
return ret;
}
function diffString( o, n, simpleSplit ) {
var splitRe=RegExp('([[]{2}|[\\]]{2}|[{]{2,3}|[}]{2,3}|[|]|=|<|>|[*:]+|\\s|\\b)');
// We need to split the strings o and n first, and entify() the parts
// individually, so that the HTML entities are never cut apart. (AxelBoldt)
var out, i, oSplitted, nSplitted;
if (simpleSplit) {
oSplitted=o.split(/\b/);
nSplitted=n.split(/\b/);
} else {
oSplitted=o.parenSplit(splitRe);
nSplitted=n.parenSplit(splitRe);
}
for (i=0; i<oSplitted.length; ++i) {oSplitted[i]=oSplitted[i].entify();}
for (i=0; i<nSplitted.length; ++i) {nSplitted[i]=nSplitted[i].entify();}
out = diff (oSplitted, nSplitted);
var str = "";
var acc=[]; // accumulator for prettier output
// crossing pairings -- eg 'A B' vs 'B A' -- cause problems, so let's iron them out
// this doesn't always do things optimally but it should be fast enough
var maxOutputPair=0;
for (i=0; i<out.n.length; ++i) {
if ( out.n[i].paired ) {
if( maxOutputPair > out.n[i].row ) {
// tangle - delete pairing
out.o[ out.n[i].row ]=out.o[ out.n[i].row ].text;
out.n[i]=out.n[i].text;
}
if (maxOutputPair < out.n[i].row) { maxOutputPair = out.n[i].row; }
}
}
// output the stuff preceding the first paired old line
for (i=0; i<out.o.length && !out.o[i].paired; ++i) { acc.push( out.o[i] ); }
str += delFmt(acc); acc=[];
// main loop
for ( i = 0; i < out.n.length; ++i ) {
// output unpaired new "lines"
while ( i < out.n.length && !out.n[i].paired ) { acc.push( out.n[i++] ); }
str += insFmt(acc); acc=[];
if ( i < out.n.length ) { // this new "line" is paired with the (out.n[i].row)th old "line"
str += out.n[i].text;
// output unpaired old rows starting after this new line's partner
var m = out.n[i].row + 1;
while ( m < out.o.length && !out.o[m].paired ) { acc.push ( out.o[m++] ); }
str += delFmt(acc); acc=[];
}
}
return str;
}
// see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Object
// FIXME: use obj.hasOwnProperty instead of this kludge!
var jsReservedProperties=RegExp('^(constructor|prototype|__((define|lookup)[GS]etter)__' +
'|eval|hasOwnProperty|propertyIsEnumerable' +
'|to(Source|String|LocaleString)|(un)?watch|valueOf)$');
function diffBugAlert(word) {
if (!diffBugAlert.list[word]) {
diffBugAlert.list[word]=1;
alert('Bad word: '+word+'\n\nPlease report this bug.');
}
}
diffBugAlert.list={};
function makeDiffHashtable(src) {
var ret={};
for ( var i = 0; i < src.length; i++ ) {
if ( jsReservedProperties.test(src[i]) ) { src[i] += '<!-- -->'; }
if ( !ret[ src[i] ] ) { ret[ src[i] ] = []; }
try { ret[ src[i] ].push( i ); } catch (err) { diffBugAlert(src[i]); }
}
return ret;
}
function diff( o, n ) {
// pass 1: make hashtable ns with new rows as keys
var ns = makeDiffHashtable(n);
// pass 2: make hashtable os with old rows as keys
var os = makeDiffHashtable(o);
// pass 3: pair unique new rows and matching unique old rows
var i;
for ( i in ns ) {
if ( ns[i].length == 1 && os[i] && os[i].length == 1 ) {
n[ ns[i][0] ] = { text: n[ ns[i][0] ], row: os[i][0], paired: true };
o[ os[i][0] ] = { text: o[ os[i][0] ], row: ns[i][0], paired: true };
}
}
// pass 4: pair matching rows immediately following paired rows (not necessarily unique)
for ( i = 0; i < n.length - 1; i++ ) {
if ( n[i].paired && ! n[i+1].paired && n[i].row + 1 < o.length && ! o[ n[i].row + 1 ].paired &&
n[i+1] == o[ n[i].row + 1 ] ) {
n[i+1] = { text: n[i+1], row: n[i].row + 1, paired: true };
o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1, paired: true };
}
}
// pass 5: pair matching rows immediately preceding paired rows (not necessarily unique)
for ( i = n.length - 1; i > 0; i-- ) {
if ( n[i].paired && ! n[i-1].paired && n[i].row > 0 && ! o[ n[i].row - 1 ].paired &&
n[i-1] == o[ n[i].row - 1 ] ) {
n[i-1] = { text: n[i-1], row: n[i].row - 1, paired: true };
o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1, paired: true };
}
}
return { o: o, n: n };
}
//</NOLITE>
// ENDFILE: diff.js
// STARTFILE: init.js
function setSiteInfo() {
if (window.popupLocalDebug) {
pg.wiki.hostname = 'en.wikipedia.org';
} else {
pg.wiki.hostname = location.hostname; // use in preference to location.hostname for flexibility (?)
}
pg.wiki.wikimedia=RegExp('(wiki([pm]edia|source|books|news|quote|versity)|wiktionary|mediawiki)[.]org').test(pg.wiki.hostname);
pg.wiki.wikia=RegExp('[.]wikia[.]com$', 'i').test(pg.wiki.hostname);
pg.wiki.isLocal=RegExp('^localhost').test(pg.wiki.hostname);
pg.wiki.commons=( pg.wiki.wikimedia && pg.wiki.hostname != 'commons.wikimedia.org') ? 'commons.wikimedia.org' : null;
pg.wiki.lang = mw.config.get('wgContentLanguage');
var port = location.port ? ':' + location.port : '';
pg.wiki.sitebase = pg.wiki.hostname + port;
}
function setUserInfo() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
list: 'users',
ususers: mw.config.get('wgUserName'),
usprop: 'rights'
};
pg.user.canReview = false;
if (getValueOf('popupReview')) {
api.get(params).done(function(data){
var rights = data.query.users[0].rights;
pg.user.canReview = rights.indexOf('review') !== -1; // TODO: Should it be a getValueOf('ReviewRight') ?
});
}
}
function setTitleBase() {
var protocol = ( window.popupLocalDebug ? 'http:' : location.protocol );
pg.wiki.articlePath = mw.config.get('wgArticlePath').replace(/\/\$1/, ""); // as in http://some.thing.com/wiki/Article
pg.wiki.botInterfacePath = mw.config.get('wgScript');
pg.wiki.APIPath = mw.config.get('wgScriptPath') +"/api.php";
// default mediawiki setting is paths like http://some.thing.com/articlePath/index.php?title=foo
var titletail = pg.wiki.botInterfacePath + '?title=';
//var titletail2 = joinPath([pg.wiki.botInterfacePath, 'wiki.phtml?title=']);
// other sites may need to add code here to set titletail depending on how their urls work
pg.wiki.titlebase = protocol + '//' + pg.wiki.sitebase + titletail;
//pg.wiki.titlebase2 = protocol + '//' + joinPath([pg.wiki.sitebase, titletail2]);
pg.wiki.wikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.botInterfacePath;
pg.wiki.apiwikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.APIPath;
pg.wiki.articlebase = protocol + '//' + pg.wiki.sitebase + pg.wiki.articlePath;
pg.wiki.commonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.botInterfacePath;
pg.wiki.apicommonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.APIPath;
pg.re.basenames = RegExp( '^(' +
map( literalizeRegex, [ pg.wiki.titlebase, //pg.wiki.titlebase2,
pg.wiki.articlebase ]).join('|') + ')' );
}
//////////////////////////////////////////////////
// Global regexps
function setMainRegex() {
var reStart='[^:]*://';
var preTitles = literalizeRegex( mw.config.get('wgScriptPath') ) + '/(?:index[.]php|wiki[.]phtml)[?]title=';
preTitles += '|' + literalizeRegex( pg.wiki.articlePath + '/' );
var reEnd='(' + preTitles + ')([^&?#]*)[^#]*(?:#(.+))?';
pg.re.main = RegExp(reStart + literalizeRegex(pg.wiki.sitebase) + reEnd);
}
function setRegexps() {
// TODO: We shoud use an api call to get the aliases for special pages, now it does not work for non-English wikipedias:
// E.g., https://ru.wikipedia.org/w/api.php?action=query&meta=siteinfo&siprop=specialpagealiases&formatversion=2
setMainRegex();
var sp=nsRe(pg.nsSpecialId);
pg.re.urlNoPopup=RegExp('((title=|/)' + sp + '(?:%3A|:)|section=[0-9]|^#$)') ;
pg.re.contribs =RegExp('(title=|/)' + sp + '(?:%3A|:)Contributions' + '(&target=|/|/' + nsRe(pg.nsUserId)+':)(.*)') ;
pg.re.email =RegExp('(title=|/)' + sp + '(?:%3A|:)EmailUser' + '(&target=|/|/(?:' + nsRe(pg.nsUserId)+':)?)(.*)') ;
pg.re.backlinks =RegExp('(title=|/)' + sp + '(?:%3A|:)WhatLinksHere' + '(&target=|/)([^&]*)');
pg.re.specialdiff=RegExp('/' + sp + '(?:%3A|:)Diff/([^?#]*)');
//<NOLITE>
var im=nsReImage();
// note: tries to get images in infobox templates too, e.g. movie pages, album pages etc
// (^|\[\[)image: *([^|\]]*[^|\] ]) *
// (^|\[\[)image: *([^|\]]*[^|\] ])([^0-9\]]*([0-9]+) *px)?
// $4 = 120 as in 120px
pg.re.image = RegExp('(^|\\[\\[)' + im + ': *([^|\\]]*[^|\\] ])' +
'([^0-9\\]]*([0-9]+) *px)?|(?:\\n *[|]?|[|]) *' +
'(' + getValueOf('popupImageVarsRegexp') + ')' +
' *= *(?:\\[\\[ *)?(?:' + im + ':)?' +
'([^|]*?)(?:\\]\\])? *[|]? *\\n', 'img') ;
pg.re.imageBracketCount = 6;
pg.re.category = RegExp('\\[\\[' +nsRe(pg.nsCategoryId) +
': *([^|\\]]*[^|\\] ]) *', 'i');
pg.re.categoryBracketCount = 1;
pg.re.ipUser=RegExp('^' +
// IPv6
'(?::(?::|(?::[0-9A-Fa-f]{1,4}){1,7})|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,6}::|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){7})' +
// IPv4
'|(((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}' +
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9]))$');
pg.re.stub= RegExp(getValueOf('popupStubRegexp'), 'im');
pg.re.disambig=RegExp(getValueOf('popupDabRegexp'), 'im');
//</NOLITE>
// FIXME replace with general parameter parsing function, this is daft
pg.re.oldid=RegExp('[?&]oldid=([^&]*)');
pg.re.diff=RegExp('[?&]diff=([^&]*)');
}
//////////////////////////////////////////////////
// miscellany
function setupCache() {
// page caching
pg.cache.pages = [];
}
function setMisc() {
pg.current.link=null;
pg.current.links=[];
pg.current.linksHash={};
setupCache();
pg.timer.checkPopupPosition=null;
pg.counter.loop=0;
// ids change with each popup: popupImage0, popupImage1 etc
pg.idNumber=0;
// for myDecodeURI
pg.misc.decodeExtras = [
{from: '%2C', to: ',' },
{from: '_', to: ' ' },
{from: '%24', to: '$'},
{from: '%26', to: '&' } // no ,
];
pg.misc.userAgent = 'Navigation popups/1.0 (' + mw.config.get( 'wgServerName' ) +')';
}
// We need a callback since this might end up asynchronous because of
// the mw.loader.using() call.
function setupPopups( callback ) {
if ( setupPopups.completed ) {
if ( typeof callback === 'function' ) {
callback();
}
return;
}
// These dependencies are also enforced from the gadget,
// but not everyone loads this as a gadget, so double check
mw.loader.using( ['mediawiki.util', 'mediawiki.user', 'user.options'] ).then( function() {
// NB translatable strings should be set up first (strings.js)
// basics
setupDebugging();
setSiteInfo();
setTitleBase();
setOptions(); // see options.js
setUserInfo();
// namespaces etc
setNamespaces();
setInterwiki();
// regexps
setRegexps();
setRedirs();
// other stuff
setMisc();
setupLivePreview();
// main deal here
setupTooltips();
log('In setupPopups(), just called setupTooltips()');
Navpopup.tracker.enable();
setupPopups.completed = true;
if ( typeof callback === 'function' ) {
callback();
}
});
}
// ENDFILE: init.js
// STARTFILE: navlinks.js
//<NOLITE>
//////////////////////////////////////////////////
// navlinks... let the fun begin
//
function defaultNavlinkSpec() {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
if (getValueOf('popupLastEditLink')) {
str += '*<<lastEdit|shortcut=/>>|<<lastContrib>>|<<sinceMe>>if(oldid){|<<oldEdit>>|<<diffCur>>}';
}
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>*<<userlog|shortcut=L|log>>';
str+='if(ipuser){*<<arin>>}if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>|<<blocklog|log>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' + editstr + '}';
var historystr='<<history|shortcut=h>>|<<editors|shortcut=E|>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str+='<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>*<<move|shortcut=m>>';
// admin links
str += 'if(admin){<br><<unprotect|unprotectShort>>|<<protect|shortcut=p>>|<<protectlog|log>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>|<<deletelog|log>>}';
return str;
}
function navLinksHTML (article, hint, params) { //oldid, rcid) {
var str = '<span class="popupNavLinks">' + defaultNavlinkSpec() + '</span>';
// BAM
return navlinkStringToHTML(str, article, params);
}
function expandConditionalNavlinkString(s,article,z,recursionCount) {
var oldid=z.oldid, rcid=z.rcid, diff=z.diff;
// nested conditionals (up to 10 deep) are ok, hopefully! (work from the inside out)
if (typeof recursionCount!=typeof 0) { recursionCount=0; }
var conditionalSplitRegex=RegExp(
//(1 if \\( (2 2) \\) {(3 3)} (4 else {(5 5)} 4)1)
'(;?\\s*if\\s*\\(\\s*([\\w]*)\\s*\\)\\s*\\{([^{}]*)\\}(\\s*else\\s*\\{([^{}]*?)\\}|))', 'i');
var splitted=s.parenSplit(conditionalSplitRegex);
// $1: whole conditional
// $2: test condition
// $3: true expansion
// $4: else clause (possibly empty)
// $5: false expansion (possibly null)
var numParens=5;
var ret = splitted[0];
for (var i=1; i<splitted.length; i=i+numParens+1) {
var testString=splitted[i+2-1];
var trueString=splitted[i+3-1];
var falseString=splitted[i+5-1];
if (typeof falseString=='undefined' || !falseString) { falseString=''; }
var testResult=null;
switch (testString) {
case 'user':
testResult=(article.userName())?true:false;
break;
case 'talk':
testResult=(article.talkPage())?false:true; // talkPage converts _articles_ to talkPages
break;
case 'admin':
testResult=getValueOf('popupAdminLinks')?true:false;
break;
case 'oldid':
testResult=(typeof oldid != 'undefined' && oldid)?true:false;
break;
case 'rcid':
testResult=(typeof rcid != 'undefined' && rcid)?true:false;
break;
case 'ipuser':
testResult=(article.isIpUser())?true:false;
break;
case 'mainspace_en':
testResult=isInMainNamespace(article) &&
pg.wiki.hostname=='en.wikipedia.org';
break;
case 'wikimedia':
testResult=(pg.wiki.wikimedia) ? true : false;
break;
case 'diff':
testResult=(typeof diff != 'undefined' && diff)?true:false;
break;
}
switch(testResult) {
case null: ret+=splitted[i]; break;
case true: ret+=trueString; break;
case false: ret+=falseString; break;
}
// append non-conditional string
ret += splitted[i+numParens];
}
if (conditionalSplitRegex.test(ret) && recursionCount < 10) {
return expandConditionalNavlinkString(ret,article,z,recursionCount+1);
}
return ret;
}
function navlinkStringToArray(s, article, params) {
s=expandConditionalNavlinkString(s,article,params);
var splitted=s.parenSplit(RegExp('<<(.*?)>>'));
var ret=[];
for (var i=0; i<splitted.length; ++i) {
if (i%2) { // i odd, so s is a tag
var t=new navlinkTag();
var ss=splitted[i].split('|');
t.id=ss[0];
for (var j=1; j<ss.length; ++j) {
var sss=ss[j].split('=');
if (sss.length>1) {
t[sss[0]]=sss[1];
}
else { // no assignment (no "="), so treat this as a title (overwriting the last one)
t.text=popupString(sss[0]);
}
}
t.article=article;
var oldid=params.oldid, rcid=params.rcid, diff=params.diff;
if (typeof oldid !== 'undefined' && oldid !== null) { t.oldid=oldid; }
if (typeof rcid !== 'undefined' && rcid !== null) { t.rcid=rcid; }
if (typeof diff !== 'undefined' && diff !== null) { t.diff=diff; }
if (!t.text && t.id !== 'mainlink') { t.text=popupString(t.id); }
ret.push(t);
}
else { // plain HTML
ret.push(splitted[i]);
}
}
return ret;
}
function navlinkSubstituteHTML(s) {
return s.split('*').join(getValueOf('popupNavLinkSeparator'))
.split('<menurow>').join('<li class="popup_menu_row">')
.split('</menurow>').join('</li>')
.split('<menu>').join('<ul class="popup_menu">')
.split('</menu>').join('</ul>');
}
function navlinkDepth(magic,s) {
return s.split('<' + magic + '>').length - s.split('</' + magic + '>').length;
}
// navlinkString: * becomes the separator
// <<foo|bar=baz|fubar>> becomes a foo-link with attribute bar='baz'
// and visible text 'fubar'
// if(test){...} and if(test){...}else{...} work too (nested ok)
function navlinkStringToHTML(s,article,params) {
//limitAlert(navlinkStringToHTML, 5, 'navlinkStringToHTML\n' + article + '\n' + (typeof article));
var p=navlinkStringToArray(s,article,params);
var html='';
var menudepth = 0; // nested menus not currently allowed, but doesn't do any harm to code for it
var menurowdepth = 0;
for (var i=0; i<p.length; ++i) {
if (typeof p[i] == typeof '') {
html+=navlinkSubstituteHTML(p[i]);
menudepth += navlinkDepth('menu', p[i]);
menurowdepth += navlinkDepth('menurow', p[i]);
// if (menudepth === 0) {
// tagType='span';
// } else if (menurowdepth === 0) {
// tagType='li';
// } else {
// tagType = null;
// }
} else if (typeof p[i].type != 'undefined' && p[i].type=='navlinkTag') {
if (menudepth > 0 && menurowdepth === 0) {
html += '<li class="popup_menu_item">' + p[i].html() + '</li>';
} else {
html+=p[i].html();
}
}
}
return html;
}
function navlinkTag() {
this.type='navlinkTag';
}
navlinkTag.prototype.html=function () {
this.getNewWin();
this.getPrintFunction();
var html='';
var opening, closing;
var tagType='span';
if (!tagType) {
opening = ''; closing = '';
} else {
opening = '<' + tagType + ' class="popup_' + this.id + '">';
closing = '</' + tagType + '>';
}
if (typeof this.print!='function') {
errlog ('Oh dear - invalid print function for a navlinkTag, id='+this.id);
} else {
html=this.print(this);
if (typeof html != typeof '') {html='';}
else if (typeof this.shortcut!='undefined') html=addPopupShortcut(html, this.shortcut);
}
return opening + html + closing;
};
navlinkTag.prototype.getNewWin=function() {
getValueOf('popupLinksNewWindow');
if (typeof pg.option.popupLinksNewWindow[this.id] === 'undefined') { this.newWin=null; }
this.newWin=pg.option.popupLinksNewWindow[this.id];
};
navlinkTag.prototype.getPrintFunction=function() { //think about this some more
// this.id and this.article should already be defined
if (typeof this.id!=typeof '' || typeof this.article!=typeof {} ) { return; }
this.noPopup=1;
switch (this.id) {
case 'contribs': case 'history': case 'whatLinksHere':
case 'userPage': case 'monobook': case 'userTalk':
case 'talk': case 'article': case 'lastEdit':
this.noPopup=null;
}
switch (this.id) {
case 'email': case 'contribs': case 'block': case 'unblock':
case 'userlog': case 'userSpace': case 'deletedContribs':
this.article=this.article.userName();
}
switch (this.id) {
case 'userTalk': case 'newUserTalk': case 'editUserTalk':
case 'userPage': case 'monobook': case 'editMonobook': case 'blocklog':
this.article=this.article.userName(true);
/* fall through */
case 'pagelog': case 'deletelog': case 'protectlog':
delete this.oldid;
}
if (this.id=='editMonobook' || this.id=='monobook') { this.article.append('/monobook.js'); }
if (this.id != 'mainlink') {
// FIXME anchor handling should be done differently with Title object
this.article=this.article.removeAnchor();
// if (typeof this.text=='undefined') this.text=popupString(this.id);
}
switch (this.id) {
case 'undelete': this.print=specialLink; this.specialpage='Undelete'; this.sep='/'; break;
case 'whatLinksHere': this.print=specialLink; this.specialpage='Whatlinkshere'; break;
case 'relatedChanges': this.print=specialLink; this.specialpage='Recentchangeslinked'; break;
case 'move': this.print=specialLink; this.specialpage='Movepage'; break;
case 'contribs': this.print=specialLink; this.specialpage='Contributions'; break;
case 'deletedContribs':this.print=specialLink; this.specialpage='Deletedcontributions'; break;
case 'email': this.print=specialLink; this.specialpage='EmailUser'; this.sep='/'; break;
case 'block': this.print=specialLink; this.specialpage='Blockip'; this.sep='&ip='; break;
case 'unblock': this.print=specialLink; this.specialpage='Ipblocklist'; this.sep='&action=unblock&ip='; break;
case 'userlog': this.print=specialLink; this.specialpage='Log'; this.sep='&user='; break;
case 'blocklog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=block&page='; break;
case 'pagelog': this.print=specialLink; this.specialpage='Log'; this.sep='&page='; break;
case 'protectlog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=protect&page='; break;
case 'deletelog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=delete&page='; break;
case 'userSpace': this.print=specialLink; this.specialpage='PrefixIndex'; this.sep='&namespace=2&prefix='; break;
case 'search': this.print=specialLink; this.specialpage='Search'; this.sep='&fulltext=Search&search='; break;
case 'thank': this.print=specialLink; this.specialpage='Thanks'; this.sep='/'; this.article.value = this.diff; break;
case 'unwatch': case 'watch':
this.print=magicWatchLink; this.action=this.id+'&autowatchlist=1&autoimpl=' + popupString('autoedit_version') + '&actoken='+autoClickToken(); break;
case 'history': case 'historyfeed':
case 'unprotect': case 'protect':
this.print=wikiLink; this.action=this.id; break;
case 'delete':
this.print=wikiLink; this.action='delete';
if (this.article.namespaceId()==pg.nsImageId) {
var img=this.article.stripNamespace();
this.action+='&image='+img;
}
break;
case 'markpatrolled':
case 'edit': // editOld should keep the oldid, but edit should not.
delete this.oldid;
/* fall through */
case 'view': case 'purge': case 'render':
this.print=wikiLink;
this.action=this.id; break;
case 'raw':
this.print=wikiLink; this.action='raw'; break;
case 'new':
this.print=wikiLink; this.action='edit§ion=new'; break;
case 'mainlink':
if (typeof this.text=='undefined') { this.text=this.article.toString().entify(); }
if (getValueOf('popupSimplifyMainLink') && isInStrippableNamespace(this.article)) {
// only show the /subpage part of the title text
var s=this.text.split('/'); this.text=s[s.length-1];
if (this.text==='' && s.length > 1) { this.text=s[s.length-2]; }
}
this.print=titledWikiLink;
if (typeof this.title==='undefined' && pg.current.link && typeof pg.current.link.href !== 'undefined') {
this.title=safeDecodeURI((pg.current.link.originalTitle)?pg.current.link.originalTitle:this.article);
if (typeof this.oldid !== 'undefined' && this.oldid) {
this.title=tprintf('Revision %s of %s', [this.oldid, this.title]);
}
}
this.action='view'; break;
case 'userPage':
case 'article':
case 'monobook':
case 'editMonobook':
case 'editArticle':
delete this.oldid;
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.article=this.article.articleFromTalkOrArticle();
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.print=wikiLink;
if (this.id.indexOf('edit')===0) {
this.action='edit';
} else { this.action='view';}
break;
case 'userTalk':
case 'talk':
this.article=this.article.talkPage();
delete this.oldid;
this.print=wikiLink;
this.action='view'; break;
case 'arin':
this.print=arinLink; break;
case 'count':
this.print=editCounterLink; break;
case 'google':
this.print=googleLink; break;
case 'editors':
this.print=editorListLink; break;
case 'globalsearch':
this.print=globalSearchLink; break;
case 'lastEdit':
this.print=titledDiffLink;
this.title=popupString('Show the last edit');
this.from='prev'; this.to='cur'; break;
case 'oldEdit':
this.print=titledDiffLink;
this.title=popupString('Show the edit made to get revision') + ' ' + this.oldid;
this.from='prev'; this.to=this.oldid; break;
case 'editOld':
this.print=wikiLink; this.action='edit'; break;
case 'undo':
this.print=wikiLink; this.action='edit&undo='; break;
case 'revert':
this.print=wikiLink; this.action='revert'; break;
case 'nullEdit':
this.print=wikiLink; this.action='nullEdit'; break;
case 'diffCur':
this.print=titledDiffLink;
this.title=tprintf('Show changes since revision %s', [this.oldid]);
this.from=this.oldid; this.to='cur'; break;
case 'editUserTalk':
case 'editTalk':
delete this.oldid;
this.article=this.article.talkPage();
this.action='edit'; this.print=wikiLink; break;
case 'newUserTalk':
case 'newTalk':
this.article=this.article.talkPage();
this.action='edit§ion=new'; this.print=wikiLink; break;
case 'lastContrib':
case 'sinceMe':
this.print=magicHistoryLink;
break;
case 'togglePreviews':
this.text=popupString(pg.option.simplePopups ? 'enable previews' : 'disable previews');
/* fall through */
case 'disablePopups': case 'purgePopups':
this.print=popupMenuLink;
break;
default:
this.print=function () {return 'Unknown navlink type: '+this.id+'';};
}
};
//
// end navlinks
//////////////////////////////////////////////////
//</NOLITE>
// ENDFILE: navlinks.js
// STARTFILE: shortcutkeys.js
//<NOLITE>
function popupHandleKeypress(evt) {
var keyCode = window.event ? window.event.keyCode : ( evt.keyCode ? evt.keyCode : evt.which);
if (!keyCode || !pg.current.link || !pg.current.link.navpopup) { return; }
if (keyCode==27) { // escape
killPopup();
return false; // swallow keypress
}
var letter=String.fromCharCode(keyCode);
var links=pg.current.link.navpopup.mainDiv.getElementsByTagName('A');
var startLink=0;
var i,j;
if (popupHandleKeypress.lastPopupLinkSelected) {
for (i=0; i<links.length; ++i) {
if (links[i]==popupHandleKeypress.lastPopupLinkSelected) { startLink=i; }
}
}
for (j=0; j<links.length; ++j) {
i=(startLink + j + 1) % links.length;
if (links[i].getAttribute('popupkey')==letter) {
if (evt && evt.preventDefault) evt.preventDefault();
links[i].focus();
popupHandleKeypress.lastPopupLinkSelected=links[i];
return false; // swallow keypress
}
}
// pass keypress on
if (document.oldPopupOnkeypress) { return document.oldPopupOnkeypress(evt); }
return true;
}
function addPopupShortcuts() {
if (document.onkeypress!=popupHandleKeypress) {
document.oldPopupOnkeypress=document.onkeypress;
}
document.onkeypress=popupHandleKeypress;
}
function rmPopupShortcuts() {
popupHandleKeypress.lastPopupLinkSelected=null;
try {
if (document.oldPopupOnkeypress && document.oldPopupOnkeypress==popupHandleKeypress) {
// panic
document.onkeypress=null; //function () {};
return;
}
document.onkeypress=document.oldPopupOnkeypress;
} catch (nasties) { /* IE goes here */ }
}
function addLinkProperty(html, property) {
// take "<a href=...>...</a> and add a property
// not sophisticated at all, easily broken
var i=html.indexOf('>');
if (i<0) { return html; }
return html.substring(0,i) + ' ' + property + html.substring(i);
}
function addPopupShortcut(html, key) {
if (!getValueOf('popupShortcutKeys')) { return html; }
var ret= addLinkProperty(html, 'popupkey="'+key+'"');
if (key==' ') { key=popupString('spacebar'); }
return ret.replace(RegExp('^(.*?)(title=")(.*?)(".*)$', 'i'),'$1$2$3 ['+key+']$4');
}
//</NOLITE>
// ENDFILE: shortcutkeys.js
// STARTFILE: diffpreview.js
//<NOLITE>
//lets jump through hoops to find the rev ids we need to retrieve
function loadDiff(article, oldid, diff, navpop) {
navpop.diffData={ oldRev: {}, newRev: {} };
mw.loader.using( 'mediawiki.api' ).then( function() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'compare',
prop: 'ids|title'
};
if(article.title){
params.fromtitle = article.title;
}
switch (diff) {
case 'cur':
switch ( oldid ) {
case null:
case '':
case 'prev':
// this can only work if we have the title
// cur -> prev
params.torelative = 'prev';
break;
default:
params.fromrev = oldid;
params.torelative = 'cur';
break;
}
break;
case 'prev':
if( oldid ) {
params.fromrev = oldid;
} else {
params.fromtitle;
}
params.torelative = 'prev';
break;
case 'next':
params.fromrev = oldid || 0;
params.torelative = 'next';
break;
default:
params.fromrev = oldid || 0;
params.torev = diff || 0;
break;
}
api.get( params ).then( function( data ) {
navpop.diffData.oldRev.revid = data.compare.fromrevid;
navpop.diffData.newRev.revid = data.compare.torevid;
addReviewLink(navpop, 'popupMiscTools');
var go = function() {
pendingNavpopTask(navpop);
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
url += 'revids=' + navpop.diffData.oldRev.revid + '|' + navpop.diffData.newRev.revid;
url += '&prop=revisions&rvprop=ids|timestamp|content';
getPageWithCaching(url, doneDiff, navpop);
return true; // remove hook once run
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_DIFFS'); }
} );
} );
}
// Put a "mark patrolled" link to an element target
// TODO: Allow patrol a revision, as well as a diff
function addReviewLink (navpop, target) {
if (! pg.user.canReview) return;
// If 'newRev' is older than 'oldRev' than it could be confusing, so we do not show the review link.
if (navpop.diffData.newRev.revid <= navpop.diffData.oldRev.revid) return;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
prop: 'info|flagged',
revids: navpop.diffData.oldRev.revid,
formatversion : 2
};
api.get (params).then(function(data){
var stable_revid = data.query.pages[0].flagged && data.query.pages[0].flagged.stable_revid || 0;
// The diff can be reviewed if the old version is the last reviewed version
// TODO: Other possible conditions that we may want to implement instead of this one:
// * old version is patrolled and the new version is not patrolled
// * old version is patrolled and the new version is more recent than the last reviewed version
if (stable_revid == navpop.diffData.oldRev.revid) {
var a = document.createElement('a');
a.innerHTML = popupString('mark patrolled');
a.title=popupString('markpatrolledHint');
a.onclick = function() {
var params = {
action: 'review',
revid: navpop.diffData.newRev.revid,
comment: tprintf('defaultpopupReviewedSummary', [navpop.diffData.oldRev.revid, navpop.diffData.newRev.revid])
};
api.postWithToken('csrf',params).done(function(){
a.style.display = "none";
// TODO: Update current page and other already constructed popups
} ).fail(function(){
alert(popupString('Could not marked this edit as patrolled'));
});
};
setPopupHTML(a, target, navpop.idNumber,null,true);
}
});
}
function doneDiff(download) {
if (!download.owner || !download.owner.diffData) { return; }
var navpop=download.owner;
completedNavpopTask(navpop);
var pages, revisions=[];
try{
// Process the downloads
pages = getJsObj(download.data).query.pages;
for(var i=0; i < pages.length; i++ ) {
revisions = revisions.concat(pages[i].revisions);
}
for(i=0; i< revisions.length; i++){
if(revisions[i].revid == navpop.diffData.oldRev.revid) {
navpop.diffData.oldRev.revision = revisions[i];
} else if (revisions[i].revid == navpop.diffData.newRev.revid) {
navpop.diffData.newRev.revision = revisions[i];
}
}
} catch(someError) {
errlog( 'Could not get diff' );
}
insertDiff(navpop);
}
function rmBoringLines(a,b,context) {
if (typeof context == 'undefined') { context=2; }
// this is fairly slow... i think it's quicker than doing a word-based diff from the off, though
var aa=[], aaa=[];
var bb=[], bbb=[];
var i, j;
// first, gather all disconnected nodes in a and all crossing nodes in a and b
for (i=0; i<a.length; ++i ) {
if(!a[i].paired) { aa[i]=1; }
else if (countCrossings(b,a,i, true)) {
aa[i]=1;
bb[ a[i].row ] = 1;
}
}
// pick up remaining disconnected nodes in b
for (i=0; i<b.length; ++i ) {
if (bb[i]==1) { continue; }
if(!b[i].paired) { bb[i]=1; }
}
// another pass to gather context: we want the neighbours of included nodes which are not yet included
// we have to add in partners of these nodes, but we don't want to add context for *those* nodes in the next pass
for (i=0; i<b.length; ++i) {
if ( bb[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(b.length, i+context); ++j) {
if ( !bb[j] ) { bb[j] = 1; aa[ b[j].row ] = 0.5; }
}
}
}
for (i=0; i<a.length; ++i) {
if ( aa[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(a.length, i+context); ++j) {
if ( !aa[j] ) { aa[j] = 1; bb[ a[j].row ] = 0.5; }
}
}
}
for (i=0; i<bb.length; ++i) {
if (bb[i] > 0) { // it's a row we need
if (b[i].paired) { bbb.push(b[i].text); } // joined; partner should be in aa
else {
bbb.push(b[i]);
}
}
}
for (i=0; i<aa.length; ++i) {
if (aa[i] > 0) { // it's a row we need
if (a[i].paired) { aaa.push(a[i].text); } // joined; partner should be in aa
else {
aaa.push(a[i]);
}
}
}
return { a: aaa, b: bbb};
}
function stripOuterCommonLines(a,b,context) {
var i=0;
while (i<a.length && i < b.length && a[i]==b[i]) { ++i; }
var j=a.length-1; var k=b.length-1;
while ( j>=0 && k>=0 && a[j]==b[k] ) { --j; --k; }
return { a: a.slice(Math.max(0,i - 1 - context), Math.min(a.length+1, j + context+1)),
b: b.slice(Math.max(0,i - 1 - context), Math.min(b.length+1, k + context+1)) };
}
function insertDiff(navpop) {
// for speed reasons, we first do a line-based diff, discard stuff that seems boring, then do a word-based diff
// FIXME: sometimes this gives misleading diffs as distant chunks are squashed together
var oldlines = navpop.diffData.oldRev.revision.content.split('\n');
var newlines = navpop.diffData.newRev.revision.content.split('\n');
var inner=stripOuterCommonLines(oldlines,newlines,getValueOf('popupDiffContextLines'));
oldlines=inner.a; newlines=inner.b;
var truncated=false;
getValueOf('popupDiffMaxLines');
if (oldlines.length > pg.option.popupDiffMaxLines || newlines.length > pg.option.popupDiffMaxLines) {
// truncate
truncated=true;
inner=stripOuterCommonLines(oldlines.slice(0,pg.option.popupDiffMaxLines),
newlines.slice(0,pg.option.popupDiffMaxLines),
pg.option.popupDiffContextLines);
oldlines=inner.a; newlines=inner.b;
}
var lineDiff=diff(oldlines, newlines);
var lines2=rmBoringLines(lineDiff.o, lineDiff.n);
var oldlines2=lines2.a; var newlines2=lines2.b;
var simpleSplit = !String.prototype.parenSplit.isNative;
var html='<hr />';
if (getValueOf('popupDiffDates')) {
html += diffDatesTable(navpop);
html += '<hr />';
}
html += shortenDiffString(
diffString(oldlines2.join('\n'), newlines2.join('\n'), simpleSplit),
getValueOf('popupDiffContextCharacters') ).join('<hr />');
setPopupTipsAndHTML(html.split('\n').join('<br>') +
(truncated ? '<hr /><b>'+popupString('Diff truncated for performance reasons')+'</b>' : '') ,
'popupPreview', navpop.idNumber);
}
function diffDatesTable( navpop ) {
var html='<table class="popup_diff_dates">';
html += diffDatesTableRow( navpop.diffData.newRev.revision, tprintf('New revision'));
html += diffDatesTableRow( navpop.diffData.oldRev.revision, tprintf('Old revision'));
html += '</table>';
return html;
}
function diffDatesTableRow( revision, label ) {
var txt='';
var lastModifiedDate = new Date(revision.timestamp);
var datePrint=getValueOf('popupDiffDatePrinter');
if (typeof lastModifiedDate[datePrint] == 'function') {
var d2 = adjustDate(lastModifiedDate, getTimeOffset());
txt = dayFormat(d2, true) + ' ' + timeFormat(d2, true);
} else {
txt = tprintf('Invalid %s %s', ['popupDiffDatePrinter', datePrint]);
}
var revlink = generalLink({url: mw.config.get('wgScript') + '?oldid='+revision.revid,
text: label, title: label});
return simplePrintf('<tr><td>%s</td><td>%s</td></tr>', [ revlink, txt ]);
}
//</NOLITE>
// ENDFILE: diffpreview.js
// STARTFILE: links.js
//<NOLITE>
/////////////////////
// LINK GENERATION //
/////////////////////
// titledDiffLink --> titledWikiLink --> generalLink
// wikiLink --> titledWikiLink --> generalLink
// editCounterLink --> generalLink
// TODO Make these functions return Element objects, not just raw HTML strings.
function titledDiffLink(l) { // article, text, title, from, to) {
return titledWikiLink({article: l.article, action: l.to + '&oldid=' + l.from,
newWin: l.newWin,
noPopup: l.noPopup,
text: l.text, title: l.title,
/* hack: no oldid here */
actionName: 'diff'});
}
function wikiLink(l) {
//{article:article, action:action, text:text, oldid, newid}) {
if (! (typeof l.article == typeof {} &&
typeof l.action == typeof '' &&
typeof l.text==typeof '')) return null;
if (typeof l.oldid == 'undefined') { l.oldid=null; }
var savedOldid = l.oldid;
if (!/^(edit|view|revert|render)$|^raw/.test(l.action)) { l.oldid=null; }
var hint=popupString(l.action + 'Hint'); // revertHint etc etc etc
var oldidData=[l.oldid, safeDecodeURI(l.article)];
var revisionString = tprintf('revision %s of %s', oldidData);
log('revisionString='+revisionString);
switch (l.action) {
case 'edit§ion=new': hint = popupString('newSectionHint'); break;
case 'edit&undo=':
if (l.diff && l.diff != 'prev' && savedOldid ) {
l.action += l.diff + '&undoafter=' + savedOldid;
} else if (savedOldid) {
l.action += savedOldid;
}
hint = popupString('undoHint');
break;
case 'raw&ctype=text/css': hint=popupString('rawHint'); break;
case 'revert':
var p=parseParams(pg.current.link.href);
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=' + revertSummary(l.oldid, p.diff);
if (p.diff=='prev') {
l.action += '&direction=prev';
revisionString = tprintf('the revision prior to revision %s of %s', oldidData);
}
if (getValueOf('popupRevertSummaryPrompt')) { l.action += '&autosummaryprompt=true'; }
if (getValueOf('popupMinorReverts')) { l.action += '&autominor=true'; }
log('revisionString is now '+revisionString);
break;
case 'nullEdit':
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=null';
break;
case 'historyfeed':
l.action='history&feed=rss';
break;
case 'markpatrolled':
l.action='markpatrolled&rcid='+l.rcid;
}
if (hint) {
if (l.oldid) {
hint = simplePrintf(hint, [revisionString]);
}
else {
hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
}
}
else {
hint = safeDecodeURI(l.article + '&action=' + l.action) + (l.oldid) ? '&oldid='+l.oldid : '';
}
return titledWikiLink({article: l.article, action: l.action, text: l.text, newWin:l.newWin,
title: hint, oldid: l.oldid, noPopup: l.noPopup, onclick: l.onclick});
}
function revertSummary(oldid, diff) {
var ret='';
if (diff == 'prev') {
ret=getValueOf('popupQueriedRevertToPreviousSummary');
} else { ret = getValueOf('popupQueriedRevertSummary'); }
return ret + '&autorv=' + oldid;
}
function titledWikiLink(l) {
// possible properties of argument:
// article, action, text, title, oldid, actionName, className, noPopup
// oldid = null is fine here
// article and action are mandatory args
if (typeof l.article == 'undefined' || typeof l.action=='undefined') {
errlog('got undefined article or action in titledWikiLink');
return null;
}
var base = pg.wiki.titlebase + l.article.urlString();
var url=base;
if (typeof l.actionName=='undefined' || !l.actionName) { l.actionName='action'; }
// no need to add &action=view, and this confuses anchors
if (l.action != 'view') { url = base + '&' + l.actionName + '=' + l.action; }
if (typeof l.oldid!='undefined' && l.oldid) { url+='&oldid='+l.oldid; }
var cssClass=pg.misc.defaultNavlinkClassname;
if (typeof l.className!='undefined' && l.className) { cssClass=l.className; }
return generalNavLink({url: url, newWin: l.newWin,
title: (typeof l.title != 'undefined') ? l.title : null,
text: (typeof l.text!='undefined')?l.text:null,
className: cssClass, noPopup:l.noPopup, onclick:l.onclick});
}
pg.fn.getLastContrib = function getLastContrib(wikipage, newWin) {
getHistoryInfo(wikipage, function(x) {
processLastContribInfo(x, {page: wikipage, newWin: newWin});
});
};
function processLastContribInfo(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: an odd thing happened. Please retry.'); return; }
if(!info.firstNewEditor) {
alert(tprintf('Only found one editor: %s made %s edits', [info.edits[0].editor,info.edits.length]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+info.firstNewEditor.oldid;
displayUrl(newUrl, stuff.newWin);
}
pg.fn.getDiffSinceMyEdit = function getDiffSinceMyEdit(wikipage, newWin) {
getHistoryInfo(wikipage, function(x){
processDiffSinceMyEdit(x, {page: wikipage, newWin: newWin});
});
};
function processDiffSinceMyEdit(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: something fishy happened. Please try again.'); return; }
var friendlyName=stuff.page.split('_').join(' ');
if(!info.myLastEdit) {
alert(tprintf('Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
[info.userName, getValueOf('popupHistoryLimit'), friendlyName]));
return;
}
if(info.myLastEdit.index === 0) {
alert(tprintf("%s seems to be the last editor to the page %s", [info.userName, friendlyName]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+ info.myLastEdit.oldid;
displayUrl(newUrl, stuff.newWin);
}
function displayUrl(url, newWin){
if(newWin) { window.open(url); }
else { document.location=url; }
}
pg.fn.purgePopups = function purgePopups() {
processAllPopups(true);
setupCache(); // deletes all cached items (not browser cached, though...)
pg.option={};
abortAllDownloads();
};
function processAllPopups(nullify, banish) {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
if (!pg.current.links[i].navpopup) { continue; }
if (nullify || banish) pg.current.links[i].navpopup.banish();
pg.current.links[i].simpleNoMore=false;
if (nullify) pg.current.links[i].navpopup=null;
}
}
pg.fn.disablePopups = function disablePopups(){
processAllPopups(false, true);
setupTooltips(null, true);
};
pg.fn.togglePreviews = function togglePreviews() {
processAllPopups(true, true);
pg.option.simplePopups=!pg.option.simplePopups;
abortAllDownloads();
};
function magicWatchLink(l) {
//Yuck!! Would require a thorough redesign to add this as a click event though ...
l.onclick = simplePrintf( 'pg.fn.modifyWatchlist(\'%s\',\'%s\');return false;', [l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), this.id] );
return wikiLink(l);
}
pg.fn.modifyWatchlist = function modifyWatchlist(title, action) {
var reqData = {
'action': 'watch',
'formatversion': 2,
'titles': title,
'uselang': mw.config.get('wgUserLanguage')
};
if ( action === 'unwatch' ) reqData.unwatch = true;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
// Load the Addedwatchtext or Removedwatchtext message and show it
var mwTitle = mw.Title.newFromText( title );
var messageName;
if ( mwTitle && mwTitle.getNamespaceId() > 0 && mwTitle.getNamespaceId() % 2 === 1 ) {
messageName = action === 'watch' ? 'addedwatchtext-talk' : 'removedwatchtext-talk';
} else {
messageName = action === 'watch' ? 'addedwatchtext' : 'removedwatchtext';
}
$.when(
api.postWithToken( 'watch', reqData ),
mw.loader.using( [ 'mediawiki.api', 'mediawiki.jqueryMsg' ] ).then( function () {
return api.loadMessagesIfMissing( [ messageName ] );
} )
).done( function () {
mw.notify( mw.message( messageName, title ).parseDom() );
} );
};
function magicHistoryLink(l) {
// FIXME use onclick change href trick to sort this out instead of window.open
var jsUrl='', title='', onClick='';
switch(l.id) {
case 'lastContrib':
onClick=simplePrintf('pg.fn.getLastContrib(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('lastContribHint');
break;
case 'sinceMe':
onClick=simplePrintf('pg.fn.getDiffSinceMyEdit(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('sinceMeHint');
break;
}
jsUrl = 'javascript:' + onClick; // jshint ignore:line
onClick += ';return false;';
return generalNavLink({url: jsUrl, newWin: false, // can't have new windows with JS links, I think
title: title, text: l.text, noPopup: l.noPopup, onclick: onClick });
}
function popupMenuLink(l) {
var jsUrl=simplePrintf('javascript:pg.fn.%s()', [l.id]); // jshint ignore:line
var title=popupString(simplePrintf('%sHint', [l.id]));
var onClick=simplePrintf('pg.fn.%s();return false;', [l.id]);
return generalNavLink({url: jsUrl, newWin:false, title:title, text:l.text, noPopup:l.noPopup, onclick: onClick});
}
function specialLink(l) {
// properties: article, specialpage, text, sep
if (typeof l.specialpage=='undefined'||!l.specialpage) return null;
var base = pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId]+':'+l.specialpage;
if (typeof l.sep == 'undefined' || l.sep === null) l.sep='&target=';
var article=l.article.urlString({keepSpaces: l.specialpage=='Search'});
var hint=popupString(l.specialpage+'Hint');
switch (l.specialpage) {
case 'Log':
switch (l.sep) {
case '&user=': hint=popupString('userLogHint'); break;
case '&type=block&page=': hint=popupString('blockLogHint'); break;
case '&page=': hint=popupString('pageLogHint'); break;
case '&type=protect&page=': hint=popupString('protectLogHint'); break;
case '&type=delete&page=': hint=popupString('deleteLogHint'); break;
default: log('Unknown log type, sep=' + l.sep); hint='Missing hint (FIXME)';
}
break;
case 'PrefixIndex': article += '/'; break;
}
if (hint) hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
else hint = safeDecodeURI(l.specialpage+':'+l.article) ;
var url = base + l.sep + article;
return generalNavLink({url: url, title: hint, text: l.text, newWin:l.newWin, noPopup:l.noPopup});
}
function generalLink(l) {
// l.url, l.text, l.title, l.newWin, l.className, l.noPopup, l.onclick
if (typeof l.url=='undefined') return null;
// only quotation marks in the url can screw us up now... I think
var url=l.url.split('"').join('%22');
var ret='<a href="' + url + '"';
if (typeof l.title!='undefined' && l.title) { ret += ' title="' + pg.escapeQuotesHTML(l.title) + '"'; }
if (typeof l.onclick!='undefined' && l.onclick) { ret += ' onclick="' + pg.escapeQuotesHTML(l.onclick) + '"'; }
if (l.noPopup) { ret += ' noPopup=1'; }
var newWin;
if (typeof l.newWin=='undefined' || l.newWin === null) { newWin=getValueOf('popupNewWindows'); }
else { newWin=l.newWin; }
if (newWin) { ret += ' target="_blank"'; }
if (typeof l.className!='undefined'&&l.className) { ret+=' class="'+l.className+'"'; }
ret += '>';
if (typeof l.text==typeof '') { ret+= l.text; }
ret +='</a>';
return ret;
}
function appendParamsToLink(linkstr, params) {
var sp=linkstr.parenSplit(RegExp('(href="[^"]+?)"', 'i'));
if (sp.length<2) return null;
var ret=sp.shift() + sp.shift();
ret += '&' + params + '"';
ret += sp.join('');
return ret;
}
function changeLinkTargetLink(x) { // newTarget, text, hint, summary, clickButton, minor, title (optional), alsoChangeLabel {
if (x.newTarget) {
log ('changeLinkTargetLink: newTarget=' + x.newTarget);
}
if (x.oldTarget !== decodeURIComponent( x.oldTarget ) ) {
log ('This might be an input problem: ' + x.oldTarget );
}
// FIXME: first character of page title as well as namespace should be case insensitive
// eg [[category:X1]] and [[Category:X1]] are equivalent
// this'll break if charAt(0) is nasty
var cA = mw.util.escapeRegExp(x.oldTarget);
var chs = cA.charAt(0).toUpperCase();
chs='['+chs + chs.toLowerCase()+']';
var currentArticleRegexBit=chs+cA.substring(1);
currentArticleRegexBit=currentArticleRegexBit
.split(RegExp('(?:[_ ]+|%20)', 'g')).join('(?:[_ ]+|%20)')
.split('\\(').join('(?:%28|\\()')
.split('\\)').join('(?:%29|\\))'); // why does this need to match encoded strings ? links in the document ?
// leading and trailing space should be ignored, and anchor bits optional:
currentArticleRegexBit = '\\s*(' + currentArticleRegexBit + '(?:#[^\\[\\|]*)?)\\s*';
// e.g. Computer (archaic) -> \s*([Cc]omputer[_ ](?:%2528|\()archaic(?:%2528|\)))\s*
// autoedit=s~\[\[([Cc]ad)\]\]~[[Computer-aided%20design|$1]]~g;s~\[\[([Cc]AD)[|]~[[Computer-aided%20design|~g
var title=x.title || mw.config.get('wgPageName').split('_').join(' ');
var lk=titledWikiLink({article: new Title(title), newWin:x.newWin,
action: 'edit',
text: x.text,
title: x.hint,
className: 'popup_change_title_link'
});
var cmd='';
if (x.newTarget) {
// escape '&' and other nasties
var t = x.newTarget;
var s = mw.util.escapeRegExp(x.newTarget);
if (x.alsoChangeLabel) {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + ']]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
} else {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + '|$1]]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
}
} else {
cmd += 's~\\[\\['+currentArticleRegexBit+'\\]\\]~$1~g;';
cmd += 's~\\[\\['+currentArticleRegexBit+'[|](.*?)\\]\\]~$2~g';
}
// Build query
cmd = 'autoedit=' + encodeURIComponent ( cmd );
cmd += '&autoclick='+ encodeURIComponent( x.clickButton ) + '&actoken=' + encodeURIComponent( autoClickToken() );
cmd += ( x.minor === null ) ? '' : '&autominor='+ encodeURIComponent( x.minor );
cmd += ( x.watch === null ) ? '' : '&autowatch='+ encodeURIComponent( x.watch );
cmd += '&autosummary='+encodeURIComponent(x.summary);
cmd += '&autoimpl='+encodeURIComponent( popupString('autoedit_version') );
return appendParamsToLink(lk, cmd);
}
function redirLink(redirMatch, article) {
// NB redirMatch is in wikiText
var ret='';
if (getValueOf('popupAppendRedirNavLinks') && getValueOf('popupNavLinks')) {
ret += '<hr />';
if (getValueOf('popupFixRedirs') && typeof autoEdit != 'undefined' && autoEdit) {
ret += popupString('Redirects to: (Fix ');
log('redirLink: newTarget=' + redirMatch);
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target'),
hint: popupString('Fix this redirect, changing just the link target'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages')
}), 'R');
ret += popupString(' or ');
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target & label'),
hint: popupString('Fix this redirect, changing the link target and label'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages'),
alsoChangeLabel: true
}), 'R');
ret += popupString(')');
}
else ret += popupString('Redirects') + popupString(' to ');
return ret;
}
else return '<br> ' + popupString('Redirects') + popupString(' to ') +
titledWikiLink({article: new Title().fromWikiText(redirMatch), action: 'view', /* FIXME: newWin */
text: safeDecodeURI(redirMatch), title: popupString('Bypass redirect')});
}
function arinLink(l) {
if (!saneLinkCheck(l)) { return null; }
if ( ! l.article.isIpUser() || ! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
return generalNavLink({url:'http://ws.arin.net/cgi-bin/whois.pl?queryinput=' + encodeURIComponent(uN), newWin:l.newWin,
title: tprintf('Look up %s in ARIN whois database', [uN]),
text: l.text, noPopup:1});
}
function toolDbName(cookieStyle) {
var ret = mw.config.get('wgDBname');
if (!cookieStyle) { ret+= '_p'; }
return ret;
}
function saneLinkCheck(l) {
if (typeof l.article != typeof {} || typeof l.text != typeof '') { return false; }
return true;
}
function editCounterLink(l) {
if(!saneLinkCheck(l)) return null;
if (! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
var tool=getValueOf('popupEditCounterTool');
var url;
var defaultToolUrl='//tools.wmflabs.org/supercount/index.php?user=$1&project=$2.$3';
switch(tool) {
case 'custom':
url=simplePrintf(getValueOf('popupEditCounterUrl'), [ encodeURIComponent(uN), toolDbName() ]);
break;
case 'soxred': // no longer available
case 'kate': // no longer available
case 'interiot':// no longer available
/* fall through */
case 'supercount':
default:
var theWiki=pg.wiki.hostname.split('.');
url=simplePrintf(defaultToolUrl, [ encodeURIComponent(uN), theWiki[0], theWiki[1] ]);
}
return generalNavLink({url:url, title: tprintf('editCounterLinkHint', [uN]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function globalSearchLink(l) {
if(!saneLinkCheck(l)) return null;
var base='http://vs.aka-online.de/cgi-bin/globalwpsearch.pl?timeout=120&search=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + article, newWin:l.newWin,
title: tprintf('globalSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function googleLink(l) {
if(!saneLinkCheck(l)) return null;
var base='https://www.google.com/search?q=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + '%22' + article + '%22', newWin:l.newWin,
title: tprintf('googleSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function editorListLink(l) {
if(!saneLinkCheck(l)) return null;
var article= l.article.articleFromTalkPage() || l.article;
var url='https://xtools.wmflabs.org/articleinfo/' +
encodeURI( pg.wiki.hostname ) + '/' +
article.urlString() +
'?uselang=' + mw.config.get('wgUserLanguage');
return generalNavLink({url:url,
title: tprintf('editorListHint', [article]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function generalNavLink(l) {
l.className = (l.className === null) ? 'popupNavLink' : l.className;
return generalLink(l);
}
//////////////////////////////////////////////////
// magic history links
//
function getHistoryInfo(wikipage, whatNext) {
log('getHistoryInfo');
getHistory(wikipage, whatNext ? function(d){whatNext(processHistory(d));} : processHistory);
}
// FIXME eliminate pg.idNumber ... how? :-(
function getHistory(wikipage, onComplete) {
log('getHistory');
var url = pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&prop=revisions&titles=' +
new Title(wikipage).urlString() + '&rvlimit=' + getValueOf('popupHistoryLimit');
log('getHistory: url='+url);
return startDownload(url, pg.idNumber+'history', onComplete);
}
function processHistory(download) {
var jsobj = getJsObj(download.data);
try {
var revisions = anyChild(jsobj.query.pages).revisions;
var edits=[];
for (var i=0; i<revisions.length; ++i) {
edits.push({ oldid: revisions[i].revid, editor: revisions[i].user });
}
log('processed ' + edits.length + ' edits');
return finishProcessHistory( edits, mw.config.get('wgUserName') );
} catch (someError) {
log('Something went wrong with JSON business');
return finishProcessHistory([]);
}
}
function finishProcessHistory(edits, userName) {
var histInfo={};
histInfo.edits=edits;
histInfo.userName=userName;
for (var i=0; i<edits.length; ++i) {
if (typeof histInfo.myLastEdit === 'undefined' && userName && edits[i].editor==userName) {
histInfo.myLastEdit={index: i, oldid: edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
if (typeof histInfo.firstNewEditor === 'undefined' && edits[i].editor != edits[0].editor) {
histInfo.firstNewEditor={index:i, oldid:edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
}
//pg.misc.historyInfo=histInfo;
return histInfo;
}
//</NOLITE>
// ENDFILE: links.js
// STARTFILE: options.js
//////////////////////////////////////////////////
// options
// check for existing value, else use default
function defaultize(x) {
if (pg.option[x]===null || typeof pg.option[x]=='undefined') {
if (typeof window[x] != 'undefined' ) pg.option[x]=window[x];
else pg.option[x]=pg.optionDefault[x];
}
}
function newOption(x, def) {
pg.optionDefault[x]=def;
}
function setDefault(x, def) {
return newOption(x, def);
}
function getValueOf(varName) {
defaultize(varName);
return pg.option[varName];
}
/*eslint-disable */
function useDefaultOptions() { // for testing
for (var p in pg.optionDefault) {
pg.option[p]=pg.optionDefault[p];
if (typeof window[p]!='undefined') { delete window[p]; }
}
}
/*eslint-enable */
function setOptions() {
// user-settable parameters and defaults
var userIsSysop = false;
if ( mw.config.get('wgUserGroups') ) {
for ( var g = 0; g < mw.config.get('wgUserGroups').length; ++g ) {
if ( mw.config.get('wgUserGroups')[g] == "sysop" )
userIsSysop = true;
}
}
// Basic options
newOption('popupDelay', 0.5);
newOption('popupHideDelay', 0.5);
newOption('simplePopups', false);
newOption('popupStructure', 'shortmenus'); // see later - default for popupStructure is 'original' if simplePopups is true
newOption('popupActionsMenu', true);
newOption('popupSetupMenu', true);
newOption('popupAdminLinks', userIsSysop);
newOption('popupShortcutKeys', false);
newOption('popupHistoricalLinks', true);
newOption('popupOnlyArticleLinks', true);
newOption('removeTitles', true);
newOption('popupMaxWidth', 350);
newOption('popupSimplifyMainLink', true);
newOption('popupAppendRedirNavLinks', true);
newOption('popupTocLinks', false);
newOption('popupSubpopups', true);
newOption('popupDragHandle', false /* 'popupTopLinks'*/);
newOption('popupLazyPreviews', true);
newOption('popupLazyDownloads', true);
newOption('popupAllDabsStubs', false);
newOption('popupDebugging', false);
newOption('popupActiveNavlinks', true);
newOption('popupModifier', false); // ctrl, shift, alt or meta
newOption('popupModifierAction', 'enable'); // or 'disable'
newOption('popupDraggable', true);
newOption('popupReview', false);
//<NOLITE>
// images
newOption('popupImages', true);
newOption('imagePopupsForImages', true);
newOption('popupNeverGetThumbs', false);
//newOption('popupImagesToggleSize', true);
newOption('popupThumbAction', 'imagepage'); //'sizetoggle');
newOption('popupImageSize', 60);
newOption('popupImageSizeLarge', 200);
// redirs, dabs, reversion
newOption('popupFixRedirs', false);
newOption('popupRedirAutoClick', 'wpDiff');
newOption('popupFixDabs', false);
newOption('popupDabsAutoClick', 'wpDiff');
newOption('popupRevertSummaryPrompt', false);
newOption('popupMinorReverts', false);
newOption('popupRedlinkRemoval', false);
newOption('popupRedlinkAutoClick', 'wpDiff');
newOption('popupWatchDisambiggedPages', null);
newOption('popupWatchRedirredPages', null);
newOption('popupDabWiktionary', 'last');
// navlinks
newOption('popupNavLinks', true);
newOption('popupNavLinkSeparator', ' ⋅ ');
newOption('popupLastEditLink', true);
newOption('popupEditCounterTool', 'supercount');
newOption('popupEditCounterUrl', '');
//</NOLITE>
// previews etc
newOption('popupPreviews', true);
newOption('popupSummaryData', true);
newOption('popupMaxPreviewSentences', 9);
newOption('popupMaxPreviewCharacters', 900);
newOption('popupLastModified', true);
newOption('popupPreviewKillTemplates', true);
newOption('popupPreviewRawTemplates', true);
newOption('popupPreviewFirstParOnly', false);
newOption('popupPreviewCutHeadings', false);
newOption('popupPreviewButton', false);
newOption('popupPreviewButtonEvent', 'click');
//<NOLITE>
// diffs
newOption('popupPreviewDiffs', true);
newOption('popupDiffMaxLines', 100);
newOption('popupDiffContextLines', 2);
newOption('popupDiffContextCharacters', 40);
newOption('popupDiffDates', true);
newOption('popupDiffDatePrinter', 'toLocaleString');
// edit summaries. God, these are ugly.
newOption('popupReviewedSummary', popupString('defaultpopupReviewedSummary') );
newOption('popupFixDabsSummary', popupString('defaultpopupFixDabsSummary') );
newOption('popupExtendedRevertSummary', popupString('defaultpopupExtendedRevertSummary') );
newOption('popupRevertSummary', popupString('defaultpopupRevertSummary') );
newOption('popupRevertToPreviousSummary', popupString('defaultpopupRevertToPreviousSummary') );
newOption('popupQueriedRevertSummary', popupString('defaultpopupQueriedRevertSummary') );
newOption('popupQueriedRevertToPreviousSummary', popupString('defaultpopupQueriedRevertToPreviousSummary') );
newOption('popupFixRedirsSummary', popupString('defaultpopupFixRedirsSummary') );
newOption('popupRedlinkSummary', popupString('defaultpopupRedlinkSummary') );
newOption('popupRmDabLinkSummary', popupString('defaultpopupRmDabLinkSummary') );
//</NOLITE>
// misc
newOption('popupHistoryLimit', 50);
//<NOLITE>
newOption('popupFilters', [popupFilterStubDetect, popupFilterDisambigDetect,
popupFilterPageSize, popupFilterCountLinks,
popupFilterCountImages, popupFilterCountCategories,
popupFilterLastModified]);
newOption('extraPopupFilters', []);
newOption('popupOnEditSelection', 'cursor');
newOption('popupPreviewHistory', true);
newOption('popupImageLinks', true);
newOption('popupCategoryMembers', true);
newOption('popupUserInfo', true);
newOption('popupHistoryPreviewLimit', 25);
newOption('popupContribsPreviewLimit',25);
newOption('popupRevDelUrl', '//en.wikipedia.org/wiki/Wikipedia:Revision_deletion');
newOption('popupShowGender', true);
//</NOLITE>
// new windows
newOption('popupNewWindows', false);
newOption('popupLinksNewWindow', {'lastContrib': true, 'sinceMe': true});
// regexps
newOption('popupDabRegexp', '(\\{\\{\\s*disambig(?!uation needed)|disambig(uation|)\\s*\\}\\}|disamb\\s*\\}\\}|dab\\s*\\}\\})|\\{\\{\\s*(((geo|hn|road?|school|number)dis)|[234][lc][acw]|(road|ship)index)(\\s*[|][^}]*)?\\s*[}][}]|is a .*disambiguation.*page');
newOption('popupAnchorRegexp', 'anchors?'); //how to identify an anchors template
newOption('popupStubRegexp', '(sect)?stub[}][}]|This .*-related article is a .*stub');
newOption('popupImageVarsRegexp', 'image|image_(?:file|skyline|name|flag|seal)|cover|badge|logo');
}
// ENDFILE: options.js
// STARTFILE: strings.js
//<NOLITE>
//////////////////////////////////////////////////
// Translatable strings
//////////////////////////////////////////////////
//
// See instructions at
// https://en.wikipedia.org/wiki/Wikipedia:Tools/Navigation_popups/Translation
pg.string = {
/////////////////////////////////////
// summary data, searching etc.
/////////////////////////////////////
'article': 'article',
'category': 'category',
'categories': 'categories',
'image': 'image',
'images': 'images',
'stub': 'stub',
'section stub': 'section stub',
'Empty page': 'Empty page',
'kB': 'kB',
'bytes': 'bytes',
'day': 'day',
'days': 'days',
'hour': 'hour',
'hours': 'hours',
'minute': 'minute',
'minutes': 'minutes',
'second': 'second',
'seconds': 'seconds',
'week': 'week',
'weeks': 'weeks',
'search': 'search',
'SearchHint': 'Find English Wikipedia articles containing %s',
'web': 'web',
'global': 'global',
'globalSearchHint': 'Search across Wikipedias in different languages for %s',
'googleSearchHint': 'Google for %s',
/////////////////////////////////////
// article-related actions and info
// (some actions also apply to user pages)
/////////////////////////////////////
'actions': 'actions', ///// view articles and view talk
'popupsMenu': 'popups',
'togglePreviewsHint': 'Toggle preview generation in popups on this page',
'enable previews': 'enable previews',
'disable previews': 'disable previews',
'toggle previews': 'toggle previews',
'show preview': 'show preview',
'reset': 'reset',
'more...': 'more...',
'disable': 'disable popups',
'disablePopupsHint': 'Disable popups on this page. Reload page to re-enable.',
'historyfeedHint': 'RSS feed of recent changes to this page',
'purgePopupsHint': 'Reset popups, clearing all cached popup data.',
'PopupsHint': 'Reset popups, clearing all cached popup data.',
'spacebar': 'space',
'view': 'view',
'view article': 'view article',
'viewHint': 'Go to %s',
'talk': 'talk',
'talk page': 'talk page',
'this revision': 'this revision',
'revision %s of %s': 'revision %s of %s',
'Revision %s of %s': 'Revision %s of %s',
'the revision prior to revision %s of %s': 'the revision prior to revision %s of %s',
'Toggle image size': 'Click to toggle image size',
'del': 'del', ///// delete, protect, move
'delete': 'delete',
'deleteHint': 'Delete %s',
'undeleteShort': 'un',
'UndeleteHint': 'Show the deletion history for %s',
'protect': 'protect',
'protectHint': 'Restrict editing rights to %s',
'unprotectShort': 'un',
'unprotectHint': 'Allow %s to be edited by anyone again',
'send thanks': 'send thanks',
'ThanksHint': 'Send a thank you notification to this user',
'move': 'move',
'move page': 'move page',
'MovepageHint': 'Change the title of %s',
'edit': 'edit', ///// edit articles and talk
'edit article': 'edit article',
'editHint': 'Change the content of %s',
'edit talk': 'edit talk',
'new': 'new',
'new topic': 'new topic',
'newSectionHint': 'Start a new section on %s',
'null edit': 'null edit',
'nullEditHint': 'Submit an edit to %s, making no changes ',
'hist': 'hist', ///// history, diffs, editors, related
'history': 'history',
'historyHint': 'List the changes made to %s',
'last': 'prev', // For labelling the previous revision in history pages; the key is "last" for backwards compatibility
'lastEdit': 'lastEdit',
'mark patrolled': 'mark patrolled',
'markpatrolledHint': 'Mark this edit as patrolled',
'Could not marked this edit as patrolled': 'Could not marked this edit as patrolled',
'show last edit': 'most recent edit',
'Show the last edit': 'Show the effects of the most recent change',
'lastContrib': 'lastContrib',
'last set of edits': 'latest edits',
'lastContribHint': 'Show the net effect of changes made by the last editor',
'cur': 'cur',
'diffCur': 'diffCur',
'Show changes since revision %s': 'Show changes since revision %s',
'%s old': '%s old', // as in 4 weeks old
'oldEdit': 'oldEdit',
'purge': 'purge',
'purgeHint': 'Demand a fresh copy of %s',
'raw': 'source',
'rawHint': 'Download the source of %s',
'render': 'simple',
'renderHint': 'Show a plain HTML version of %s',
'Show the edit made to get revision': 'Show the edit made to get revision',
'sinceMe': 'sinceMe',
'changes since mine': 'diff my edit',
'sinceMeHint': 'Show changes since my last edit',
'Couldn\'t find an edit by %s\nin the last %s edits to\n%s': 'Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
'eds': 'eds',
'editors': 'editors',
'editorListHint': 'List the users who have edited %s',
'related': 'related',
'relatedChanges': 'relatedChanges',
'related changes': 'related changes',
'RecentchangeslinkedHint': 'Show changes in articles related to %s',
'editOld': 'editOld', ///// edit old version, or revert
'rv': 'rv',
'revert': 'revert',
'revertHint': 'Revert to %s',
'defaultpopupReviewedSummary': 'Accepted by reviewing the [[Special:diff/%s/%s|difference]] between this version and previously accepted version using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRedlinkSummary': 'Removing link to empty page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixDabsSummary': 'Disambiguate [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixRedirsSummary': 'Redirect bypass from [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupExtendedRevertSummary': 'Revert to revision dated %s by %s, oldid %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertToPreviousSummary': 'Revert to the revision prior to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertSummary': 'Revert to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertToPreviousSummary': 'Revert to the revision prior to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertSummary': 'Revert to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRmDabLinkSummary': 'Remove link to dab page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'Redirects': 'Redirects', // as in Redirects to ...
' to ': ' to ', // as in Redirects to ...
'Bypass redirect': 'Bypass redirect',
'Fix this redirect': 'Fix this redirect',
'disambig': 'disambig', ///// add or remove dab etc.
'disambigHint': 'Disambiguate this link to [[%s]]',
'Click to disambiguate this link to:': 'Click to disambiguate this link to:',
'remove this link': 'remove this link',
'remove all links to this page from this article': 'remove all links to this page from this article',
'remove all links to this disambig page from this article': 'remove all links to this disambig page from this article',
'mainlink': 'mainlink', ///// links, watch, unwatch
'wikiLink': 'wikiLink',
'wikiLinks': 'wikiLinks',
'links here': 'links here',
'whatLinksHere': 'whatLinksHere',
'what links here': 'what links here',
'WhatlinkshereHint': 'List the pages that are hyperlinked to %s',
'unwatchShort': 'un',
'watchThingy': 'watch', // called watchThingy because {}.watch is a function
'watchHint': 'Add %s to my watchlist',
'unwatchHint': 'Remove %s from my watchlist',
'Only found one editor: %s made %s edits': 'Only found one editor: %s made %s edits',
'%s seems to be the last editor to the page %s': '%s seems to be the last editor to the page %s',
'rss': 'rss',
/////////////////////////////////////
// diff previews
/////////////////////////////////////
'Diff truncated for performance reasons': 'Diff truncated for performance reasons',
'Old revision': 'Old revision',
'New revision': 'New revision',
'Something went wrong :-(': 'Something went wrong :-(',
'Empty revision, maybe non-existent': 'Empty revision, maybe non-existent',
'Unknown date': 'Unknown date',
/////////////////////////////////////
// other special previews
/////////////////////////////////////
'Empty category': 'Empty category',
'Category members (%s shown)': 'Category members (%s shown)',
'No image links found': 'No image links found',
'File links': 'File links',
'No image found': 'No image found',
'Image from Commons': 'Image from Commons',
'Description page': 'Description page',
'Alt text:': 'Alt text:',
'revdel':'Hidden revision',
/////////////////////////////////////
// user-related actions and info
/////////////////////////////////////
'user': 'user', ///// user page, talk, email, space
'user page': 'user page',
'user talk': 'user talk',
'edit user talk': 'edit user talk',
'leave comment': 'leave comment',
'email': 'email',
'email user': 'email user',
'EmailuserHint': 'Send an email to %s',
'space': 'space', // short form for userSpace link
'PrefixIndexHint': 'Show pages in the userspace of %s',
'count': 'count', ///// contributions, log
'edit counter': 'edit counter',
'editCounterLinkHint': 'Count the contributions made by %s',
'contribs': 'contribs',
'contributions': 'contributions',
'deletedContribs': 'deleted contributions',
'DeletedcontributionsHint': 'List deleted edits made by %s',
'ContributionsHint': 'List the contributions made by %s',
'log': 'log',
'user log': 'user log',
'userLogHint': 'Show %s\'s user log',
'arin': 'ARIN lookup', ///// ARIN lookup, block user or IP
'Look up %s in ARIN whois database': 'Look up %s in the ARIN whois database',
'unblockShort': 'un',
'block': 'block',
'block user': 'block user',
'IpblocklistHint': 'Unblock %s',
'BlockipHint': 'Prevent %s from editing',
'block log': 'block log',
'blockLogHint': 'Show the block log for %s',
'protectLogHint': 'Show the protection log for %s',
'pageLogHint': 'Show the page log for %s',
'deleteLogHint': 'Show the deletion log for %s',
'Invalid %s %s': 'The option %s is invalid: %s',
'No backlinks found': 'No backlinks found',
' and more': ' and more',
'undo': 'undo',
'undoHint': 'undo this edit',
'Download preview data': 'Download preview data',
'Invalid or IP user': 'Invalid or IP user',
'Not a registered username': 'Not a registered username',
'CẤM': 'CẤM',
'Has blocks': 'Has blocks',
' sửa đổi từ: ': ' sửa đổi từ: ',
'last edit on ': 'last edit on ',
/////////////////////////////////////
// Autoediting
/////////////////////////////////////
'Enter a non-empty edit summary or press cancel to abort': 'Enter a non-empty edit summary or press cancel to abort',
'Failed to get revision information, please edit manually.\n\n': 'Failed to get revision information, please edit manually.\n\n',
'The %s button has been automatically clicked. Please wait for the next page to load.': 'The %s button has been automatically clicked. Please wait for the next page to load.',
'Could not find button %s. Please check the settings in your javascript file.': 'Could not find button %s. Please check the settings in your javascript file.',
/////////////////////////////////////
// Popups setup
/////////////////////////////////////
'Open full-size image': 'Open full-size image',
'zxy': 'zxy',
'autoedit_version': 'np20140416'
};
function popupString(str) {
if (typeof popupStrings != 'undefined' && popupStrings && popupStrings[str]) { return popupStrings[str]; }
if (pg.string[str]) { return pg.string[str]; }
return str;
}
function tprintf(str,subs) {
if (typeof subs != typeof []) { subs = [subs]; }
return simplePrintf(popupString(str), subs);
}
//</NOLITE>
// ENDFILE: strings.js
// STARTFILE: run.js
////////////////////////////////////////////////////////////////////
// Run things
////////////////////////////////////////////////////////////////////
// For some reason popups requires a fully loaded page jQuery.ready(...) causes problems for some.
// The old addOnloadHook did something similar to the below
if (document.readyState=="complete")
autoEdit(); //will setup popups
else
$( window ).on( 'load', autoEdit );
// Support for MediaWiki's live preview, VisualEditor's saves and Echo's flyout.
( function () {
var once = true;
function dynamicContentHandler( $content ) {
// Try to detect the hook fired on initial page load and disregard
// it, we already hook to onload (possibly to different parts of
// page - it's configurable) and running twice might be bad. Ugly…
if ( $content.attr( 'id' ) == 'mw-content-text' ) {
if ( once ) {
once = false;
return;
}
}
function registerHooksForVisibleNavpops () {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
var navpop = pg.current.links[i].navpopup;
if (!navpop || !navpop.isVisible()) { continue; }
Navpopup.tracker.addHook(posCheckerHook(navpop));
}
}
function doIt () {
registerHooksForVisibleNavpops();
$content.each( function () {
this.ranSetupTooltipsAlready = false;
setupTooltips( this );
} );
}
setupPopups( doIt );
}
// This hook is also fired after page load.
mw.hook( 'wikipage.content' ).add( dynamicContentHandler );
mw.hook( 'ext.echo.overlay.beforeShowingOverlay' ).add( function($overlay){
dynamicContentHandler( $overlay.find(".mw-echo-state") );
});
} )();
// Forked from User:Connel MacKenzie/mess-with-popups.js
Previewmaker.prototype.firstBit = function() {
try{
if (mw.config.get("wgNamespaceNumber") !== 0){
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d = this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data = this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data = this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data = this.data.replace(RegExp('^[\\s\\n]*'), '');
stuff = (RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) {
d = stuff[0];
}
if (!getValueOf('popupPreviewFirstParOnly')) {
d = this.data;
}
/// now put \n\n after sections so that bullets and numbered lists work
d = d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["' + "'" + ']*\\s)', 'g'));
// leading space is bad, mmkay?
d[0] = d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds = RegExp('([^.][a-z][.][a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
var maxChars=getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd;
do {
dd = this.firstSentences(d, n);
--n;
}
while (dd.length > maxChars && n > 0);
this.data = dd;
} else {
//TODO: Limit this override to urls that start with http://*.wiktionary.org/
//TODO: extend the above section to also preview Wikipedia links
var charsWikt = 0;
var linesWikt = this.data.split('\n');
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[in]transitive}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[un]countable}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{cattag\|(.*?)}}/g, "");
linesWikt[jWikt] = linesWikt[jWikt].replace(/[\{\}]/g, "'");
if (linesWikt[jWikt].search(/^[#]/) == -1) {
if (linesWikt[jWikt].search(/^==[A-Z]/) == -1) {
linesWikt[jWikt] = '';
}
}
charsWikt += linesWikt[jWikt].length;
}
if (charsWikt < 2) {
linesWikt[0] = "No properly formatted content";
}
var maxChars = getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd = '';
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
if (linesWikt[jWikt] !== '') {
dd += linesWikt[jWikt] + '\n';
}
if (dd.length < maxChars) {
this.data = dd;
}
}
}
}
catch (e) {
console.log("error from mess with popups: ", e);
}
};
});
qwdgm5plp41md6gs93lqsroj7mbz15m
2513951
2513949
2026-08-14T04:48:50Z
Hiyuune
50834
2513951
javascript
text/javascript
// {{documentation}}
// _________________________________________________________________________________________
// | |
// | === WARNING: GLOBAL GADGET FILE === |
// | Changes to this page affect many users. |
// | Please discuss changes on the [[Wiktionary:Grease Pit]] before editing. |
// |_________________________________________________________________________________________|
mw.loader.load('//en.wikipedia.org/w/index.php?action=raw&title=MediaWiki:Gadget-navpop.css&ctype=text/css', "text/css");
// Forked from [[:w:MediaWiki:Gadget-popups.js]]
$(function () {
//////////////////////////////////////////////////
// Globals
//
// Trying to shove as many of these as possible into the pg (popup globals) object
var pg = {
re: {}, // regexps
ns: {}, // namespaces
string: {}, // translatable strings
wiki: {}, // local site info
user: {}, // current user info
misc: {}, // YUCK PHOOEY
option: {}, // options, see newOption etc
optionDefault: {}, // default option values
flag: {}, // misc flags
cache: {}, // page and image cache
structures: {}, // navlink structures
timer: {}, // all sorts of timers (too damn many)
counter: {}, // .. and all sorts of counters
current: {}, // state info
fn: {}, // functions
endoflist: null
};
/* Bail if the gadget/script is being loaded twice */
if( window.pg ) {
return;
}
/* Export to global context */
window.pg = pg;
/// Local Variables: ///
/// mode:c ///
/// End: ///
// ENDFILE: main.js
// STARTFILE: actions.js
function setupTooltips(container, remove, force, popData) {
log('setupTooltips, container='+container+', remove='+remove);
if (!container) {
//<NOLITE>
// the main initial call
if (getValueOf('popupOnEditSelection') && document && document.editform && document.editform.wpTextbox1) {
document.editform.wpTextbox1.onmouseup=doSelectionPopup;
}
//</NOLITE>
// article/content is a structure-dependent thing
container = defaultPopupsContainer();
}
if (!remove && !force && container.ranSetupTooltipsAlready) { return; }
container.ranSetupTooltipsAlready = !remove;
var anchors;
anchors=container.getElementsByTagName('A');
setupTooltipsLoop(anchors, 0, 250, 100, remove, popData);
}
function defaultPopupsContainer() {
if (getValueOf('popupOnlyArticleLinks')) {
return document.getElementById('mw_content') ||
document.getElementById('content') ||
document.getElementById('article') || document;
}
return document;
}
function setupTooltipsLoop(anchors,begin,howmany,sleep, remove, popData) {
log(simplePrintf('setupTooltipsLoop(%s,%s,%s,%s,%s)', arguments));
var finish=begin+howmany;
var loopend = Math.min(finish, anchors.length);
var j=loopend - begin;
log ('setupTooltips: anchors.length=' + anchors.length + ', begin=' + begin +
', howmany=' + howmany + ', loopend=' + loopend + ', remove=' + remove);
var doTooltip= remove ? removeTooltip : addTooltip;
// try a faster (?) loop construct
if (j > 0) {
do {
var a=anchors[loopend - j];
if (typeof a==='undefined' || !a || !a.href) {
log('got null anchor at index ' + loopend - j);
continue;
}
doTooltip(a, popData);
} while (--j);
}
if (finish < anchors.length) {
setTimeout(function() {
setupTooltipsLoop(anchors,finish,howmany,sleep,remove,popData);},
sleep);
} else {
if ( !remove && ! getValueOf('popupTocLinks')) { rmTocTooltips(); }
pg.flag.finishedLoading=true;
}
}
// eliminate popups from the TOC
// This also kills any onclick stuff that used to be going on in the toc
function rmTocTooltips() {
var toc=document.getElementById('toc');
if (toc) {
var tocLinks=toc.getElementsByTagName('A');
var tocLen = tocLinks.length;
for (var j=0; j<tocLen; ++j) {
removeTooltip(tocLinks[j], true);
}
}
}
function addTooltip(a, popData) {
if ( !isPopupLink(a) ) { return; }
a.onmouseover=mouseOverWikiLink;
a.onmouseout= mouseOutWikiLink;
a.onmousedown = killPopup;
a.hasPopup = true;
a.popData = popData;
}
function removeTooltip(a) {
if ( !a.hasPopup ) { return; }
a.onmouseover = null;
a.onmouseout = null;
if (a.originalTitle) { a.title = a.originalTitle; }
a.hasPopup=false;
}
function removeTitle(a) {
if (!a.originalTitle) {
a.originalTitle=a.title;
}
a.title='';
}
function restoreTitle(a) {
if ( a.title || !a.originalTitle ) { return; }
a.title = a.originalTitle;
}
function registerHooks(np) {
var popupMaxWidth=getValueOf('popupMaxWidth');
if (typeof popupMaxWidth === 'number') {
var setMaxWidth = function () {
np.mainDiv.style.maxWidth = popupMaxWidth + 'px';
np.maxWidth = popupMaxWidth;
};
np.addHook(setMaxWidth, 'unhide', 'before');
}
//<NOLITE>
np.addHook(addPopupShortcuts, 'unhide', 'after');
np.addHook(rmPopupShortcuts, 'hide', 'before');
//</NOLITE>
}
function removeModifierKeyHandler(a) {
//remove listeners for modifier key if any that were added in mouseOverWikiLink
document.removeEventListener('keydown', a.modifierKeyHandler, false);
document.removeEventListener('keyup', a.modifierKeyHandler, false);
}
function mouseOverWikiLink(evt) {
if (!evt && window.event) {evt=window.event;}
// if the modifier is needed, listen for it,
// we will remove the listener when we mouseout of this link or kill popup.
if (getValueOf('popupModifier')) {
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
var key = action=='disable' ? 'keyup' : 'keydown';
var a = this;
a.modifierKeyHandler = function(evt) {
mouseOverWikiLink2(a, evt);
};
document.addEventListener(key, a.modifierKeyHandler, false);
}
return mouseOverWikiLink2(this, evt);
}
/**
* Gets the references list item that the provided footnote link targets. This
* is typically a li element within the ol.references element inside the reflist.
* @param {Element} a - A footnote link.
* @returns {Element|boolean} The targeted element, or false if one can't be found.
*/
function footnoteTarget(a) {
var aTitle=Title.fromAnchor(a);
// We want ".3A" rather than "%3A" or "?" here, so use the anchor property directly
var anch = aTitle.anchor;
if ( ! /^(cite_note-|_note-|endnote)/.test(anch) ) { return false; }
var lTitle=Title.fromURL(location.href);
if ( lTitle.toString(true) !== aTitle.toString(true) ) { return false; }
var el=document.getElementById(anch);
while ( el && typeof el.nodeName === 'string') {
var nt = el.nodeName.toLowerCase();
if ( nt === 'li' ) { return el; }
else if ( nt === 'body' ) { return false; }
else if ( el.parentNode ) { el=el.parentNode; }
else { return false; }
}
return false;
}
function footnotePreview(x, navpop) {
setPopupHTML('<hr />' + x.innerHTML, 'popupPreview', navpop.idNumber);
}
function modifierPressed(evt) {
var mod=getValueOf('popupModifier');
if (!mod) { return false; }
if (!evt && window.event) {evt=window.event;}
return ( evt && mod && evt[mod.toLowerCase() + 'Key'] );
}
// Checks if the correct modifier pressed/unpressed if needed
function isCorrectModifier(a,evt) {
if (!getValueOf('popupModifier')) { return true; }
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
return ( action == 'enable' && modifierPressed(evt) ||
action == 'disable' && !modifierPressed(evt) );
}
function mouseOverWikiLink2(a, evt) {
if (!isCorrectModifier(a,evt)) { return; }
if ( getValueOf('removeTitles') ) { removeTitle(a); }
if ( a==pg.current.link && a.navpopup && a.navpopup.isVisible() ) { return; }
pg.current.link=a;
if (getValueOf('simplePopups') && !pg.option.popupStructure) {
// reset *default value* of popupStructure
setDefault('popupStructure', 'original');
}
var article=(new Title()).fromAnchor(a);
// set global variable (ugh) to hold article (wikipage)
pg.current.article = article;
if (!a.navpopup) {
a.navpopup=newNavpopup(a, article);
pg.current.linksHash[a.href] = a.navpopup;
pg.current.links.push(a);
}
if (a.navpopup.pending === null || a.navpopup.pending !== 0) {
// either fresh popups or those with unfinshed business are redone from scratch
simplePopupContent(a, article);
}
a.navpopup.showSoonIfStable(a.navpopup.delay);
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=setInterval(checkPopupPosition, 600);
if(getValueOf('simplePopups')) {
if (getValueOf('popupPreviewButton') && !a.simpleNoMore) {
var d=document.createElement('div');
d.className='popupPreviewButtonDiv';
var s=document.createElement('span');
d.appendChild(s);
s.className='popupPreviewButton';
s['on' + getValueOf('popupPreviewButtonEvent')] = function() {
a.simpleNoMore=true;
d.style.display = "none";
nonsimplePopupContent(a,article);
};
s.innerHTML=popupString('show preview');
setPopupHTML(d, 'popupPreview', a.navpopup.idNumber);
}
}
if (a.navpopup.pending !== 0 ) {
nonsimplePopupContent(a, article);
}
}
// simplePopupContent: the content that do not require additional download
// (it is shown even when simplePopups is true)
function simplePopupContent(a, article) {
/* FIXME hack */ a.navpopup.hasPopupMenu=false;
a.navpopup.setInnerHTML(popupHTML(a));
fillEmptySpans({navpopup:a.navpopup});
if (getValueOf('popupDraggable'))
{
var dragHandle = getValueOf('popupDragHandle') || null;
if (dragHandle && dragHandle != 'all') {
dragHandle += a.navpopup.idNumber;
}
setTimeout(function(){a.navpopup.makeDraggable(dragHandle);}, 150);
}
//<NOLITE>
if (getValueOf('popupRedlinkRemoval') && a.className=='new') {
setPopupHTML('<br>'+popupRedlinkHTML(article), 'popupRedlink', a.navpopup.idNumber);
}
//</NOLITE>
}
function debugData(navpopup) {
if(getValueOf('popupDebugging') && navpopup.idNumber) {
setPopupHTML('idNumber='+navpopup.idNumber + ', pending=' + navpopup.pending,
'popupError', navpopup.idNumber);
}
}
function newNavpopup(a, article) {
var navpopup = new Navpopup();
navpopup.fuzz=5;
navpopup.delay=getValueOf('popupDelay')*1000;
// increment global counter now
navpopup.idNumber = ++pg.idNumber;
navpopup.parentAnchor = a;
navpopup.parentPopup = (a.popData && a.popData.owner);
navpopup.article = article;
registerHooks(navpopup);
return navpopup;
}
// Should we show nonsimple context?
// If simplePopups is set to true, then we do not show nonsimple context,
// but if a bottom "show preview" was clicked we do show nonsimple context
function shouldShowNonSimple(a) {
return !getValueOf('simplePopups') || a.simpleNoMore;
}
// Should we show nonsimple context govern by the option (e.g. popupUserInfo)?
// If the user explicitly asked for nonsimple context by setting the option to true,
// then we show it even in nonsimple mode.
function shouldShow(a,option) {
if (shouldShowNonSimple(a)) {
return getValueOf(option);
} else {
return (typeof window[option] != 'undefined' ) && window[option];
}
}
function nonsimplePopupContent(a, article) {
var diff=null, history=null;
var params=parseParams(a.href);
var oldid=(typeof params.oldid=='undefined' ? null : params.oldid);
//<NOLITE>
if(shouldShow(a,'popupPreviewDiffs')) {
diff=params.diff;
}
if(shouldShow(a,'popupPreviewHistory')) {
history=(params.action=='history');
}
//</NOLITE>
a.navpopup.pending=0;
var referenceElement = footnoteTarget(a);
if (referenceElement) {
footnotePreview(referenceElement, a.navpopup);
//<NOLITE>
} else if ( diff || diff === 0 ) {
loadDiff(article, oldid, diff, a.navpopup);
} else if ( history ) {
loadAPIPreview('history', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.contribs.test(a.href) ) {
loadAPIPreview('contribs', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.backlinks.test(a.href) ) {
loadAPIPreview('backlinks', article, a.navpopup);
} else if ( // FIXME should be able to get all preview combinations with options
article.namespaceId()==pg.nsImageId &&
( shouldShow(a,'imagePopupsForImages') || ! anchorContainsImage(a) )
) {
loadAPIPreview('imagepagepreview', article, a.navpopup);
loadImage(article, a.navpopup);
//</NOLITE>
} else {
if (article.namespaceId() == pg.nsCategoryId &&
shouldShow(a,'popupCategoryMembers')) {
loadAPIPreview('category', article, a.navpopup);
} else if ((article.namespaceId() == pg.nsUserId || article.namespaceId() == pg.nsUsertalkId) &&
shouldShow(a,'popupUserInfo')) {
loadAPIPreview('userinfo', article, a.navpopup);
}
if (shouldShowNonSimple(a)) startArticlePreview(article, oldid, a.navpopup);
}
}
function pendingNavpopTask(navpop) {
if (navpop && navpop.pending === null) { navpop.pending=0; }
++navpop.pending;
debugData(navpop);
}
function completedNavpopTask(navpop) {
if (navpop && navpop.pending) { --navpop.pending; }
debugData(navpop);
}
function startArticlePreview(article, oldid, navpop) {
navpop.redir=0;
loadPreview(article, oldid, navpop);
}
function loadPreview(article, oldid, navpop) {
if (!navpop.redir) { navpop.originalArticle=article; }
article.oldid = oldid;
loadAPIPreview('revision', article, navpop);
}
function loadPreviewFromRedir(redirMatch, navpop) {
// redirMatch is a regex match
var target = new Title().fromWikiText(redirMatch[2]);
// overwrite (or add) anchor from original target
// mediawiki does overwrite; eg User:Lupin/foo3#Done
if ( navpop.article.anchor ) { target.anchor = navpop.article.anchor; }
navpop.redir++;
navpop.redirTarget=target;
//<NOLITE>
var warnRedir = redirLink(target, navpop.article);
setPopupHTML(warnRedir, 'popupWarnRedir', navpop.idNumber);
//</NOLITE>
navpop.article=target;
fillEmptySpans({redir: true, redirTarget: target, navpopup:navpop});
return loadPreview(target, null, navpop);
}
function insertPreview(download) {
if (!download.owner) { return; }
var redirMatch = pg.re.redirect.exec(download.data);
if (download.owner.redir === 0 && redirMatch) {
loadPreviewFromRedir(redirMatch, download.owner);
return;
}
if (download.owner.visible || !getValueOf('popupLazyPreviews')) {
insertPreviewNow(download);
} else {
var id=(download.owner.redir) ? 'PREVIEW_REDIR_HOOK' : 'PREVIEW_HOOK';
download.owner.addHook( function(){insertPreviewNow(download); return true;},
'unhide', 'after', id );
}
}
function insertPreviewNow(download) {
if (!download.owner) { return; }
var wikiText=download.data;
var navpop=download.owner;
var art=navpop.redirTarget || navpop.originalArticle;
//<NOLITE>
makeFixDabs(wikiText, navpop);
if (getValueOf('popupSummaryData')) {
getPageInfo(wikiText, download);
setPopupTrailer(getPageInfo(wikiText, download), navpop.idNumber);
}
var imagePage='';
if (art.namespaceId()==pg.nsImageId) { imagePage=art.toString(); }
else { imagePage=getValidImageFromWikiText(wikiText); }
if(imagePage) { loadImage(Title.fromWikiText(imagePage), navpop); }
//</NOLITE>
if (getValueOf('popupPreviews')) { insertArticlePreview(download, art, navpop); }
}
function insertArticlePreview(download, art, navpop) {
if (download && typeof download.data == typeof ''){
if (art.namespaceId()==pg.nsTemplateId && getValueOf('popupPreviewRawTemplates')) {
// FIXME compare/consolidate with diff escaping code for wikitext
var h='<hr /><span style="font-family: monospace;">' + download.data.entify().split('\\n').join('<br />\\n') + '</span>';
setPopupHTML(h, 'popupPreview', navpop.idNumber);
}
else {
var p=prepPreviewmaker(download.data, art, navpop);
p.showPreview();
}
}
}
function prepPreviewmaker(data, article, navpop) {
// deal with tricksy anchors
var d=anchorize(data, article.anchorString());
var urlBase=joinPath([pg.wiki.articlebase, article.urlString()]);
var p=new Previewmaker(d, urlBase, navpop);
return p;
}
// Try to imitate the way mediawiki generates HTML anchors from section titles
function anchorize(d, anch) {
if (!anch) { return d; }
var anchRe=RegExp('(?:=+\\s*' + literalizeRegex(anch).replace(/[_ ]/g, '[_ ]') + '\\s*=+|\\{\\{\\s*'+getValueOf('popupAnchorRegexp')+'\\s*(?:\\|[^|}]*)*?\\s*'+literalizeRegex(anch)+'\\s*(?:\\|[^}]*)?}})');
var match=d.match(anchRe);
if(match && match.length > 0 && match[0]) { return d.substring(d.indexOf(match[0])); }
// now try to deal with == foo [[bar|baz]] boom == -> #foo_baz_boom
var lines=d.split('\n');
for (var i=0; i<lines.length; ++i) {
lines[i]=lines[i].replace(RegExp('[[]{2}([^|\\]]*?[|])?(.*?)[\\]]{2}', 'g'), '$2')
.replace(/'''([^'])/g, '$1').replace(RegExp("''([^'])", 'g'), '$1');
if (lines[i].match(anchRe)) {
return d.split('\n').slice(i).join('\n').replace(RegExp('^[^=]*'), '');
}
}
return d;
}
function killPopup() {
removeModifierKeyHandler(this);
if (getValueOf('popupShortcutKeys')) { rmPopupShortcuts(); }
if (!pg) { return; }
if (pg.current.link && pg.current.link.navpopup) { pg.current.link.navpopup.banish(); }
pg.current.link=null;
abortAllDownloads();
if (pg.timer.checkPopupPosition) {
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=null;
}
return true; // preserve default action
}
// ENDFILE: actions.js
// STARTFILE: domdrag.js
/**
@fileoverview
The {@link Drag} object, which enables objects to be dragged around.
<pre>
*************************************************
dom-drag.js
09.25.2001
www.youngpup.net
**************************************************
10.28.2001 - fixed minor bug where events
sometimes fired off the handle, not the root.
*************************************************
Pared down, some hooks added by User:Lupin
Copyright Aaron Boodman.
Saying stupid things daily since March 2001.
</pre>
*/
/**
Creates a new Drag object. This is used to make various DOM elements draggable.
@constructor
*/
function Drag () {
/**
Condition to determine whether or not to drag. This function should take one parameter, an Event.
To disable this, set it to <code>null</code>.
@type Function
*/
this.startCondition = null;
/**
Hook to be run when the drag finishes. This is passed the final coordinates of
the dragged object (two integers, x and y). To disables this, set it to <code>null</code>.
@type Function
*/
this.endHook = null;
}
/**
Gets an event in a cross-browser manner.
@param {Event} e
@private
*/
Drag.prototype.fixE = function(e) {
if (typeof e == 'undefined') { e = window.event; }
if (typeof e.layerX == 'undefined') { e.layerX = e.offsetX; }
if (typeof e.layerY == 'undefined') { e.layerY = e.offsetY; }
return e;
};
/**
Initialises the Drag instance by telling it which object you want to be draggable, and what you want to drag it by.
@param {DOMElement} o The "handle" by which <code>oRoot</code> is dragged.
@param {DOMElement} oRoot The object which moves when <code>o</code> is dragged, or <code>o</code> if omitted.
*/
Drag.prototype.init = function(o, oRoot) {
var dragObj = this;
this.obj = o;
o.onmousedown = function(e) { dragObj.start.apply( dragObj, [e]); };
o.dragging = false;
o.popups_draggable = true;
o.hmode = true;
o.vmode = true;
o.root = oRoot ? oRoot : o ;
if (isNaN(parseInt(o.root.style.left, 10))) { o.root.style.left = "0px"; }
if (isNaN(parseInt(o.root.style.top, 10))) { o.root.style.top = "0px"; }
o.root.onthisStart = function(){};
o.root.onthisEnd = function(){};
o.root.onthis = function(){};
};
/**
Starts the drag.
@private
@param {Event} e
*/
Drag.prototype.start = function(e) {
var o = this.obj; // = this;
e = this.fixE(e);
if (this.startCondition && !this.startCondition(e)) { return; }
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10);
o.root.onthisStart(x, y);
o.lastMouseX = e.clientX;
o.lastMouseY = e.clientY;
var dragObj = this;
o.onmousemoveDefault = document.onmousemove;
o.dragging = true;
document.onmousemove = function(e) { dragObj.drag.apply( dragObj, [e] ); };
document.onmouseup = function(e) { dragObj.end.apply( dragObj, [e] ); };
return false;
};
/**
Does the drag.
@param {Event} e
@private
*/
Drag.prototype.drag = function(e) {
e = this.fixE(e);
var o = this.obj;
var ey = e.clientY;
var ex = e.clientX;
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10 );
var nx, ny;
nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));
this.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
this.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
this.obj.lastMouseX = ex;
this.obj.lastMouseY = ey;
this.obj.root.onthis(nx, ny);
return false;
};
/**
Ends the drag.
@private
*/
Drag.prototype.end = function() {
document.onmousemove=this.obj.onmousemoveDefault;
document.onmouseup = null;
this.obj.dragging = false;
if (this.endHook) {
this.endHook( parseInt(this.obj.root.style[this.obj.hmode ? "left" : "right"], 10),
parseInt(this.obj.root.style[this.obj.vmode ? "top" : "bottom"], 10));
}
};
// ENDFILE: domdrag.js
// STARTFILE: structures.js
//<NOLITE>
pg.structures.original={};
pg.structures.original.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle',
'popupUserData', 'popupData', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.original.popupRedirSpans=function () {
return ['popupRedir', 'popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'];
};
pg.structures.original.popupTitle=function (x) {
log ('defaultstructure.popupTitle');
if (!getValueOf('popupNavLinks')) {
return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
}
return '';
};
pg.structures.original.popupTopLinks=function (x) {
log ('defaultstructure.popupTopLinks');
if (getValueOf('popupNavLinks')) { return navLinksHTML(x.article, x.hint, x.params); }
return '';
};
pg.structures.original.popupImage=function(x) {
log ('original.popupImage, x.article='+x.article+', x.navpop.idNumber='+x.navpop.idNumber);
return imageHTML(x.article, x.navpop.idNumber);
};
pg.structures.original.popupRedirTitle=pg.structures.original.popupTitle;
pg.structures.original.popupRedirTopLinks=pg.structures.original.popupTopLinks;
function copyStructure(oldStructure, newStructure) {
pg.structures[newStructure]={};
for (var prop in pg.structures[oldStructure]) {
pg.structures[newStructure][prop]=pg.structures[oldStructure][prop];
}
}
copyStructure('original', 'nostalgia');
pg.structures.nostalgia.popupTopLinks=function(x) {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>';
str+='if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' +
editstr + '}';
var historystr='<<history|shortcut=h>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str += '<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>';
str += 'if(admin){<br>}else{*}<<move|shortcut=m>>';
// admin links
str += 'if(admin){*<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>}';
return navlinkStringToHTML(str, x.article, x.params);
};
pg.structures.nostalgia.popupRedirTopLinks=pg.structures.nostalgia.popupTopLinks;
/** -- fancy -- **/
copyStructure('original', 'fancy');
pg.structures.fancy.popupTitle=function (x) {
return navlinkStringToHTML('<font size=+0><<mainlink>></font>',x.article,x.params);
};
pg.structures.fancy.popupTopLinks=function(x) {
var hist='<<history|shortcut=h|hist>>|<<lastEdit|shortcut=/|last>>|<<editors|shortcut=E|eds>>';
var watch='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
var move='<<move|shortcut=m|move>>';
return navlinkStringToHTML('if(talk){' +
'<<edit|shortcut=e>>|<<new|shortcut=+|+>>*' + hist + '*' +
'<<article|shortcut=a>>|<<editArticle|edit>>' + '*' + watch + '*' + move +
'}else{<<edit|shortcut=e>>*' + hist +
'*<<talk|shortcut=t|>>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>' +
'*' + watch + '*' + move+'}<br>', x.article, x.params);
};
pg.structures.fancy.popupOtherLinks=function(x) {
var admin='<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*<<undelete|undeleteShort>>|<<delete|shortcut=d|del>>';
var user='<<contribs|shortcut=c>>if(wikimedia){|<<count|shortcut=#|#>>}';
user+='if(ipuser){|<<arin>>}else{*<<email|shortcut=E|'+
popupString('email')+'>>}if(admin){*<<block|shortcut=b>>}';
var normal='<<whatLinksHere|shortcut=l|links here>>*<<relatedChanges|shortcut=r|related>>';
return navlinkStringToHTML('<br>if(user){' + user + '*}if(admin){'+admin+'if(user){<br>}else{*}}' + normal,
x.article, x.params);
};
pg.structures.fancy.popupRedirTitle=pg.structures.fancy.popupTitle;
pg.structures.fancy.popupRedirTopLinks=pg.structures.fancy.popupTopLinks;
pg.structures.fancy.popupRedirOtherLinks=pg.structures.fancy.popupOtherLinks;
/** -- fancy2 -- **/
// hack for User:MacGyverMagic
copyStructure('fancy', 'fancy2');
pg.structures.fancy2.popupTopLinks=function(x) { // hack out the <br> at the end and put one at the beginning
return '<br>'+pg.structures.fancy.popupTopLinks(x).replace(RegExp('<br>$','i'),'');
};
pg.structures.fancy2.popupLayout=function () { // move toplinks to after the title
return ['popupError', 'popupImage', 'popupTitle', 'popupUserData', 'popupData', 'popupTopLinks', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
/** -- menus -- **/
copyStructure('original', 'menus');
pg.structures.menus.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupUserData', 'popupData', 'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.menus.popupTopLinks = function (x, shorter) {
// FIXME maybe this stuff should be cached
var s=[];
var dropdiv='<div class="popup_drop">';
var enddiv='</div>';
var hist='<<history|shortcut=h>>';
if (!shorter) { hist = '<menurow>' + hist +
'|<<historyfeed|rss>>|<<editors|shortcut=E>></menurow>'; }
var lastedit='<<lastEdit|shortcut=/|show last edit>>';
var thank='if(diff){<<thank|send thanks>>}';
var jsHistory='<<lastContrib|last set of edits>><<sinceMe|changes since mine>>';
var linkshere='<<whatLinksHere|shortcut=l|what links here>>';
var related='<<relatedChanges|shortcut=r|related changes>>';
var search='<menurow><<search|shortcut=s>>if(wikimedia){|<<globalsearch|shortcut=g|global>>}' +
'|<<google|shortcut=G|web>></menurow>';
var watch='<menurow><<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>></menurow>';
var protect='<menurow><<unprotect|unprotectShort>>|' +
'<<protect|shortcut=p>>|<<protectlog|log>></menurow>';
var del='<menurow><<undelete|undeleteShort>>|<<delete|shortcut=d>>|' +
'<<deletelog|log>></menurow>';
var move='<<move|shortcut=m|move page>>';
var nullPurge='<menurow><<nullEdit|shortcut=n|null edit>>|<<purge|shortcut=P>></menurow>';
var viewOptions='<menurow><<view|shortcut=v>>|<<render|shortcut=S>>|<<raw>></menurow>';
var editRow='if(oldid){' +
'<menurow><<edit|shortcut=e>>|<<editOld|shortcut=e|this revision>></menurow>' +
'<menurow><<revert|shortcut=v>>|<<undo>></menurow>' + '}else{<<edit|shortcut=e>>}';
var markPatrolled='if(rcid){<<markpatrolled|mark patrolled>>}';
var newTopic='if(talk){<<new|shortcut=+|new topic>>}';
var protectDelete='if(admin){' + protect + del + '}';
if (getValueOf('popupActionsMenu')) {
s.push( '<<mainlink>>*' + dropdiv + menuTitle('actions'));
} else {
s.push( dropdiv + '<<mainlink>>');
}
s.push( '<menu>');
s.push( editRow + markPatrolled + newTopic + hist + lastedit + thank );
if (!shorter) { s.push(jsHistory); }
s.push( move + linkshere + related);
if (!shorter) { s.push(nullPurge + search); }
if (!shorter) { s.push(viewOptions); }
s.push('<hr />' + watch + protectDelete);
s.push('<hr />' +
'if(talk){<<article|shortcut=a|view article>><<editArticle|edit article>>}' +
'else{<<talk|shortcut=t|talk page>><<editTalk|edit talk>>' +
'<<newTalk|shortcut=+|new topic>>}</menu>' + enddiv);
// user menu starts here
var email='<<email|shortcut=E|email user>>';
var contribs= 'if(wikimedia){<menurow>}<<contribs|shortcut=c|contributions>>if(wikimedia){</menurow>}' +
'if(admin){<menurow><<deletedContribs>></menurow>}';
s.push('if(user){*' + dropdiv + menuTitle('user'));
s.push('<menu>');
s.push('<menurow><<userPage|shortcut=u|user page>>|<<userSpace|space>></menurow>');
s.push('<<userTalk|shortcut=t|user talk>><<editUserTalk|edit user talk>>' +
'<<newUserTalk|shortcut=+|leave comment>>');
if(!shorter) { s.push( 'if(ipuser){<<arin>>}else{' + email + '}' ); }
else { s.push( 'if(ipuser){}else{' + email + '}' ); }
s.push('<hr />' + contribs + '<<userlog|shortcut=L|user log>>');
s.push('if(wikimedia){<<count|shortcut=#|edit counter>>}');
s.push('if(admin){<menurow><<unblock|unblockShort>>|<<block|shortcut=b|block user>></menurow>}');
s.push('<<blocklog|shortcut=B|block log>>');
s.push('</menu>' + enddiv + '}');
// popups menu starts here
if (getValueOf('popupSetupMenu') && !x.navpop.hasPopupMenu /* FIXME: hack */) {
x.navpop.hasPopupMenu=true;
s.push('*' + dropdiv + menuTitle('popupsMenu') + '<menu>');
s.push('<<togglePreviews|toggle previews>>');
s.push('<<purgePopups|reset>>');
s.push('<<disablePopups|disable>>');
s.push('</menu>'+enddiv);
}
return navlinkStringToHTML(s.join(''), x.article, x.params);
};
function menuTitle(s) {
return '<a href="#" noPopup=1>' + popupString(s) + '</a>';
}
pg.structures.menus.popupRedirTitle=pg.structures.menus.popupTitle;
pg.structures.menus.popupRedirTopLinks=pg.structures.menus.popupTopLinks;
copyStructure('menus', 'shortmenus');
pg.structures.shortmenus.popupTopLinks=function(x) {
return pg.structures.menus.popupTopLinks(x,true);
};
pg.structures.shortmenus.popupRedirTopLinks=pg.structures.shortmenus.popupTopLinks;
//</NOLITE>
pg.structures.lite={};
pg.structures.lite.popupLayout=function () {
return ['popupTitle', 'popupPreview' ];
};
pg.structures.lite.popupTitle=function (x) {
log (x.article + ': structures.lite.popupTitle');
//return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
return '<div><span class="popup_mainlink"><b>' + x.article.toString() + '</b></span></div>';
};
// ENDFILE: structures.js
// STARTFILE: autoedit.js
//<NOLITE>
function substitute(data,cmdBody) {
// alert('sub\nfrom: '+cmdBody.from+'\nto: '+cmdBody.to+'\nflags: '+cmdBody.flags);
var fromRe=RegExp(cmdBody.from, cmdBody.flags);
return data.replace(fromRe, cmdBody.to);
}
function execCmds(data, cmdList) {
for (var i=0; i<cmdList.length; ++i) {
data=cmdList[i].action(data, cmdList[i]);
}
return data;
}
function parseCmd(str) {
// returns a list of commands
if (!str.length) { return []; }
var p=false;
switch (str.charAt(0)) {
case 's':
p=parseSubstitute(str);
break;
default:
return false;
}
if (p) { return [p].concat(parseCmd(p.remainder)); }
return false;
}
function unEscape(str, sep) {
return str.split('\\\\').join('\\').split('\\'+sep).join(sep).split('\\n').join('\n');
}
function parseSubstitute(str) {
// takes a string like s/a/b/flags;othercmds and parses it
var from,to,flags,tmp;
if (str.length<4) { return false; }
var sep=str.charAt(1);
str=str.substring(2);
tmp=skipOver(str,sep);
if (tmp) { from=tmp.segment; str=tmp.remainder; }
else { return false; }
tmp=skipOver(str,sep);
if (tmp) { to=tmp.segment; str=tmp.remainder; }
else { return false; }
flags='';
if (str.length) {
tmp=skipOver(str,';') || skipToEnd(str, ';');
if (tmp) {flags=tmp.segment; str=tmp.remainder; }
}
return {action: substitute, from: from, to: to, flags: flags, remainder: str};
}
function skipOver(str,sep) {
var endSegment=findNext(str,sep);
if (endSegment<0) { return false; }
var segment=unEscape(str.substring(0,endSegment), sep);
return {segment: segment, remainder: str.substring(endSegment+1)};
}
/*eslint-disable*/
function skipToEnd(str,sep) {
return {segment: str, remainder: ''};
}
/*eslint-enable */
function findNext(str, ch) {
for (var i=0; i<str.length; ++i) {
if (str.charAt(i)=='\\') { i+=2; }
if (str.charAt(i)==ch) { return i; }
}
return -1;
}
function setCheckbox(param, box) {
var val=mw.util.getParamValue(param);
if (val) {
switch (val) {
case '1': case 'yes': case 'true':
box.checked=true;
break;
case '0': case 'no': case 'false':
box.checked=false;
}
}
}
function autoEdit() {
setupPopups( function () {
if (mw.util.getParamValue('autoimpl') !== popupString('autoedit_version') ) { return false; }
if (mw.util.getParamValue('autowatchlist') && mw.util.getParamValue('actoken')===autoClickToken()) {
pg.fn.modifyWatchlist(mw.util.getParamValue('title'), mw.util.getParamValue('action'));
}
if (!document.editform) { return false; }
if (autoEdit.alreadyRan) { return false; }
autoEdit.alreadyRan=true;
var cmdString=mw.util.getParamValue('autoedit');
if (cmdString) {
try {
var editbox=document.editform.wpTextbox1;
var cmdList=parseCmd(cmdString);
var input=editbox.value;
var output=execCmds(input, cmdList);
editbox.value=output;
} catch (dang) { return; }
// wikEd user script compatibility
if (typeof(wikEdUseWikEd) != 'undefined') {
if (wikEdUseWikEd === true) {
WikEdUpdateFrame();
}
}
}
setCheckbox('autominor', document.editform.wpMinoredit);
setCheckbox('autowatch', document.editform.wpWatchthis);
var rvid = mw.util.getParamValue('autorv');
if (rvid) {
var url=pg.wiki.apiwikibase + '?action=query&format=json&formatversion=2&prop=revisions&revids='+rvid;
startDownload(url, null, autoEdit2);
} else { autoEdit2(); }
} );
}
function autoEdit2(d) {
var summary=mw.util.getParamValue('autosummary');
var summaryprompt=mw.util.getParamValue('autosummaryprompt');
var summarynotice='';
if (d && d.data && mw.util.getParamValue('autorv')) {
var s = getRvSummary(summary, d.data);
if (s === false) {
summaryprompt=true;
summarynotice=popupString('Failed to get revision information, please edit manually.\n\n');
summary = simplePrintf(summary, [mw.util.getParamValue('autorv'), '(unknown)', '(unknown)']);
} else { summary = s; }
}
if (summaryprompt) {
var txt= summarynotice +
popupString('Enter a non-empty edit summary or press cancel to abort');
var response=prompt(txt, summary);
if (response) { summary=response; }
else { return; }
}
if (summary) { document.editform.wpSummary.value=summary; }
// Attempt to avoid possible premature clicking of the save button
// (maybe delays in updates to the DOM are to blame?? or a red herring)
setTimeout(autoEdit3, 100);
}
function autoClickToken() {
return mw.user.sessionId();
}
function autoEdit3() {
if( mw.util.getParamValue('actoken') != autoClickToken()) { return; }
var btn=mw.util.getParamValue('autoclick');
if (btn) {
if (document.editform && document.editform[btn]) {
var button=document.editform[btn];
var msg=tprintf('The %s button has been automatically clicked. Please wait for the next page to load.',
[ button.value ]);
bannerMessage(msg);
document.title='('+document.title+')';
button.click();
} else {
alert(tprintf('Could not find button %s. Please check the settings in your javascript file.',
[ btn ]));
}
}
}
function bannerMessage(s) {
var headings=document.getElementsByTagName('h1');
if (headings) {
var div=document.createElement('div');
div.innerHTML='<font size=+1><b>' + s + '</b></font>';
headings[0].parentNode.insertBefore(div, headings[0]);
}
}
function getRvSummary(template, json) {
try {
var o=getJsObj(json);
var edit = anyChild(o.query.pages).revisions[0];
var timestamp = edit.timestamp.split(/[A-Z]/g).join(' ').replace(/^ *| *$/g, '');
return simplePrintf(template, [edit.revid, timestamp, edit.userhidden ? '(hidden)' : edit.user ]);
} catch (badness) {
return false;
}
}
//</NOLITE>
// ENDFILE: autoedit.js
// STARTFILE: downloader.js
/**
@fileoverview
{@link Downloader}, a xmlhttprequest wrapper, and helper functions.
*/
/**
Creates a new Downloader
@constructor
@class The Downloader class. Create a new instance of this class to download stuff.
@param {String} url The url to download. This can be omitted and supplied later.
*/
function Downloader(url) {
if (typeof XMLHttpRequest!='undefined') { this.http = new XMLHttpRequest(); }
/**
The url to download
@type String
*/
this.url = url;
/**
A universally unique ID number
@type integer
*/
this.id=null;
/**
Modification date, to be culled from the incoming headers
@type Date
@private
*/
this.lastModified = null;
/**
What to do when the download completes successfully
@type Function
@private
*/
this.callbackFunction = null;
/**
What to do on failure
@type Function
@private
*/
this.onFailure = null;
/**
Flag set on <code>abort</code>
@type boolean
*/
this.aborted = false;
/**
HTTP method. See https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html for possibilities.
@type String
*/
this.method='GET';
/**
Async flag.
@type boolean
*/
this.async=true;
}
new Downloader();
/** Submits the http request. */
Downloader.prototype.send = function (x) {
if (!this.http) { return null; }
return this.http.send(x);
};
/** Aborts the download, setting the <code>aborted</code> field to true. */
Downloader.prototype.abort = function () {
if (!this.http) { return null; }
this.aborted=true;
return this.http.abort();
};
/** Returns the downloaded data. */
Downloader.prototype.getData = function () {if (!this.http) { return null; } return this.http.responseText;};
/** Prepares the download. */
Downloader.prototype.setTarget = function () {
if (!this.http) { return null; }
this.http.open(this.method, this.url, this.async);
this.http.setRequestHeader( 'Api-User-Agent', pg.misc.userAgent );
};
/** Gets the state of the download. */
Downloader.prototype.getReadyState=function () {if (!this.http) { return null; } return this.http.readyState;};
pg.misc.downloadsInProgress = { };
/** Starts the download.
Note that setTarget {@link Downloader#setTarget} must be run first
*/
Downloader.prototype.start=function () {
if (!this.http) { return; }
pg.misc.downloadsInProgress[this.id] = this;
this.http.send(null);
};
/** Gets the 'Last-Modified' date from the download headers.
Should be run after the download completes.
Returns <code>null</code> on failure.
@return {Date}
*/
Downloader.prototype.getLastModifiedDate=function () {
if(!this.http) { return null; }
var lastmod=null;
try {
lastmod=this.http.getResponseHeader('Last-Modified');
} catch (err) {}
if (lastmod) { return new Date(lastmod); }
return null;
};
/** Sets the callback function.
@param {Function} f callback function, called as <code>f(this)</code> on success
*/
Downloader.prototype.setCallback = function (f) {
if(!this.http) { return; }
this.http.onreadystatechange = f;
};
Downloader.prototype.getStatus = function() { if (!this.http) { return null; } return this.http.status; };
//////////////////////////////////////////////////
// helper functions
/** Creates a new {@link Downloader} and prepares it for action.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function newDownload(url, id, callback, onfailure) {
var d=new Downloader(url);
if (!d.http) { return 'ohdear'; }
d.id=id;
d.setTarget();
if (!onfailure) {
onfailure=2;
}
var f = function () {
if (d.getReadyState() == 4) {
delete pg.misc.downloadsInProgress[this.id];
try {
if ( d.getStatus() == 200 ) {
d.data=d.getData();
d.lastModified=d.getLastModifiedDate();
callback(d);
} else if (typeof onfailure == typeof 1) {
if (onfailure > 0) {
// retry
newDownload(url, id, callback, onfailure - 1);
}
} else if (typeof onfailure === 'function') {
onfailure(d,url,id,callback);
}
} catch (somerr) { /* ignore it */ }
}
};
d.setCallback(f);
return d;
}
/** Simulates a download from cached data.
The supplied data is put into a {@link Downloader} as if it had downloaded it.
@param {String} url The url.
@param {integer} id The ID.
@param {Function} callback The callback, which is invoked immediately as <code>callback(d)</code>,
where <code>d</code> is the new {@link Downloader}.
@param {String} data The (cached) data.
@param {Date} lastModified The (cached) last modified date.
*/
function fakeDownload(url, id, callback, data, lastModified, owner) {
var d=newDownload(url,callback);
d.owner=owner;
d.id=id; d.data=data;
d.lastModified=lastModified;
return callback(d);
}
/**
Starts a download.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function startDownload(url, id, callback) {
var d=newDownload(url, id, callback);
if (typeof d == typeof '' ) { return d; }
d.start();
return d;
}
/**
Aborts all downloads which have been started.
*/
function abortAllDownloads() {
for ( var x in pg.misc.downloadsInProgress ) {
try {
pg.misc.downloadsInProgress[x].aborted=true;
pg.misc.downloadsInProgress[x].abort();
delete pg.misc.downloadsInProgress[x];
} catch (e) {}
}
}
// ENDFILE: downloader.js
// STARTFILE: livepreview.js
// TODO: location is often not correct (eg relative links in previews)
// NOTE: removed md5 and image and math parsing. was broken, lots of bytes.
/**
* InstaView - a Mediawiki to HTML converter in JavaScript
* Version 0.6.1
* Copyright (C) Pedro Fayolle 2005-2006
* https://en.wikipedia.org/wiki/User:Pilaf
* Distributed under the BSD license
*
* Changelog:
*
* 0.6.1
* - Fixed problem caused by \r characters
* - Improved inline formatting parser
*
* 0.6
* - Changed name to InstaView
* - Some major code reorganizations and factored out some common functions
* - Handled conversion of relative links (i.e. [[/foo]])
* - Fixed misrendering of adjacent definition list items
* - Fixed bug in table headings handling
* - Changed date format in signatures to reflect Mediawiki's
* - Fixed handling of [[:Image:...]]
* - Updated MD5 function (hopefully it will work with UTF-8)
* - Fixed bug in handling of links inside images
*
* To do:
* - Better support for math tags
* - Full support for <nowiki>
* - Parser-based (as opposed to RegExp-based) inline wikicode handling (make it one-pass and bullet-proof)
* - Support for templates (through AJAX)
* - Support for coloured links (AJAX)
*/
var Insta = {};
function setupLivePreview() {
// options
Insta.conf =
{
baseUrl: '',
user: {},
wiki: {
lang: pg.wiki.lang,
interwiki: pg.wiki.interwiki,
default_thumb_width: 180
},
paths: {
articles: pg.wiki.articlePath + '/',
// Only used for Insta previews with images. (not in popups)
math: '/math/',
images: '//upload.wikimedia.org/wikipedia/en/', // FIXME getImageUrlStart(pg.wiki.hostname),
images_fallback: '//upload.wikimedia.org/wikipedia/commons/',
},
locale: {
user: mw.config.get('wgFormattedNamespaces')[pg.nsUserId],
image: mw.config.get('wgFormattedNamespaces')[pg.nsImageId],
category: mw.config.get('wgFormattedNamespaces')[pg.nsCategoryId],
// shouldn't be used in popup previews, i think
months: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
}
};
// options with default values or backreferences
Insta.conf.user.name = Insta.conf.user.name || 'Wikipedian';
Insta.conf.user.signature = '[['+Insta.conf.locale.user+':'+Insta.conf.user.name+'|'+Insta.conf.user.name+']]';
//Insta.conf.paths.images = '//upload.wikimedia.org/wikipedia/' + Insta.conf.wiki.lang + '/';
// define constants
Insta.BLOCK_IMAGE = new RegExp('^\\[\\[(?:File|Image|'+Insta.conf.locale.image+
'):.*?\\|.*?(?:frame|thumbnail|thumb|none|right|left|center)', 'i');
}
Insta.dump = function(from, to)
{
if (typeof from == 'string') { from = document.getElementById(from); }
if (typeof to == 'string') { to = document.getElementById(to); }
to.innerHTML = this.convert(from.value);
};
Insta.convert = function(wiki)
{
var ll = (typeof wiki == 'string')? wiki.replace(/\r/g,'').split(/\n/): wiki, // lines of wikicode
o = '', // output
p = 0, // para flag
$r; // result of passing a regexp to $()
// some shorthands
function remain() { return ll.length; }
function sh() { return ll.shift(); } // shift
function ps(s) { o += s; } // push
// similar to C's printf, uses ? as placeholders, ?? to escape question marks
function f()
{
var i=1, a=arguments, f=a[0], o='', c, p;
for (; i<a.length; i++) {
if ((p=f.indexOf('?'))+1) {
// allow character escaping
i -= c = f.charAt(p+1)=='?' ? 1 : 0;
o += f.substring(0,p) + (c ? '?' : a[i]);
f = f.substr(p+1+c);
} else { break; }
}
return o+f;
}
function html_entities(s) {
return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
}
// Wiki text parsing to html is a nightmare.
// The below functions deliberately don't escape the ampersand since this would make it more difficult,
// and we don't absolutely need to for how we need it.
// This means that any unescaped ampersands in wikitext will remain unescaped and can cause invalid HTML.
// Browsers should all be able to handle it though.
// We also escape significant wikimarkup characters to prevent further matching on the processed text
function htmlescape_text(s) {
return s.replace(/</g,"<").replace(/>/g,">").replace(/:/g,":").replace(/\[/g,"[").replace(/]/g,"]");
}
function htmlescape_attr(s) {
return htmlescape_text(s).replace(/'/g,"'").replace(/"/g,""");
}
// return the first non matching character position between two strings
function str_imatch(a, b)
{
for (var i=0, l=Math.min(a.length, b.length); i<l; i++) {
if (a.charAt(i)!=b.charAt(i)) { break; }
}
return i;
}
// compare current line against a string or regexp
// if passed a string it will compare only the first string.length characters
// if passed a regexp the result is stored in $r
function $(c) { return (typeof c == 'string') ? (ll[0].substr(0,c.length)==c) : ($r = ll[0].match(c)); }
function $$(c) { return ll[0]==c; } // compare current line against a string
function _(p) { return ll[0].charAt(p); } // return char at pos p
function endl(s) { ps(s); sh(); }
function parse_list()
{
var prev='';
while (remain() && $(/^([*#:;]+)(.*)$/)) {
var l_match = $r;
sh();
var ipos = str_imatch(prev, l_match[1]);
// close uncontinued lists
for (var prevPos=prev.length-1; prevPos >= ipos; prevPos--) {
var pi = prev.charAt(prevPos);
if (pi=='*') { ps('</ul>'); }
else if (pi=='#') { ps('</ol>'); }
// close a dl only if the new item is not a dl item (:, ; or empty)
else if($.inArray(l_match[1].charAt(prevPos), ['','*','#'])) { ps('</dl>'); }
}
// open new lists
for (var matchPos=ipos; matchPos<l_match[1].length; matchPos++) {
var li = l_match[1].charAt(matchPos);
if (li=='*') { ps('<ul>'); }
else if (li=='#') { ps('<ol>'); }
// open a new dl only if the prev item is not a dl item (:, ; or empty)
else if ($.inArray(prev.charAt(matchPos), ['','*','#'])) { ps('<dl>'); }
}
switch (l_match[1].charAt(l_match[1].length-1)) {
case '*': case '#':
ps('<li>' + parse_inline_nowiki(l_match[2]));
break;
case ';':
ps('<dt>');
var dt_match = l_match[2].match(/(.*?)(:.*?)$/);
// handle ;dt :dd format
if (dt_match) {
ps(parse_inline_nowiki(dt_match[1]));
ll.unshift(dt_match[2]);
} else ps(parse_inline_nowiki(l_match[2]));
break;
case ':':
ps('<dd>' + parse_inline_nowiki(l_match[2]));
}
prev=l_match[1];
}
// close remaining lists
for (var i=prev.length-1; i>=0; i--) {
ps(f('</?>', (prev.charAt(i)=='*')? 'ul': ((prev.charAt(i)=='#')? 'ol': 'dl')));
}
}
function parse_table()
{
endl(f('<table>', $(/^\{\|( .*)$/)? $r[1]: ''));
for (;remain();) if ($('|')) switch (_(1)) {
case '}':
endl('</table>');
return;
case '-':
endl(f('<tr>', $(/\|-*(.*)/)[1]));
break;
default:
parse_table_data();
}
else if ($('!')) { parse_table_data(); }
else { sh(); }
}
function parse_table_data()
{
var td_line, match_i;
// 1: "|+", '|' or '+'
// 2: ??
// 3: attributes ??
// TODO: finish commenting this regexp
var td_match = sh().match(/^(\|\+|\||!)((?:([^[|]*?)\|(?!\|))?(.*))$/);
if (td_match[1] == '|+') ps('<caption');
else ps('<t' + ((td_match[1]=='|')?'d':'h'));
if (typeof td_match[3] != 'undefined') {
//ps(' ' + td_match[3])
match_i = 4;
} else match_i = 2;
ps('>');
if (td_match[1] != '|+') {
// use || or !! as a cell separator depending on context
// NOTE: when split() is passed a regexp make sure to use non-capturing brackets
td_line = td_match[match_i].split((td_match[1] == '|')? '||': /(?:\|\||!!)/);
ps(parse_inline_nowiki(td_line.shift()));
while (td_line.length) ll.unshift(td_match[1] + td_line.pop());
} else ps(td_match[match_i]);
var tc = 0, td = [];
while (remain()) {
td.push(sh());
if ($('|')) {
if (!tc) break; // we're at the outer-most level (no nested tables), skip to td parse
else if (_(1)=='}') tc--;
}
else if (!tc && $('!')) break;
else if ($('{|')) tc++;
}
if (td.length) ps(Insta.convert(td));
}
function parse_pre()
{
ps('<pre>');
do {
endl(parse_inline_nowiki(ll[0].substring(1)) + "\n");
} while (remain() && $(' '));
ps('</pre>');
}
function parse_block_image()
{
ps(parse_image(sh()));
}
function parse_image(str)
{
//<NOLITE>
// get what's in between "[[Image:" and "]]"
var tag = str.substring(str.indexOf(':') + 1, str.length - 2);
/* eslint-disable no-unused-vars */
var width;
var attr = [], filename, caption = '';
var thumb=0, frame=0, center=0;
var align='';
/* eslint-enable no-unused-vars */
if (tag.match(/\|/)) {
// manage nested links
var nesting = 0;
var last_attr;
for (var i = tag.length-1; i > 0; i--) {
if (tag.charAt(i) == '|' && !nesting) {
last_attr = tag.substr(i+1);
tag = tag.substring(0, i);
break;
} else switch (tag.substr(i-1, 2)) {
case ']]':
nesting++;
i--;
break;
case '[[':
nesting--;
i--;
}
}
attr = tag.split(/\s*\|\s*/);
attr.push(last_attr);
filename = attr.shift();
var w_match;
for (;attr.length; attr.shift()) {
w_match = attr[0].match(/^(\d*)(?:[px]*\d*)?px$/);
if (w_match) width = w_match[1];
else switch(attr[0]) {
case 'thumb':
case 'thumbnail':
thumb=true;
frame=true;
break;
case 'frame':
frame=true;
break;
case 'none':
case 'right':
case 'left':
center=false;
align=attr[0];
break;
case 'center':
center=true;
align='none';
break;
default:
if (attr.length == 1) caption = attr[0];
}
}
} else filename = tag;
return '';
//</NOLITE>
}
function parse_inline_nowiki(str)
{
var start, lastend=0;
var substart=0, nestlev=0, open, close, subloop;
var html='';
while (-1 != (start = str.indexOf('<nowiki>', substart))) {
html += parse_inline_wiki(str.substring(lastend, start));
start += 8;
substart = start;
subloop = true;
do {
open = str.indexOf('<nowiki>', substart);
close = str.indexOf('</nowiki>', substart);
if (close<=open || open==-1) {
if (close==-1) {
return html + html_entities(str.substr(start));
}
substart = close+9;
if (nestlev) {
nestlev--;
} else {
lastend = substart;
html += html_entities(str.substring(start, lastend-9));
subloop = false;
}
} else {
substart = open+8;
nestlev++;
}
} while (subloop);
}
return html + parse_inline_wiki(str.substr(lastend));
}
function parse_inline_images(str)
{
//<NOLITE>
var start, substart=0, nestlev=0;
var loop, close, open, wiki, html;
while (-1 != (start=str.indexOf('[[', substart))) {
if(str.substr(start+2).match(RegExp('^(Image|File|' + Insta.conf.locale.image + '):','i'))) {
loop=true;
substart=start;
do {
substart+=2;
close=str.indexOf(']]',substart);
open=str.indexOf('[[',substart);
if (close<=open||open==-1) {
if (close==-1) return str;
substart=close;
if (nestlev) {
nestlev--;
} else {
wiki=str.substring(start,close+2);
html=parse_image(wiki);
str=str.replace(wiki,html);
substart=start+html.length;
loop=false;
}
} else {
substart=open;
nestlev++;
}
} while (loop);
} else break;
}
//</NOLITE>
return str;
}
// the output of this function doesn't respect the FILO structure of HTML
// but since most browsers can handle it I'll save myself the hassle
function parse_inline_formatting(str)
{
var em,st,i,li,o='';
while ((i=str.indexOf("''",li))+1) {
o += str.substring(li,i);
li=i+2;
if (str.charAt(i+2)=="'") {
li++;
st=!st;
o+=st?'<strong>':'</strong>';
} else {
em=!em;
o+=em?'<em>':'</em>';
}
}
return o+str.substr(li);
}
function parse_inline_wiki(str)
{
str = parse_inline_images(str);
str = parse_inline_formatting(str);
// math
str = str.replace(/<(?:)math>(.*?)<\/math>/ig, '');
// Build a Mediawiki-formatted date string
var date = new Date();
var minutes = date.getUTCMinutes();
if (minutes < 10) minutes = '0' + minutes;
date = f("?:?, ? ? ? (UTC)", date.getUTCHours(), minutes, date.getUTCDate(), Insta.conf.locale.months[date.getUTCMonth()], date.getUTCFullYear());
// text formatting
return str.
// signatures
replace(/~{5}(?!~)/g, date).
replace(/~{4}(?!~)/g, Insta.conf.user.name+' '+date).
replace(/~{3}(?!~)/g, Insta.conf.user.name).
// [[:Category:...]], [[:Image:...]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):[^|]*?)\\]\\](\\w*)','gi'), function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2));}).
// remove straight category and interwiki tags
replace(RegExp('\\[\\[(?:'+Insta.conf.locale.category+'|'+Insta.conf.wiki.interwiki+'):.*?\\]\\]','gi'),'').
// [[:Category:...|Links]], [[:Image:...|Links]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):.*?)\\|([^\\]]+?)\\]\\](\\w*)','gi'), function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3));}).
// [[/Relative links]]
replace(/\[\[(\/[^|]*?)\]\]/g, function($0,$1){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($1)); }).
// [[/Replaced|Relative links]]
replace(/\[\[(\/.*?)\|(.+?)\]\]/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($2)); }).
// [[Common links]]
replace(/\[\[([^[|]*?)\]\](\w*)/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2)); }).
// [[Replaced|Links]]
replace(/\[\[([^[]*?)\|([^\]]+?)\]\](\w*)/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3)); }).
// [[Stripped:Namespace|Namespace]]
replace(/\[\[([^\]]*?:)?(.*?)( *\(.*?\))?\|\]\]/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1) + htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($2)); }).
// External links
replace(/\[(https?|news|ftp|mailto|gopher|irc):(\/*)([^\]]*?) (.*?)\]/g, function($0,$1,$2,$3,$4){return f("<a class='external' href='?:?'>?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($4)); }).
replace(/\[http:\/\/(.*?)\]/g, function($0,$1){return f("<a class='external' href='http://?'>[#]</a>", htmlescape_attr($1)); }).
replace(/\[(news|ftp|mailto|gopher|irc):(\/*)(.*?)\]/g, function($0,$1,$2,$3){return f("<a class='external' href='?:?'>?:?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($1), htmlescape_text($2) + htmlescape_text($3)); }).
replace(/(^| )(https?|news|ftp|mailto|gopher|irc):(\/*)([^ $]*[^.,!?;: $])/g, function($0,$1,$2,$3,$4){return f("?<a class='external' href='?:?'>?:?</a>", htmlescape_text($1), htmlescape_attr($2), htmlescape_attr($3) + htmlescape_attr($4), htmlescape_text($2), htmlescape_text($3) + htmlescape_text($4)); }).
replace('__NOTOC__','').
replace('__NOEDITSECTION__','');
}
// begin parsing
for (;remain();) if ($(/^(={1,6})(.*)\1(.*)$/)) {
p=0;
endl(f('<h?>?</h?>?', $r[1].length, parse_inline_nowiki($r[2]), $r[1].length, $r[3]));
} else if ($(/^[*#:;]/)) {
p=0;
parse_list();
} else if ($(' ')) {
p=0;
parse_pre();
} else if ($('{|')) {
p=0;
parse_table();
} else if ($(/^----+$/)) {
p=0;
endl('<hr />');
} else if ($(Insta.BLOCK_IMAGE)) {
p=0;
parse_block_image();
} else {
// handle paragraphs
if ($$('')) {
p = (remain()>1 && ll[1]===(''));
if (p) endl('<p><br>');
} else {
if(!p) {
ps('<p>');
p=1;
}
ps(parse_inline_nowiki(ll[0]) + ' ');
}
sh();
}
return o;
};
function wiki2html(txt,baseurl) {
Insta.conf.baseUrl=baseurl;
return Insta.convert(txt);
}
// ENDFILE: livepreview.js
// STARTFILE: pageinfo.js
//<NOLITE>
function popupFilterPageSize(data) {
return formatBytes(data.length);
}
function popupFilterCountLinks(data) {
var num=countLinks(data);
return String(num) + ' ' + ((num!=1)?popupString('wikiLinks'):popupString('wikiLink'));
}
function popupFilterCountImages(data) {
var num=countImages(data);
return String(num) + ' ' + ((num!=1)?popupString('images'):popupString('image'));
}
function popupFilterCountCategories(data) {
var num=countCategories(data);
return String(num) + ' ' + ((num!=1)?popupString('categories'):popupString('category'));
}
function popupFilterLastModified(data,download) {
var lastmod=download.lastModified;
var now=new Date();
var age=now-lastmod;
if (lastmod && getValueOf('popupLastModified')) {
return (tprintf('%s old', [formatAge(age)])).replace(RegExp(' ','g'), ' ');
}
return '';
}
function formatAge(age) {
// coerce into a number
var a=0+age, aa=a;
var seclen = 1000;
var minlen = 60*seclen;
var hourlen = 60*minlen;
var daylen = 24*hourlen;
var weeklen = 7*daylen;
var numweeks = (a-a%weeklen)/weeklen; a = a-numweeks*weeklen; var sweeks = addunit(numweeks, 'week');
var numdays = (a-a%daylen)/daylen; a = a-numdays*daylen; var sdays = addunit(numdays, 'day');
var numhours = (a-a%hourlen)/hourlen; a = a-numhours*hourlen; var shours = addunit(numhours,'hour');
var nummins = (a-a%minlen)/minlen; a = a-nummins*minlen; var smins = addunit(nummins, 'minute');
var numsecs = (a-a%seclen)/seclen; a = a-numsecs*seclen; var ssecs = addunit(numsecs, 'second');
if (aa > 4*weeklen) { return sweeks; }
if (aa > weeklen) { return sweeks + ' ' + sdays; }
if (aa > daylen) { return sdays + ' ' + shours; }
if (aa > 6*hourlen) { return shours; }
if (aa > hourlen) { return shours + ' ' + smins; }
if (aa > 10*minlen) { return smins; }
if (aa > minlen) { return smins + ' ' + ssecs; }
return ssecs;
}
function addunit(num,str) { return '' + num + ' ' + ((num!=1) ? popupString(str+'s') : popupString(str)) ;}
function runPopupFilters(list, data, download) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (list[i] && typeof list[i] == 'function') {
var s=list[i](data, download, download.owner.article);
if (s) { ret.push(s); }
}
}
return ret;
}
function getPageInfo(data, download) {
if (!data || data.length === 0) { return popupString('Empty page'); }
var popupFilters=getValueOf('popupFilters') || [];
var extraPopupFilters = getValueOf('extraPopupFilters') || [];
var pageInfoArray = runPopupFilters(popupFilters.concat(extraPopupFilters), data, download);
var pageInfo=pageInfoArray.join(', ');
if (pageInfo !== '' ) { pageInfo = upcaseFirst(pageInfo); }
return pageInfo;
}
// this could be improved!
function countLinks(wikiText) { return wikiText.split('[[').length - 1; }
// if N = # matches, n = # brackets, then
// String.parenSplit(regex) intersperses the N+1 split elements
// with Nn other elements. So total length is
// L= N+1 + Nn = N(n+1)+1. So N=(L-1)/(n+1).
function countImages(wikiText) {
return (wikiText.parenSplit(pg.re.image).length - 1) / (pg.re.imageBracketCount + 1);
}
function countCategories(wikiText) {
return (wikiText.parenSplit(pg.re.category).length - 1) / (pg.re.categoryBracketCount + 1);
}
function popupFilterStubDetect(data, download, article) {
var counts=stubCount(data, article);
if (counts.real) { return popupString('stub'); }
if (counts.sect) { return popupString('section stub'); }
return '';
}
function popupFilterDisambigDetect(data, download, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return ''; }
return (isDisambig(data, article)) ? popupString('disambig') : '';
}
function formatBytes(num) {
return (num > 949) ? (Math.round(num/100)/10+popupString('kB')) : (num +' ' + popupString('bytes')) ;
}
//</NOLITE>
// ENDFILE: pageinfo.js
// STARTFILE: titles.js
/**
@fileoverview Defines the {@link Title} class, and associated crufty functions.
<code>Title</code> deals with article titles and their various
forms. {@link Stringwrapper} is the parent class of
<code>Title</code>, which exists simply to make things a little
neater.
*/
/**
Creates a new Stringwrapper.
@constructor
@class the Stringwrapper class. This base class is not really
useful on its own; it just wraps various common string operations.
*/
function Stringwrapper() {
/**
Wrapper for this.toString().indexOf()
@param {String} x
@type integer
*/
this.indexOf=function(x){return this.toString().indexOf(x);};
/**
Returns this.value.
@type String
*/
this.toString=function(){return this.value;};
/**
Wrapper for {@link String#parenSplit} applied to this.toString()
@param {RegExp} x
@type Array
*/
this.parenSplit=function(x){return this.toString().parenSplit(x);};
/**
Wrapper for this.toString().substring()
@param {String} x
@param {String} y (optional)
@type String
*/
this.substring=function(x,y){
if (typeof y=='undefined') { return this.toString().substring(x); }
return this.toString().substring(x,y);
};
/**
Wrapper for this.toString().split()
@param {String} x
@type Array
*/
this.split=function(x){return this.toString().split(x);};
/**
Wrapper for this.toString().replace()
@param {String} x
@param {String} y
@type String
*/
this.replace=function(x,y){ return this.toString().replace(x,y); };
}
/**
Creates a new <code>Title</code>.
@constructor
@class The Title class. Holds article titles and converts them into
various forms. Also deals with anchors, by which we mean the bits
of the article URL after a # character, representing locations
within an article.
@param {String} value The initial value to assign to the
article. This must be the canonical title (see {@link
Title#value}. Omit this in the constructor and use another function
to set the title if this is unavailable.
*/
function Title(val) {
/**
The canonical article title. This must be in UTF-8 with no
entities, escaping or nasties. Also, underscores should be
replaced with spaces.
@type String
@private
*/
this.value=null;
/**
The canonical form of the anchor. This should be exactly as
it appears in the URL, i.e. with the .C3.0A bits in.
@type String
*/
this.anchor='';
this.setUtf(val);
}
Title.prototype=new Stringwrapper();
/**
Returns the canonical representation of the article title, optionally without anchor.
@param {boolean} omitAnchor
@fixme Decide specs for anchor
@return String The article title and the anchor.
*/
Title.prototype.toString=function(omitAnchor) {
return this.value + ( (!omitAnchor && this.anchor) ? '#' + this.anchorString() : '' );
};
Title.prototype.anchorString=function() {
if (!this.anchor) { return ''; }
var split=this.anchor.parenSplit(/((?:[.][0-9A-F]{2})+)/);
var len=split.length;
for (var j=1; j<len; j+=2) {
// FIXME s/decodeURI/decodeURIComponent/g ?
split[j]=decodeURIComponent(split[j].split('.').join('%')).split('_').join(' ');
}
return split.join('');
};
Title.prototype.urlAnchor=function() {
var split=this.anchor.parenSplit('/((?:[%][0-9A-F]{2})+)/');
var len=split.length;
for (var j=1; j<len; j+=2) {
split[j]=split[j].split('%').join('.');
}
return split.join('');
};
Title.prototype.anchorFromUtf=function(str) {
this.anchor=encodeURIComponent(str.split(' ').join('_'))
.split('%3A').join(':').split("'").join('%27').split('%').join('.');
};
Title.fromURL=function(h) {
return new Title().fromURL(h);
};
Title.prototype.fromURL=function(h) {
if (typeof h != 'string') {
this.value=null;
return this;
}
// NOTE : playing with decodeURI, encodeURI, escape, unescape,
// we seem to be able to replicate the IE borked encoding
// IE doesn't do this new-fangled utf-8 thing.
// and it's worse than that.
// IE seems to treat the query string differently to the rest of the url
// the query is treated as bona-fide utf8, but the first bit of the url is pissed around with
// we fix up & for all browsers, just in case.
var splitted=h.split('?');
splitted[0]=splitted[0].split('&').join('%26');
h=splitted.join('?');
var contribs=pg.re.contribs.exec(h);
if (contribs) {
if (contribs[1]=='title=') { contribs[3]=contribs[3].split('+').join(' '); }
var u=new Title(contribs[3]);
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + u.stripNamespace()));
return this;
}
var email=pg.re.email.exec(h);
if (email) {
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(email[3]).stripNamespace()));
return this;
}
var backlinks=pg.re.backlinks.exec(h);
if (backlinks) {
this.setUtf(this.decodeNasties(new Title(backlinks[3])));
return this;
}
//A dummy title object for a Special:Diff link.
var specialdiff=pg.re.specialdiff.exec(h);
if (specialdiff) {
this.setUtf(this.decodeNasties(new Title(mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Diff')));
return this;
}
// no more special cases to check --
// hopefully it's not a disguised user-related or specially treated special page
var m=pg.re.main.exec(h);
if(m === null) { this.value=null; }
else {
var fromBotInterface = /[?](.+[&])?title=/.test(h);
if (fromBotInterface) {
m[2]=m[2].split('+').join('_');
}
var extracted = m[2] + (m[3] ? '#' + m[3] : '');
if (pg.flag.isSafari && /%25[0-9A-Fa-f]{2}/.test(extracted)) {
// Fix Safari issue
// Safari sometimes encodes % as %25 in UTF-8 encoded strings like %E5%A3 -> %25E5%25A3.
this.setUtf(decodeURIComponent(unescape(extracted)));
} else {
this.setUtf(this.decodeNasties(extracted));
}
}
return this;
};
Title.prototype.decodeNasties=function(txt) {
var ret= this.decodeEscapes(decodeURI(txt));
ret = ret.replace(/[_ ]*$/, '');
return ret;
};
Title.prototype.decodeEscapes=function(txt) {
var split=txt.parenSplit(/((?:[%][0-9A-Fa-f]{2})+)/);
var len=split.length;
for (var i=1; i<len; i=i+2) {
// FIXME is decodeURIComponent better?
split[i]=unescape(split[i]);
}
return split.join('');
};
Title.fromAnchor=function(a) {
return new Title().fromAnchor(a);
};
Title.prototype.fromAnchor=function(a) {
if (!a) { this.value=null; return this; }
return this.fromURL(a.href);
};
Title.fromWikiText=function(txt) {
return new Title().fromWikiText(txt);
};
Title.prototype.fromWikiText=function(txt) {
// FIXME - testing needed
txt=myDecodeURI(txt);
this.setUtf(txt);
return this;
};
Title.prototype.hintValue=function(){
if(!this.value) { return ''; }
return safeDecodeURI(this.value);
};
//<NOLITE>
Title.prototype.toUserName=function(withNs) {
if (this.namespaceId() != pg.nsUserId && this.namespaceId() != pg.nsUsertalkId) {
this.value=null;
return;
}
this.value = (withNs ? mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' : '') + this.stripNamespace().split('/')[0];
};
Title.prototype.userName=function(withNs) {
var t=(new Title(this.value));
t.toUserName(withNs);
if (t.value) { return t; }
return null;
};
Title.prototype.toTalkPage=function() {
// convert article to a talk page, or if we can't, return null
// In other words: return null if this ALREADY IS a talk page
// and return the corresponding talk page otherwise
//
// Per https://www.mediawiki.org/wiki/Manual:Namespace#Subject_and_talk_namespaces
// * All discussion namespaces have odd-integer indices
// * The discussion namespace index for a specific namespace with index n is n + 1
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId>=0 && namespaceId % 2 === 0) //non-special and subject namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId+1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
//</NOLITE>
// Return canonical, localized namespace
Title.prototype.namespace=function() {
return mw.config.get('wgFormattedNamespaces')[this.namespaceId()];
};
Title.prototype.namespaceId=function() {
var n=this.value.indexOf(':');
if (n<0) { return 0; } //mainspace
var namespaceId = mw.config.get('wgNamespaceIds')[this.value.substring(0,n).split(' ').join('_').toLowerCase()];
if (typeof namespaceId=='undefined') return 0; //mainspace
return namespaceId;
};
//<NOLITE>
Title.prototype.talkPage=function() {
var t=new Title(this.value);
t.toTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.isTalkPage=function() {
if (this.talkPage()===null) { return true; }
return false;
};
Title.prototype.toArticleFromTalkPage=function() {
//largely copy/paste from toTalkPage above.
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId >= 0 && namespaceId % 2 == 1) //non-special and talk namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId-1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
Title.prototype.articleFromTalkPage=function() {
var t=new Title(this.value);
t.toArticleFromTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.articleFromTalkOrArticle=function() {
var t=new Title(this.value);
if ( t.toArticleFromTalkPage() ) { return t; }
return this;
};
Title.prototype.isIpUser=function() {
return pg.re.ipUser.test(this.userName());
};
//</NOLITE>
Title.prototype.stripNamespace=function(){ // returns a string, not a Title
var n=this.value.indexOf(':');
if (n<0) { return this.value; }
var namespaceId = this.namespaceId();
if (namespaceId === pg.nsMainspaceId) return this.value;
return this.value.substring(n+1);
};
Title.prototype.setUtf=function(value){
if (!value) { this.value=''; return; }
var anch=value.indexOf('#');
if(anch < 0) { this.value=value.split('_').join(' '); this.anchor=''; return; }
this.value=value.substring(0,anch).split('_').join(' ');
this.anchor=value.substring(anch+1);
this.ns=null; // wait until namespace() is called
};
Title.prototype.setUrl=function(urlfrag) {
var anch=urlfrag.indexOf('#');
this.value=safeDecodeURI(urlfrag.substring(0,anch));
this.anchor=this.value.substring(anch+1);
};
Title.prototype.append=function(x){
this.setUtf(this.value + x);
};
Title.prototype.urlString=function(x) {
if(!x) { x={}; }
var v=this.toString(true);
if (!x.omitAnchor && this.anchor) { v+= '#' + this.urlAnchor(); }
if (!x.keepSpaces) { v=v.split(' ').join('_'); }
return encodeURI(v).split('&').join('%26').split('?').join('%3F').split('+').join('%2B');
};
Title.prototype.removeAnchor=function() {
return new Title(this.toString(true));
};
Title.prototype.toUrl=function() {
return pg.wiki.titlebase + this.urlString();
};
function parseParams(url) {
var specialDiff = pg.re.specialdiff.exec(url);
if (specialDiff)
{
var split= specialDiff[1].split('/');
if (split.length==1) return {oldid:split[0], diff: 'prev'};
else if (split.length==2) return {oldid: split[0], diff: split[1]};
}
var ret={};
if (url.indexOf('?')==-1) { return ret; }
url = url.split('#')[0];
var s=url.split('?').slice(1).join();
var t=s.split('&');
for (var i=0; i<t.length; ++i) {
var z=t[i].split('=');
z.push(null);
ret[z[0]]=z[1];
}
//Diff revision with no oldid is interpreted as a diff to the previous revision by MediaWiki
if (ret.diff && typeof(ret.oldid)==='undefined')
{
ret.oldid = "prev";
}
//Documentation seems to say something different, but oldid can also accept prev/next, and Echo is emitting such URLs. Simple fixup during parameter decoding:
if (ret.oldid && (ret.oldid==='prev' || ret.oldid==='next' || ret.oldid==='cur'))
{
var helper = ret.diff;
ret.diff = ret.oldid;
ret.oldid = helper;
}
return ret;
}
// (a) myDecodeURI (first standard decodeURI, then pg.re.urlNoPopup)
// (b) change spaces to underscores
// (c) encodeURI (just the straight one, no pg.re.urlNoPopup)
function myDecodeURI (str) {
var ret;
// FIXME decodeURIComponent??
try { ret=decodeURI(str.toString()); }
catch (summat) { return str; }
for (var i=0; i<pg.misc.decodeExtras.length; ++i) {
var from=pg.misc.decodeExtras[i].from;
var to=pg.misc.decodeExtras[i].to;
ret=ret.split(from).join(to);
}
return ret;
}
function safeDecodeURI(str) { var ret=myDecodeURI(str); return ret || str; }
///////////
// TESTS //
///////////
//<NOLITE>
function isDisambig(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
return ! article.isTalkPage() && pg.re.disambig.test(data);
}
function stubCount(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
var sectStub=0;
var realStub=0;
if (pg.re.stub.test(data)) {
var s=data.parenSplit(pg.re.stub);
for (var i=1; i<s.length; i=i+2) {
if (s[i]) { ++sectStub; }
else { ++realStub; }
}
}
return { real: realStub, sect: sectStub };
}
function isValidImageName(str){ // extend as needed...
return ( str.indexOf('{') == -1 );
}
function isInStrippableNamespace(article) {
// Does the namespace allow subpages
// Note, would be better if we had access to wgNamespacesWithSubpages
return ( article.namespaceId() !== 0 );
}
function isInMainNamespace(article) { return article.namespaceId() === 0; }
function anchorContainsImage(a) {
// iterate over children of anchor a
// see if any are images
if (a === null) { return false; }
var kids=a.childNodes;
for (var i=0; i<kids.length; ++i) { if (kids[i].nodeName=='IMG') { return true; } }
return false;
}
//</NOLITE>
function isPopupLink(a) {
// NB for performance reasons, TOC links generally return true
// they should be stripped out later
if (!markNopopupSpanLinks.done) { markNopopupSpanLinks(); }
if (a.inNopopupSpan) { return false; }
// FIXME is this faster inline?
if (a.onmousedown || a.getAttribute('nopopup')) { return false; }
var h=a.href;
if (h === document.location.href+'#') { return false; }
if (!pg.re.basenames.test(h)) { return false; }
if (!pg.re.urlNoPopup.test(h)) { return true; }
return (
(pg.re.email.test(h) || pg.re.contribs.test(h) || pg.re.backlinks.test(h) || pg.re.specialdiff.test(h)) &&
h.indexOf('&limit=') == -1 );
}
function markNopopupSpanLinks() {
if( !getValueOf('popupOnlyArticleLinks'))
fixVectorMenuPopups();
var s = $('.nopopups').toArray();
for (var i=0; i<s.length; ++i) {
var as=s[i].getElementsByTagName('a');
for (var j=0; j<as.length; ++j) {
as[j].inNopopupSpan=true;
}
}
markNopopupSpanLinks.done=true;
}
function fixVectorMenuPopups() {
$('div.vectorMenu h3:first a:first').prop('inNopopupSpan', true);
}
// ENDFILE: titles.js
// STARTFILE: getpage.js
//////////////////////////////////////////////////
// Wiki-specific downloading
//
// Schematic for a getWiki call
//
// getPageWithCaching
// |
// false | true
// getPage<-[findPictureInCache]->-onComplete(a fake download)
// \.
// (async)->addPageToCache(download)->-onComplete(download)
// check cache to see if page exists
function getPageWithCaching(url, onComplete, owner) {
log('getPageWithCaching, url='+url);
var i=findInPageCache(url);
var d;
if (i > -1) {
d=fakeDownload(url, owner.idNumber, onComplete,
pg.cache.pages[i].data, pg.cache.pages[i].lastModified,
owner);
} else {
d=getPage(url, onComplete, owner);
if (d && owner && owner.addDownload) {
owner.addDownload(d);
d.owner=owner;
}
}
}
function getPage(url, onComplete, owner) {
log('getPage');
var callback= function (d) { if (!d.aborted) {addPageToCache(d); onComplete(d);} };
return startDownload(url, owner.idNumber, callback);
}
function findInPageCache(url) {
for (var i=0; i<pg.cache.pages.length; ++i) {
if (url==pg.cache.pages[i].url) { return i; }
}
return -1;
}
function addPageToCache(download) {
log('addPageToCache '+download.url);
var page = {url: download.url, data: download.data, lastModified: download.lastModified};
return pg.cache.pages.push(page);
}
// ENDFILE: getpage.js
// STARTFILE: parensplit.js
//////////////////////////////////////////////////
// parenSplit
// String.prototype.parenSplit should do what ECMAscript says String.prototype.split does,
// interspersing paren matches (regex capturing groups) between the split elements.
// i.e. 'abc'.split(/(b)/)) should return ['a','b','c'], not ['a','c']
if (String('abc'.split(/(b)/))!='a,b,c') {
// broken String.split, e.g. konq, IE < 10
String.prototype.parenSplit=function (re) {
re=nonGlobalRegex(re);
var s=this;
var m=re.exec(s);
var ret=[];
while (m && s) {
// without the following loop, we have
// 'ab'.parenSplit(/a|(b)/) != 'ab'.split(/a|(b)/)
for(var i=0; i<m.length; ++i) {
if (typeof m[i]=='undefined') m[i]='';
}
ret.push(s.substring(0,m.index));
ret = ret.concat(m.slice(1));
s=s.substring(m.index + m[0].length);
m=re.exec(s);
}
ret.push(s);
return ret;
};
} else {
String.prototype.parenSplit=function (re) { return this.split(re); };
String.prototype.parenSplit.isNative=true;
}
function nonGlobalRegex(re) {
var s=re.toString();
var flags='';
for (var j=s.length; s.charAt(j) != '/'; --j) {
if (s.charAt(j) != 'g') { flags += s.charAt(j); }
}
var t=s.substring(1,j);
return RegExp(t,flags);
}
// ENDFILE: parensplit.js
// STARTFILE: tools.js
// IE madness with encoding
// ========================
//
// suppose throughout that the page is in utf8, like wikipedia
//
// if a is an anchor DOM element and a.href should consist of
//
// http://host.name.here/wiki/foo?bar=baz
//
// then IE gives foo as "latin1-encoded" utf8; we have foo = decode_utf8(decodeURI(foo_ie))
// but IE gives bar=baz correctly as plain utf8
//
// ---------------------------------
//
// IE's xmlhttp doesn't understand utf8 urls. Have to use encodeURI here.
//
// ---------------------------------
//
// summat else
// Source: http://aktuell.de.selfhtml.org/artikel/javascript/utf8b64/utf8.htm
//<NOLITE>
function getJsObj(json) {
try {
var json_ret = JSON.parse(json);
if( json_ret.warnings ) {
for( var w=0; w < json_ret.warnings.length; w++ ) {
if( json_ret.warnings[w]['*'] ) {
log( json_ret.warnings[w]['*'] );
} else {
log( json_ret.warnings[w]['warnings'] );
}
}
} else if ( json_ret.error ) {
errlog( json_ret.error.code + ': ' + json_ret.error.info );
}
return json_ret;
} catch (someError) {
errlog('Something went wrong with getJsObj, json='+json);
return 1;
}
}
function anyChild(obj) {
for (var p in obj) {
return obj[p];
}
return null;
}
//</NOLITE>
function upcaseFirst(str) {
if (typeof str != typeof '' || str === '') return '';
return str.charAt(0).toUpperCase() + str.substring(1);
}
function findInArray(arr, foo) {
if (!arr || !arr.length) { return -1; }
var len=arr.length;
for (var i=0; i<len; ++i) { if (arr[i]==foo) { return i; } }
return -1;
}
/* eslint-disable no-unused-vars */
function nextOne (array, value) {
// NB if the array has two consecutive entries equal
// then this will loop on successive calls
var i=findInArray(array, value);
if (i<0) { return null; }
return array[i+1];
}
/* eslint-enable no-unused-vars */
function literalizeRegex(str){
return mw.util.escapeRegExp(str);
}
String.prototype.entify=function() {
//var shy='­';
return this.split('&').join('&').split('<').join('<').split('>').join('>'/*+shy*/).split('"').join('"');
};
// Array filter function
function removeNulls(val) { return val !== null; }
function joinPath(list) {
return list.filter(removeNulls).join('/');
}
function simplePrintf(str, subs) {
if (!str || !subs) { return str; }
var ret=[];
var s=str.parenSplit(/(%s|\$[0-9]+)/);
var i=0;
do {
ret.push(s.shift());
if ( !s.length ) { break; }
var cmd=s.shift();
if (cmd == '%s') {
if ( i < subs.length ) { ret.push(subs[i]); } else { ret.push(cmd); }
++i;
} else {
var j=parseInt( cmd.replace('$', ''), 10 ) - 1;
if ( j > -1 && j < subs.length ) { ret.push(subs[j]); } else { ret.push(cmd); }
}
} while (s.length > 0);
return ret.join('');
}
/* eslint-disable no-unused-vars */
function isString(x) { return (typeof x === 'string' || x instanceof String); }
function isNumber(x) { return (typeof x === 'number' || x instanceof Number); }
function isRegExp(x) { return x instanceof RegExp; }
function isArray (x) { return x instanceof Array; }
function isObject(x) { return x instanceof Object; }
function isFunction(x) {
return !isRegExp(x) && (typeof x === 'function' || x instanceof Function);
}
/* eslint-enable no-unused-vars */
function repeatString(s,mult) {
var ret='';
for (var i=0; i<mult; ++i) { ret += s; }
return ret;
}
function zeroFill(s, min) {
min = min || 2;
var t=s.toString();
return repeatString('0', min - t.length) + t;
}
function map(f, o) {
if (isArray(o)) { return map_array(f,o); }
return map_object(f,o);
}
function map_array(f,o) {
var ret=[];
for (var i=0; i<o.length; ++i) {
ret.push(f(o[i]));
}
return ret;
}
function map_object(f,o) {
var ret={};
for (var i in o) { ret[o]=f(o[i]); }
return ret;
}
pg.escapeQuotesHTML = function ( text ) {
return text
.replace(/&/g, "&")
.replace(/"/g, """)
.replace(/</g, "<")
.replace(/>/g, ">");
};
// ENDFILE: tools.js
// STARTFILE: dab.js
//<NOLITE>
//////////////////////////////////////////////////
// Dab-fixing code
//
function retargetDab(newTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) {
log('retargetDab: newTarget='+newTarget + ' oldTarget=' + oldTarget);
return changeLinkTargetLink(
{newTarget: newTarget,
text: newTarget.split(' ').join(' '),
hint: tprintf('disambigHint', [newTarget]),
summary: simplePrintf(
getValueOf('popupFixDabsSummary'), [friendlyCurrentArticleName, newTarget ]),
clickButton: getValueOf('popupDabsAutoClick'), minor: true, oldTarget: oldTarget,
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit});
}
function listLinks(wikitext, oldTarget, titleToEdit) {
// mediawiki strips trailing spaces, so we do the same
// testcase: https://en.wikipedia.org/w/index.php?title=Radial&oldid=97365633
var reg=RegExp('\\[\\[([^|]*?) *(\\||\\]\\])', 'gi');
var ret=[];
var splitted=wikitext.parenSplit(reg);
// ^[a-z]+ should match interwiki links, hopefully (case-insensitive)
// and ^[a-z]* should match those and [[:Category...]] style links too
var omitRegex=RegExp('^[a-z]*:|^[Ss]pecial:|^[Ii]mage|^[Cc]ategory');
var friendlyCurrentArticleName= oldTarget.toString();
var wikPos = getValueOf('popupDabWiktionary');
for (var i=1; i<splitted.length; i=i+3) {
if (typeof splitted[i] == typeof 'string' && splitted[i].length>0 && !omitRegex.test(splitted[i])) {
ret.push( retargetDab(splitted[i], oldTarget, friendlyCurrentArticleName, titleToEdit) );
} /* if */
} /* for loop */
ret = rmDupesFromSortedList(ret.sort());
if (wikPos) {
var wikTarget='wiktionary:' +
friendlyCurrentArticleName.replace( RegExp('^(.+)\\s+[(][^)]+[)]\\s*$'), '$1' );
var meth;
if (wikPos.toLowerCase() == 'first') { meth = 'unshift'; }
else { meth = 'push'; }
ret[meth]( retargetDab(wikTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) );
}
ret.push(changeLinkTargetLink(
{ newTarget: null,
text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this disambig page from this article"),
clickButton: getValueOf('popupDabsAutoClick'), oldTarget: oldTarget,
summary: simplePrintf(getValueOf('popupRmDabLinkSummary'), [friendlyCurrentArticleName]),
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit
}));
return ret;
}
function rmDupesFromSortedList(list) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (ret.length === 0 || list[i]!=ret[ret.length-1]) { ret.push(list[i]); }
}
return ret;
}
function makeFixDab(data, navpop) {
// grab title from parent popup if there is one; default exists in changeLinkTargetLink
var titleToEdit=(navpop.parentPopup && navpop.parentPopup.article.toString());
var list=listLinks(data, navpop.originalArticle, titleToEdit);
if (list.length === 0) { log('listLinks returned empty list'); return null; }
var html='<hr />' + popupString('Click to disambiguate this link to:') + '<br>';
html+=list.join(', ');
return html;
}
function makeFixDabs(wikiText, navpop) {
if (getValueOf('popupFixDabs') && isDisambig(wikiText, navpop.article) &&
Title.fromURL(location.href).namespaceId() != pg.nsSpecialId &&
navpop.article.talkPage() ) {
setPopupHTML(makeFixDab(wikiText, navpop), 'popupFixDab', navpop.idNumber);
}
}
function popupRedlinkHTML(article) {
return changeLinkTargetLink(
{ newTarget: null, text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this page from this article"),
clickButton: getValueOf('popupRedlinkAutoClick'),
oldTarget: article.toString(),
summary: simplePrintf(getValueOf('popupRedlinkSummary'), [article.toString()])});
}
//</NOLITE>
// ENDFILE: dab.js
// STARTFILE: htmloutput.js
// this has to use a timer loop as we don't know if the DOM element exists when we want to set the text
function setPopupHTML (str, elementId, popupId, onSuccess, append) {
if (typeof popupId === 'undefined') {
//console.error('popupId is not defined in setPopupHTML, html='+str.substring(0,100));
popupId = pg.idNumber;
}
var popupElement=document.getElementById(elementId+popupId);
if (popupElement) {
if (!append) { popupElement.innerHTML=''; }
if (isString(str)) {
popupElement.innerHTML+=str;
} else {
popupElement.appendChild(str);
}
if (onSuccess) { onSuccess(); }
setTimeout(checkPopupPosition, 100);
return true;
} else {
// call this function again in a little while...
setTimeout(function(){
setPopupHTML(str,elementId,popupId,onSuccess);
}, 600);
}
return null;
}
//<NOLITE>
function setPopupTrailer(str,id) {return setPopupHTML(str, 'popupData', id);}
//</NOLITE>
// args.navpopup is mandatory
// optional: args.redir, args.redirTarget
// FIXME: ye gods, this is ugly stuff
function fillEmptySpans(args) {
// if redir is present and true then redirTarget is mandatory
var redir=true;
var rcid;
if (typeof args != 'object' || typeof args.redir == 'undefined' || !args.redir) { redir=false; }
var a=args.navpopup.parentAnchor;
var article, hint=null, oldid=null, params={};
if (redir && typeof args.redirTarget == typeof {}) {
article=args.redirTarget;
//hint=article.hintValue();
} else {
article=(new Title()).fromAnchor(a);
hint=a.originalTitle || article.hintValue();
params=parseParams(a.href);
oldid=(getValueOf('popupHistoricalLinks')) ? params.oldid : null;
rcid=params.rcid;
}
var x={ article:article, hint: hint, oldid: oldid, rcid: rcid, navpop:args.navpopup, params:params };
var structure=pg.structures[getValueOf('popupStructure')];
if (typeof structure != 'object') {
setPopupHTML('popupError', 'Unknown structure (this should never happen): '+
pg.option.popupStructure, args.navpopup.idNumber);
return;
}
var spans=flatten(pg.misc.layout);
var numspans = spans.length;
var redirs=pg.misc.redirSpans;
for (var i=0; i<numspans; ++i) {
var found = redirs && (redirs.indexOf( spans[i] ) !== -1);
//log('redir='+redir+', found='+found+', spans[i]='+spans[i]);
if ( (found && !redir) || (!found && redir) ) {
//log('skipping this set of the loop');
continue;
}
var structurefn=structure[spans[i]];
var setfn = setPopupHTML;
if (getValueOf('popupActiveNavlinks') &&
(spans[i].indexOf('popupTopLinks')===0 || spans[i].indexOf('popupRedirTopLinks')===0)
) {
setfn = setPopupTipsAndHTML;
}
switch (typeof structurefn) {
case 'function':
log('running '+spans[i]+'({article:'+x.article+', hint:'+x.hint+', oldid: '+x.oldid+'})');
setfn(structurefn(x), spans[i], args.navpopup.idNumber);
break;
case 'string':
setfn(structurefn, spans[i], args.navpopup.idNumber);
break;
default:
errlog('unknown thing with label '+spans[i] + ' (span index was ' + i + ')');
break;
}
}
}
// flatten an array
function flatten(list, start) {
var ret=[];
if (typeof start == 'undefined') { start=0; }
for (var i=start; i<list.length; ++i) {
if (typeof list[i] == typeof []) {
return ret.concat(flatten(list[i])).concat(flatten(list, i+1));
}
else { ret.push(list[i]); }
}
return ret;
}
// Generate html for whole popup
function popupHTML (a) {
getValueOf('popupStructure');
var structure=pg.structures[pg.option.popupStructure];
if (typeof structure != 'object') {
//return 'Unknown structure: '+pg.option.popupStructure;
// override user choice
pg.option.popupStructure=pg.optionDefault.popupStructure;
return popupHTML(a);
}
if (typeof structure.popupLayout != 'function') { return 'Bad layout'; }
pg.misc.layout=structure.popupLayout();
if (typeof structure.popupRedirSpans === 'function') { pg.misc.redirSpans=structure.popupRedirSpans(); }
else { pg.misc.redirSpans=[]; }
return makeEmptySpans(pg.misc.layout, a.navpopup);
}
function makeEmptySpans (list, navpop) {
var ret='';
for (var i=0; i<list.length; ++i) {
if (typeof list[i] == typeof '') {
ret += emptySpanHTML(list[i], navpop.idNumber, 'div');
} else if (typeof list[i] == typeof [] && list[i].length > 0 ) {
ret = ret.parenSplit(RegExp('(</[^>]*?>$)')).join(makeEmptySpans(list[i], navpop));
} else if (typeof list[i] == typeof {} && list[i].nodeType ) {
ret += emptySpanHTML(list[i].name, navpop.idNumber, list[i].nodeType);
}
}
return ret;
}
function emptySpanHTML(name, id, tag, classname) {
tag = tag || 'span';
if (!classname) { classname = emptySpanHTML.classAliases[name]; }
classname = classname || name;
if (name == getValueOf('popupDragHandle')) { classname += ' popupDragHandle'; }
return simplePrintf('<%s id="%s" class="%s"></%s>', [tag, name + id, classname, tag]);
}
emptySpanHTML.classAliases={ 'popupSecondPreview': 'popupPreview' };
// generate html for popup image
// <a id="popupImageLinkn"><img id="popupImagen">
// where n=idNumber
function imageHTML(article, idNumber) {
return simplePrintf('<a id="popupImageLink$1">' +
'<img align="right" valign="top" id="popupImg$1" style="display: none;"></img>' +
'</a>', [ idNumber ]);
}
function popTipsSoonFn(id, when, popData) {
if (!when) { when=250; }
var popTips=function(){ setupTooltips(document.getElementById(id), false, true, popData); };
return function() { setTimeout( popTips, when, popData ); };
}
function setPopupTipsAndHTML(html, divname, idnumber, popData) {
setPopupHTML(html, divname, idnumber,
getValueOf('popupSubpopups') ?
popTipsSoonFn(divname + idnumber, null, popData) :
null);
}
// ENDFILE: htmloutput.js
// STARTFILE: mouseout.js
//////////////////////////////////////////////////
// fuzzy checks
function fuzzyCursorOffMenus(x,y, fuzz, parent) {
if (!parent) { return null; }
var uls=parent.getElementsByTagName('ul');
for (var i=0; i<uls.length; ++i) {
if (uls[i].className=='popup_menu') {
if (uls[i].offsetWidth > 0) return false;
} // else {document.title+='.';}
}
return true;
}
function checkPopupPosition () { // stop the popup running off the right of the screen
// FIXME avoid pg.current.link
if (pg.current.link && pg.current.link.navpopup)
pg.current.link.navpopup.limitHorizontalPosition();
}
function mouseOutWikiLink () {
//console ('mouseOutWikiLink');
var a=this;
removeModifierKeyHandler(a);
if (a.navpopup === null || typeof a.navpopup === 'undefined') return;
if ( ! a.navpopup.isVisible() ) {
a.navpopup.banish();
return;
}
restoreTitle(a);
Navpopup.tracker.addHook(posCheckerHook(a.navpopup));
}
function posCheckerHook(navpop) {
return function() {
if (!navpop.isVisible()) { return true; /* remove this hook */ }
if (Navpopup.tracker.dirty) {
return false;
}
var x=Navpopup.tracker.x, y=Navpopup.tracker.y;
var mouseOverNavpop = navpop.isWithin(x,y,navpop.fuzz, navpop.mainDiv) ||
!fuzzyCursorOffMenus(x,y,navpop.fuzz, navpop.mainDiv);
// FIXME it'd be prettier to do this internal to the Navpopup objects
var t=getValueOf('popupHideDelay');
if (t) { t = t * 1000; }
if (!t) {
if(!mouseOverNavpop) {
if(navpop.parentAnchor) {
restoreTitle( navpop.parentAnchor );
}
navpop.banish();
return true; /* remove this hook */
}
return false;
}
// we have a hide delay set
var d=+(new Date());
if ( !navpop.mouseLeavingTime ) {
navpop.mouseLeavingTime = d;
return false;
}
if ( mouseOverNavpop ) {
navpop.mouseLeavingTime=null;
return false;
}
if (d - navpop.mouseLeavingTime > t) {
navpop.mouseLeavingTime=null;
navpop.banish(); return true; /* remove this hook */
}
return false;
};
}
function runStopPopupTimer(navpop) {
// at this point, we should have left the link but remain within the popup
// so we call this function again until we leave the popup.
if (!navpop.stopPopupTimer) {
navpop.stopPopupTimer=setInterval(posCheckerHook(navpop), 500);
navpop.addHook(function(){clearInterval(navpop.stopPopupTimer);},
'hide', 'before');
}
}
// ENDFILE: mouseout.js
// STARTFILE: previewmaker.js
/**
@fileoverview
Defines the {@link Previewmaker} object, which generates short previews from wiki markup.
*/
/**
Creates a new Previewmaker
@constructor
@class The Previewmaker class. Use an instance of this to generate short previews from Wikitext.
@param {String} wikiText The Wikitext source of the page we wish to preview.
@param {String} baseUrl The url we should prepend when creating relative urls.
@param {Navpopup} owner The navpop associated to this preview generator
*/
function Previewmaker(wikiText, baseUrl, owner) {
/** The wikitext which is manipulated to generate the preview. */
this.originalData=wikiText;
this.baseUrl=baseUrl;
this.owner=owner;
this.maxCharacters=getValueOf('popupMaxPreviewCharacters');
this.maxSentences=getValueOf('popupMaxPreviewSentences');
this.setData();
}
Previewmaker.prototype.setData=function() {
var maxSize=Math.max(10000, 2*this.maxCharacters);
this.data=this.originalData.substring(0,maxSize);
};
/** Remove HTML comments
@private
*/
Previewmaker.prototype.killComments = function () {
// this also kills one trailing newline, eg [[diamyo]]
this.data=this.data.replace(RegExp('^<!--[^$]*?-->\\n|\\n<!--[^$]*?-->(?=\\n)|<!--[^$]*?-->', 'g'), '');
};
/**
@private
*/
Previewmaker.prototype.killDivs = function () {
// say goodbye, divs (can be nested, so use * not *?)
this.data=this.data.replace(RegExp('< *div[^>]* *>[\\s\\S]*?< */ *div *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killGalleries = function () {
this.data=this.data.replace(RegExp('< *gallery[^>]* *>[\\s\\S]*?< */ *gallery *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.kill = function(opening, closing, subopening, subclosing, repl) {
var oldk=this.data;
var k=this.killStuff(this.data, opening, closing, subopening, subclosing, repl);
while (k.length < oldk.length) {
oldk=k;
k=this.killStuff(k, opening, closing, subopening, subclosing, repl);
}
this.data=k;
};
/**
@private
*/
Previewmaker.prototype.killStuff = function (txt, opening, closing, subopening, subclosing, repl) {
var op=this.makeRegexp(opening);
var cl=this.makeRegexp(closing, '^');
var sb=subopening ? this.makeRegexp(subopening, '^') : null;
var sc=subclosing ? this.makeRegexp(subclosing, '^') : cl;
if (!op || !cl) {
alert('Navigation Popups error: op or cl is null! something is wrong.');
return;
}
if (!op.test(txt)) { return txt; }
var ret='';
var opResult = op.exec(txt);
ret = txt.substring(0,opResult.index);
txt=txt.substring(opResult.index+opResult[0].length);
var depth = 1;
while (txt.length > 0) {
var removal=0;
if (depth==1 && cl.test(txt)) {
depth--;
removal=cl.exec(txt)[0].length;
} else if (depth > 1 && sc.test(txt)) {
depth--;
removal=sc.exec(txt)[0].length;
}else if (sb && sb.test(txt)) {
depth++;
removal=sb.exec(txt)[0].length;
}
if ( !removal ) { removal = 1; }
txt=txt.substring(removal);
if (depth === 0) { break; }
}
return ret + (repl || '') + txt;
};
/**
@private
*/
Previewmaker.prototype.makeRegexp = function (x, prefix, suffix) {
prefix = prefix || '';
suffix = suffix || '';
var reStr='';
var flags='';
if (isString(x)) {
reStr=prefix + literalizeRegex(x) + suffix;
} else if (isRegExp(x)) {
var s=x.toString().substring(1);
var sp=s.split('/');
flags=sp[sp.length-1];
sp[sp.length-1]='';
s=sp.join('/');
s=s.substring(0,s.length-1);
reStr= prefix + s + suffix;
} else {
log ('makeRegexp failed');
}
log ('makeRegexp: got reStr=' + reStr + ', flags=' + flags);
return RegExp(reStr, flags);
};
/**
@private
*/
Previewmaker.prototype.killBoxTemplates = function () {
// taxobox removal... in fact, there's a saudiprincebox_begin, so let's be more general
// also, have float_begin, ... float_end
this.kill(RegExp('[{][{][^{}\\s|]*?(float|box)[_ ](begin|start)', 'i'), /[}][}]\s*/, '{{');
// infoboxes etc
// from User:Zyxw/popups.js: kill frames too
this.kill(RegExp('[{][{][^{}\\s|]*?(infobox|elementbox|frame)[_ ]', 'i'), /[}][}]\s*/, '{{');
};
/**
@private
*/
Previewmaker.prototype.killTemplates = function () {
// the plus sign is to avoid transcluding [[Template:', ']]
this.kill('{'+'{', '}'+'}', '{', '}', ' ');
};
/**
@private
*/
Previewmaker.prototype.killTables = function () {
// tables are bad, too
// this can be slow, but it's an inprovement over a browser hang
// torture test: [[Comparison_of_Intel_Central_Processing_Units]]
this.kill('{|', /[|]}\s*/, '{|');
this.kill(/<table.*?>/i, /<\/table.*?>/i, /<table.*?>/i);
// remove lines starting with a pipe for the hell of it (?)
this.data=this.data.replace(RegExp('^[|].*$', 'mg'), '');
};
/**
@private
*/
Previewmaker.prototype.killImages = function () {
var forbiddenNamespaceAliases = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=pg.nsImageId && _namespaceId!=pg.nsCategoryId) return;
forbiddenNamespaceAliases.push(_localizedNamespaceLc.split(' ').join('[ _]')); //todo: escape regexp fragments!
});
// images and categories are a nono
this.kill(RegExp('[[][[]\\s*(' + forbiddenNamespaceAliases.join('|') + ')\\s*:', 'i'),
/\]\]\s*/, '[', ']');
};
/**
@private
*/
Previewmaker.prototype.killHTML = function () {
// kill <ref ...>...</ref>
this.kill(/<ref\b[^/>]*?>/i, /<\/ref>/i);
// let's also delete entire lines starting with <. it's worth a try.
this.data=this.data.replace(RegExp('(^|\\n) *<.*', 'g'), '\n');
// and those pesky html tags, but not <nowiki> or <blockquote>
var splitted=this.data.parenSplit(/(<[\w\W]*?(?:>|$|(?=<)))/);
var len=splitted.length;
for (var i=1; i<len; i=i+2) {
switch (splitted[i]) {
case '<nowiki>':
case '</nowiki>':
case '<blockquote>':
case '</blockquote>':
break;
default:
splitted[i]='';
}
}
this.data=splitted.join('');
};
/**
@private
*/
Previewmaker.prototype.killChunks = function() { // heuristics alert
// chunks of italic text? you crazy, man?
var italicChunkRegex=new RegExp
("((^|\\n)\\s*:*\\s*''[^']([^']|'''|'[^']){20}(.|\\n[^\\n])*''[.!?\\s]*\\n)+", 'g');
// keep stuff separated, though, so stick in \n (fixes [[Union Jack]]?
this.data=this.data.replace(italicChunkRegex, '\n');
};
/**
@private
*/
Previewmaker.prototype.mopup = function () {
// we simply *can't* be doing with horizontal rules right now
this.data=this.data.replace(RegExp('^-{4,}','mg'),'');
// no indented lines
this.data=this.data.replace(RegExp('(^|\\n) *:[^\\n]*','g'), '');
// replace __TOC__, __NOTOC__ and whatever else there is
// this'll probably do
this.data=this.data.replace(RegExp('^__[A-Z_]*__ *$', 'gmi'),'');
};
/**
@private
*/
Previewmaker.prototype.firstBit = function () {
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d=this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data=this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data=this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data=this.data.replace(RegExp('^[\\s\\n]*'), '');
var stuff=(RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) { d = stuff[0]; }
if (!getValueOf('popupPreviewFirstParOnly')) { d = this.data; }
/// now put \n\n after sections so that bullets and numbered lists work
d=d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// Split sentences. Superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["'+"'"+']*\\s)','g'));
// leading space is bad, mmkay?
d[0]=d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds=RegExp('([^.][a-z][.] *[a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|op|cit|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
this.fullLength=d.join('').length;
var n=this.maxSentences;
var dd=this.firstSentences(d,n);
do {
dd=this.firstSentences(d,n); --n;
} while ( dd.length > this.maxCharacters && n !== 0 );
this.data = dd;
};
/**
@private
*/
Previewmaker.prototype.fixSentenceEnds = function(strs, reg) {
// take an array of strings, strs
// join strs[i] to strs[i+1] & strs[i+2] if strs[i] matches regex reg
for (var i=0; i<strs.length-2; ++i) {
if (reg.test(strs[i])) {
var a=[];
for (var j=0; j<strs.length; ++j) {
if (j<i) a[j]=strs[j];
if (j==i) a[i]=strs[i]+strs[i+1]+strs[i+2];
if (j>i+2) a[j-2]=strs[j];
}
return this.fixSentenceEnds(a,reg);
}
}
return strs;
};
/**
@private
*/
Previewmaker.prototype.firstSentences = function(strs, howmany) {
var t=strs.slice(0, 2*howmany);
return t.join('');
};
/**
@private
*/
Previewmaker.prototype.killBadWhitespace = function() {
// also cleans up isolated '''', eg [[Suntory Sungoliath]]
this.data=this.data.replace(RegExp('^ *\'+ *$', 'gm'), '');
};
/**
Runs the various methods to generate the preview.
The preview is stored in the <code>html</html> field.
@private
*/
Previewmaker.prototype.makePreview = function() {
if (this.owner.article.namespaceId()!=pg.nsTemplateId &&
this.owner.article.namespaceId()!=pg.nsImageId ) {
this.killComments();
this.killDivs();
this.killGalleries();
this.killBoxTemplates();
if (getValueOf('popupPreviewKillTemplates')) {
this.killTemplates();
} else {
this.killMultilineTemplates();
}
this.killTables();
this.killImages();
this.killHTML();
this.killChunks();
this.mopup();
this.firstBit();
this.killBadWhitespace();
}
else
{
this.killHTML();
}
this.html=wiki2html(this.data, this.baseUrl); // needs livepreview
this.fixHTML();
this.stripLongTemplates();
};
/**
@private
*/
Previewmaker.prototype.esWiki2HtmlPart = function(data) {
var reLinks = /(?:\[\[([^|\]]*)(?:\|([^|\]]*))*]]([a-z]*))/gi; //match a wikilink
reLinks.lastIndex = 0; //reset regex
var match;
var result = "";
var postfixIndex = 0;
while ((match = reLinks.exec(data))) //match all wikilinks
{
//FIXME: the way that link is built here isn't perfect. It is clickable, but popups preview won't recognize it in some cases.
result += pg.escapeQuotesHTML(data.substring(postfixIndex, match.index)) +
'<a href="'+Insta.conf.paths.articles+pg.escapeQuotesHTML(match[1])+'">'+pg.escapeQuotesHTML((match[2]?match[2]:match[1])+match[3])+"</a>";
postfixIndex = reLinks.lastIndex;
}
//append the rest
result += pg.escapeQuotesHTML(data.substring(postfixIndex));
return result;
};
Previewmaker.prototype.editSummaryPreview=function() {
var reAes = /\/\* *(.*?) *\*\//g; //match the first section marker
reAes.lastIndex = 0; //reset regex
var match;
match = reAes.exec(this.data);
if (match)
{
//we have a section link. Split it, process it, combine it.
var prefix = this.data.substring(0,match.index-1);
var section = match[1];
var postfix = this.data.substring(reAes.lastIndex);
var start = "<span class='autocomment'>";
var end = "</span>";
if (prefix.length>0) start = this.esWiki2HtmlPart(prefix) + " " + start + "- ";
if (postfix.length>0) end = ": " + end + this.esWiki2HtmlPart(postfix);
var t=new Title().fromURL(this.baseUrl);
t.anchorFromUtf(section);
var sectionLink = Insta.conf.paths.articles + pg.escapeQuotesHTML(t.toString(true)) + '#' + pg.escapeQuotesHTML(t.anchor);
return start + '<a href="'+sectionLink+'">→</a> '+pg.escapeQuotesHTML(section) + end;
}
//else there's no section link, htmlify the whole thing.
return this.esWiki2HtmlPart(this.data);
};
//<NOLITE>
/** Test function for debugging preview problems one step at a time.
*/
/*eslint-disable */
function previewSteps(txt) {
try {
txt=txt || document.editform.wpTextbox1.value;
} catch (err) {
if (pg.cache.pages.length > 0) {
txt=pg.cache.pages[pg.cache.pages.length-1].data;
} else {
alert('provide text or use an edit page');
}
}
txt=txt.substring(0,10000);
var base=pg.wiki.articlebase + Title.fromURL(document.location.href).urlString();
var p=new Previewmaker(txt, base, pg.current.link.navpopup);
if (this.owner.article.namespaceId() != pg.nsTemplateId) {
p.killComments(); if (!confirm('done killComments(). Continue?\n---\n' + p.data)) { return; }
p.killDivs(); if (!confirm('done killDivs(). Continue?\n---\n' + p.data)) { return; }
p.killGalleries(); if (!confirm('done killGalleries(). Continue?\n---\n' + p.data)) { return; }
p.killBoxTemplates(); if (!confirm('done killBoxTemplates(). Continue?\n---\n' + p.data)) { return; }
if (getValueOf('popupPreviewKillTemplates')) {
p.killTemplates(); if (!confirm('done killTemplates(). Continue?\n---\n' + p.data)) { return; }
} else {
p.killMultilineTemplates(); if (!confirm('done killMultilineTemplates(). Continue?\n---\n' + p.data)) { return; }
}
p.killTables(); if (!confirm('done killTables(). Continue?\n---\n' + p.data)) { return; }
p.killImages(); if (!confirm('done killImages(). Continue?\n---\n' + p.data)) { return; }
p.killHTML(); if (!confirm('done killHTML(). Continue?\n---\n' + p.data)) { return; }
p.killChunks(); if (!confirm('done killChunks(). Continue?\n---\n' + p.data)) { return; }
p.mopup(); if (!confirm('done mopup(). Continue?\n---\n' + p.data)) { return; }
p.firstBit(); if (!confirm('done firstBit(). Continue?\n---\n' + p.data)) { return; }
p.killBadWhitespace(); if (!confirm('done killBadWhitespace(). Continue?\n---\n' + p.data)) { return; }
}
p.html=wiki2html(p.data, base); // needs livepreview
p.fixHTML(); if (!confirm('done fixHTML(). Continue?\n---\n' + p.html)) { return; }
p.stripLongTemplates(); if (!confirm('done stripLongTemplates(). Continue?\n---\n' + p.html)) { return; }
alert('finished preview - end result follows.\n---\n' + p.html);
}
/*eslint-enable */
//</NOLITE>
/**
Works around livepreview bugs.
@private
*/
Previewmaker.prototype.fixHTML = function() {
if(!this.html) return;
var ret = this.html;
// fix question marks in wiki links
// maybe this'll break some stuff :-(
ret=ret.replace(RegExp('(<a href="' + pg.wiki.articlePath + '/[^"]*)[?](.*?")', 'g'), '$1%3F$2');
ret=ret.replace(RegExp('(<a href=\'' + pg.wiki.articlePath + '/[^\']*)[?](.*?\')', 'g'), '$1%3F$2');
// FIXME fix up % too
this.html=ret;
};
/**
Generates the preview and displays it in the current popup.
Does nothing if the generated preview is invalid or consists of whitespace only.
Also activates wikilinks in the preview for subpopups if the popupSubpopups option is true.
*/
Previewmaker.prototype.showPreview = function () {
this.makePreview();
if (typeof this.html != typeof '') return;
if (RegExp('^\\s*$').test(this.html)) return;
setPopupHTML('<hr />', 'popupPrePreviewSep', this.owner.idNumber);
setPopupTipsAndHTML(this.html, 'popupPreview', this.owner.idNumber, { owner: this.owner });
var more = (this.fullLength > this.data.length) ? this.moreLink() : '';
setPopupHTML(more, 'popupPreviewMore', this.owner.idNumber);
};
/**
@private
*/
Previewmaker.prototype.moreLink=function() {
var a=document.createElement('a');
a.className='popupMoreLink';
a.innerHTML=popupString('more...');
var savedThis=this;
a.onclick=function() {
savedThis.maxCharacters+=2000;
savedThis.maxSentences+=20;
savedThis.setData();
savedThis.showPreview();
};
return a;
};
/**
@private
*/
Previewmaker.prototype.stripLongTemplates = function() {
// operates on the HTML!
this.html=this.html.replace(RegExp('^.{0,1000}[{][{][^}]*?(<(p|br)( /)?>\\s*){2,}([^{}]*?[}][}])?', 'gi'), '');
this.html=this.html.split('\n').join(' '); // workaround for <pre> templates
this.html=this.html.replace(RegExp('[{][{][^}]*<pre>[^}]*[}][}]','gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killMultilineTemplates = function() {
this.kill('{{{', '}}}');
this.kill(RegExp('\\s*[{][{][^{}]*\\n'), '}}', '{{');
};
// ENDFILE: previewmaker.js
// STARTFILE: querypreview.js
function loadAPIPreview(queryType, article, navpop) {
var art=new Title(article).urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
var htmlGenerator=function(/*a, d*/){alert('invalid html generator');};
var usernameart = '';
switch (queryType) {
case 'history':
url += 'titles=' + art + '&prop=revisions&rvlimit=' +
getValueOf('popupHistoryPreviewLimit');
htmlGenerator=APIhistoryPreviewHTML;
break;
case 'category':
url += 'list=categorymembers&cmtitle=' + art;
htmlGenerator=APIcategoryPreviewHTML;
break;
case 'userinfo':
var username = new Title( article ).userName();
usernameart = encodeURIComponent( username );
if (pg.re.ipUser.test(username)) {
url += 'list=blocks&bkprop=range|restrictions&bkip=' + usernameart;
} else {
url += 'list=users|usercontribs&usprop=blockinfo|groups|editcount|registration|gender&ususers=' + usernameart + "&meta=globaluserinfo&guiprop=groups|unattached&guiuser="+ usernameart + "&uclimit=1&ucprop=timestamp&ucuser=" + usernameart;
}
htmlGenerator=APIuserInfoPreviewHTML;
break;
case 'contribs':
usernameart = encodeURIComponent( new Title( article ).userName() );
url += 'list=usercontribs&ucuser=' + usernameart +
'&uclimit=' + getValueOf('popupContribsPreviewLimit');
htmlGenerator=APIcontribsPreviewHTML;
break;
case 'imagepagepreview':
var trail='';
if (getValueOf('popupImageLinks')) { trail = '&list=imageusage&iutitle=' + art; }
url += 'titles=' + art + '&prop=revisions|imageinfo&rvprop=content' + trail;
htmlGenerator=APIimagepagePreviewHTML;
break;
case 'backlinks':
url += 'list=backlinks&bltitle=' + art;
htmlGenerator=APIbacklinksPreviewHTML;
break;
case 'revision':
if (article.oldid) {
url += 'revids=' + article.oldid;
} else {
url += 'titles=' + article.removeAnchor().urlString();
}
url += '&prop=revisions|pageprops|info|images|categories&rvprop=ids|timestamp|flags|comment|user|content&cllimit=max&imlimit=max';
htmlGenerator=APIrevisionPreviewHTML;
break;
}
pendingNavpopTask(navpop);
var callback=function(d){
log( "callback of API functions was hit" );
showAPIPreview(queryType, htmlGenerator(article,d,navpop), navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_'+queryType+'_QUERY_DATA'); }
}
function linkList(list) {
list.sort(function(x,y) { return (x==y ? 0 : (x<y ? -1 : 1)); });
var buf=[];
for (var i=0; i<list.length; ++i) {
buf.push(wikiLink({article: new Title(list[i]),
text: list[i].split(' ').join(' '),
action: 'view'}));
}
return buf.join(', ');
}
function getTimeOffset() {
var tz = mw.user.options.get('timecorrection');
if(tz) {
if( tz.indexOf('|') > -1 ) {
// New format
return parseInt(tz.split('|')[1],10);
} else if ( tz.indexOf(':') > -1 ) {
// Old format
return( parseInt(tz,10)*60 + parseInt(tz.split(':')[1],10) );
}
}
return 0;
}
/*
* Creates a HTML table that's shown in the history and user-contribs popups.
* @param {Object[]} h - a list of revisions, returned from the API
* @param {boolean} reallyContribs - true only if we're displaying user contributions
*/
function editPreviewTable(article, h, reallyContribs, timeOffset) {
var html=['<table>'];
var day=null;
var curart=article;
var page=null;
var makeFirstColumnLinks;
if(reallyContribs) {
// We're showing user contributions, so make (diff | hist) links
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&diff=prev' +
'&oldid=' + currentRevision.revid + '">' + popupString('diff') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&action=history">' +
popupString('hist') + '</a>';
result += ')';
return result;
};
} else {
// It's a regular history page, so make (cur | last) links
var firstRevid = h[0].revid;
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=' + firstRevid + '&oldid=' + currentRevision.revid + '">' + popupString('cur') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=prev&oldid=' + currentRevision.revid + '">' + popupString('last') + '</a>';
result += ')';
return result;
};
}
for (var i=0; i<h.length; ++i) {
if (reallyContribs) {
page = h[i].title;
curart = new Title(page);
}
var minor = h[i].minor ? '<b>m </b>' : '';
var editDate = adjustDate(getDateFromTimestamp(h[i].timestamp), timeOffset);
var thisDay = dayFormat(editDate);
var thisTime = timeFormat(editDate);
if (thisDay == day) {
thisDay = '';
} else {
day = thisDay;
}
if (thisDay) {
html.push( '<tr><td colspan=3><span class="popup_history_date">' +
thisDay+'</span></td></tr>' );
}
html.push('<tr class="popup_history_row_' + ( (i%2) ? 'odd' : 'even') + '">');
html.push('<td>' + makeFirstColumnLinks(h[i]) + '</td>');
html.push('<td>' +
'<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&oldid=' + h[i].revid + '">' + thisTime + '</a></td>');
var col3url='', col3txt='';
if (!reallyContribs) {
var user=h[i].user;
if( !h[i].userhidden ) {
if( pg.re.ipUser.test(user) ) {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Contributions&target=' + new Title(user).urlString();
} else {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(user).urlString();
}
col3txt=pg.escapeQuotesHTML(user);
} else {
col3url=getValueOf('popupRevDelUrl');
col3txt=pg.escapeQuotesHTML( popupString('revdel'));
}
} else {
col3url=pg.wiki.titlebase + curart.urlString();
col3txt=pg.escapeQuotesHTML(page);
}
html.push('<td>' + (reallyContribs ? minor : '') +
'<a href="' + col3url + '">' + col3txt + '</a></td>');
var comment='';
var c=h[i].comment || h[i].content;
if (c) {
comment=new Previewmaker(c, new Title(curart).toUrl()).editSummaryPreview();
} else if ( h[i].commenthidden ) {
comment=popupString('revdel');
}
html.push('<td>' + (!reallyContribs ? minor : '') + comment + '</td>');
html.push('</tr>');
html=[html.join('')];
}
html.push('</table>');
return html.join('');
}
function getDateFromTimestamp(t) {
var s=t.split(/[^0-9]/);
switch(s.length) {
case 0: return null;
case 1: return new Date(s[0]);
case 2: return new Date(s[0], s[1]-1);
case 3: return new Date(s[0], s[1]-1, s[2]);
case 4: return new Date(s[0], s[1]-1, s[2], s[3]);
case 5: return new Date(s[0], s[1]-1, s[2], s[3], s[4]);
case 6: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5]);
default: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5], s[6]);
}
}
function adjustDate(d, offset) {
// offset is in minutes
var o=offset * 60 * 1000;
return new Date( +d + o);
}
function dayFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCFullYear(), editDate.getUTCMonth()+1, editDate.getUTCDate()]).join('-'); }
return map(zeroFill, [editDate.getFullYear(), editDate.getMonth()+1, editDate.getDate()]).join('-');
}
function timeFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCHours(), editDate.getUTCMinutes(), editDate.getUTCSeconds()]).join(':'); }
return map(zeroFill, [editDate.getHours(), editDate.getMinutes(), editDate.getSeconds()]).join(':');
}
function showAPIPreview(queryType, html, id, navpop, download) {
// DJ: done
var target='popupPreview';
completedNavpopTask(navpop);
switch (queryType) {
case 'imagelinks':
case 'category':
target='popupPostPreview'; break;
case 'userinfo':
target='popupUserData'; break;
case 'revision':
insertPreview(download);
return;
}
setPopupTipsAndHTML(html, target, id);
}
function APIrevisionPreviewHTML(article, download) {
try{
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
if( page.missing ) {
// TODO we need to fix this proper later on
download.owner = null;
return;
}
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
download.data = content;
download.lastModified = new Date(page.revisions[0].timestamp);
}
} catch(someError) {
return 'Revision preview failed :(';
}
}
function APIbacklinksPreviewHTML(article, download/*, navpop*/ ) {
try {
var jsObj=getJsObj(download.data);
var list=jsObj.query.backlinks;
var html=[];
if (!list) { return popupString('No backlinks found'); }
for ( var i=0; i < list.length; i++ ) {
var t=new Title(list[i].title);
html.push('<a href="' + pg.wiki.titlebase + t.urlString() + '">' + t + '</a>');
}
html=html.join(', ');
if (jsObj['continue'] && jsObj['continue'].blcontinue) {
html += popupString(' and more');
}
return html;
} catch (someError) {
return 'backlinksPreviewHTML went wonky';
}
}
pg.fn.APIsharedImagePagePreviewHTML = function APIsharedImagePagePreviewHTML(obj) {
log( "APIsharedImagePagePreviewHTML" );
var popupid = obj.requestid;
if( obj.query && obj.query.pages )
{
var page=anyChild(obj.query.pages );
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
/* Not entirely safe, but the best we can do */
var p=new Previewmaker(content, pg.current.link.navpopup.article, pg.current.link.navpopup);
p.makePreview();
setPopupHTML( p.html, "popupSecondPreview", popupid );
}
}
};
function APIimagepagePreviewHTML(article, download, navpop) {
try {
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
var content= (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
var ret='';
var alt='';
try{alt=navpop.parentAnchor.childNodes[0].alt;} catch(e){}
if (alt) {
ret = ret + '<hr /><b>' + popupString('Alt text:') + '</b> ' + pg.escapeQuotesHTML(alt);
}
if (typeof content === 'string') {
var p=prepPreviewmaker(content, article, navpop);
p.makePreview();
if (p.html) { ret += '<hr />' + p.html; }
if (getValueOf('popupSummaryData')) {
var info=getPageInfo(content, download);
log(info);
setPopupTrailer(info, navpop.idNumber);
}
}
if (page && page.imagerepository == "shared" ) {
var art=new Title(article);
var encart = encodeURIComponent( "File:" + art.stripNamespace() );
var shared_url = pg.wiki.apicommonsbase + '?format=json&formatversion=2' +
'&callback=pg.fn.APIsharedImagePagePreviewHTML' +
'&requestid=' + navpop.idNumber +
'&action=query&prop=revisions&rvprop=content&titles=' + encart;
ret = ret +'<hr />' + popupString( 'Image from Commons') +
': <a href="' + pg.wiki.commonsbase + '?title=' + encart + '">' +
popupString( 'Description page') + '</a>';
mw.loader.load( shared_url );
}
showAPIPreview('imagelinks', APIimagelinksPreviewHTML(article,download), navpop.idNumber, download);
return ret;
} catch (someError) {
return 'API imagepage preview failed :(';
}
}
function APIimagelinksPreviewHTML(article, download) {
try {
var jsobj=getJsObj(download.data);
var list=jsobj.query.imageusage;
if (list) {
var ret=[];
for (var i=0; i < list.length; i++) {
ret.push(list[i].title);
}
if (ret.length === 0) { return popupString('No image links found'); }
return '<h2>' + popupString('File links') + '</h2>' + linkList(ret);
} else {
return popupString('No image links found');
}
} catch(someError) {
return 'Image links preview generation failed :(';
}
}
function APIcategoryPreviewHTML(article, download) {
try{
var jsobj=getJsObj(download.data);
var list=jsobj.query.categorymembers;
var ret=[];
for (var p=0; p < list.length; p++) {
ret.push(list[p].title);
}
if (ret.length === 0) { return popupString('Empty category'); }
ret = '<h2>' + tprintf('Category members (%s shown)', [ret.length]) + '</h2>' +linkList(ret);
if (jsobj['continue'] && jsobj['continue'].cmcontinue) {
ret += popupString(' and more');
}
return ret;
} catch(someError) {
return 'Category preview failed :(';
}
}
function APIuserInfoPreviewHTML(article, download) {
var ret=[];
var queryobj = {};
try{
queryobj=getJsObj(download.data).query;
} catch(someError) { return 'Userinfo preview failed :('; }
var user=anyChild(queryobj.users);
if (user) {
var globaluserinfo=queryobj.globaluserinfo;
if (user.invalid === '') {
ret.push( popupString( 'Invalid user') );
} else if (user.missing === '') {
ret.push( popupString( 'Not a registered username') );
}
if( user.blockedby ) {
if( user.blockpartial ) {
ret.push('<b>' + popupString('Has blocks') + '</b>');
} else {
ret.push('<b>' + popupString('CẤM') + '</b>');
}
}
if( globaluserinfo && ( 'locked' in globaluserinfo || 'hidden' in globaluserinfo ) ) {
var lockedSulAccountIsAttachedToThis = true;
for( var i=0; globaluserinfo.unattached && i < globaluserinfo.unattached.length; i++) {
if ( globaluserinfo.unattached[i].wiki === mw.config.get('wgDBname') ) {
lockedSulAccountIsAttachedToThis=false;
break;
}
}
if (lockedSulAccountIsAttachedToThis) {
if ( 'locked' in globaluserinfo ) ret.push('<b><i>' + popupString('LOCKED') + '</i></b>');
if ( 'hidden' in globaluserinfo ) ret.push('<b><i>' + popupString('HIDDEN') + '</i></b>');
}
}
if( getValueOf('popupShowGender') && user.gender ) {
switch( user.gender ) {
case "male": ret.push( popupString( "\u2642" ) ); break;
case "female": ret.push( popupString( "\u2640" ) ); break;
}
}
if( user.groups ) {
for( var j=0; j < user.groups.length; j++) {
var currentGroup = user.groups[j];
if( ["*", "user", "autoconfirmed", "extendedconfirmed"].indexOf( currentGroup ) === -1 ) {
ret.push( pg.escapeQuotesHTML(user.groups[j]) );
}
}
}
if( globaluserinfo && globaluserinfo.groups ) {
for( var k=0; k < globaluserinfo.groups.length; k++) {
ret.push( '<i>'+pg.escapeQuotesHTML(globaluserinfo.groups[k])+'</i>' );
}
}
if( user.registration )
ret.push( pg.escapeQuotesHTML((user.editcount?user.editcount:'0') + popupString(' sửa đổi từ: ') + (user.registration?dayFormat(getDateFromTimestamp(user.registration)):'')) );
}
if (queryobj.usercontribs && queryobj.usercontribs.length) {
ret.push( popupString('sửa đổi lần cuối vào ') + dayFormat(getDateFromTimestamp(queryobj.usercontribs[0].timestamp)) );
}
if (queryobj.blocks) {
ret.push( popupString( 'IP user') ); //we only request list=blocks for IPs
for (var l=0; l<queryobj.blocks.length; l++) {
console.log(queryobj);
var rbstr = queryobj.blocks[l].rangestart === queryobj.blocks[l].rangeend ? 'BLOCK' : 'RANGEBLOCK';
rbstr = (!Array.isArray(queryobj.blocks[l].restrictions) ? 'Has ' + rbstr.toLowerCase() + 's' : rbstr + 'ED');
ret.push('<b>' + popupString(rbstr) + '</b>' );
}
}
ret = '<hr />' + ret.join( ', ' );
return ret;
}
function APIcontribsPreviewHTML(article, download, navpop) {
return APIhistoryPreviewHTML(article, download, navpop, true);
}
function APIhistoryPreviewHTML(article, download, navpop, reallyContribs) {
try {
var jsobj=getJsObj(download.data);
var edits = [];
if( reallyContribs ) {
edits=jsobj.query.usercontribs;
} else {
edits=anyChild(jsobj.query.pages).revisions;
}
var ret=editPreviewTable(article, edits, reallyContribs, getTimeOffset());
return ret;
} catch (someError) {
return 'History preview failed :-(';
}
}
//</NOLITE>
// ENDFILE: querypreview.js
// STARTFILE: debug.js
////////////////////////////////////////////////////////////////////
// Debugging functions
////////////////////////////////////////////////////////////////////
function setupDebugging() {
//<NOLITE>
if (window.popupDebug) { // popupDebug is set from .version
window.log=function(x) { //if(gMsg!='')gMsg += '\n'; gMsg+=time() + ' ' + x; };
window.console.log(x);
};
window.errlog=function(x) {
window.console.error(x);
};
log('Initializing logger');
} else {
//</NOLITE>
window.log = function() {};
window.errlog = function() {};
//<NOLITE>
}
//</NOLITE>
}
// ENDFILE: debug.js
// STARTFILE: images.js
// load image of type Title.
function loadImage(image, navpop) {
if (typeof image.stripNamespace != 'function') { alert('loadImages bad'); }
// API call to retrieve image info.
if ( !getValueOf('popupImages') ) return;
if ( !isValidImageName(image) ) return false;
var art=image.urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query';
url += '&prop=imageinfo&iiprop=url|mime&iiurlwidth=' + getValueOf('popupImageSizeLarge');
url += '&titles=' + art;
pendingNavpopTask(navpop);
var callback=function(d){
popupsInsertImage(navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'after', 'DOWNLOAD_IMAGE_QUERY_DATA'); }
}
function popupsInsertImage(id, navpop, download) {
log( "popupsInsertImage");
var imageinfo;
try {
var jsObj=getJsObj(download.data);
var imagepage=anyChild(jsObj.query.pages);
if (typeof imagepage.imageinfo === 'undefined') return;
imageinfo = imagepage.imageinfo[0];
} catch (someError) {
log( "popupsInsertImage failed :(" );
return;
}
var popupImage = document.getElementById("popupImg"+id);
if (!popupImage) {
log( "could not find insertion point for image");
return;
}
popupImage.width=getValueOf('popupImageSize');
popupImage.style.display='inline';
// Set the source for the image.
if( imageinfo.thumburl )
popupImage.src=imageinfo.thumburl;
else if( imageinfo.mime.indexOf("image") === 0 ){
popupImage.src=imageinfo.url;
log( "a thumb could not be found, using original image" );
} else log( "fullsize imagethumb, but not sure if it's an image");
var a=document.getElementById("popupImageLink"+id);
if (a === null) { return null; }
// Determine the action of the surrouding imagelink.
switch (getValueOf('popupThumbAction')) {
case 'imagepage':
if (pg.current.article.namespaceId()!=pg.nsImageId) {
a.href=imageinfo.descriptionurl;
// FIXME: unreliable pg.idNumber
popTipsSoonFn('popupImage' + id)();
break;
}
/* falls through */
case 'sizetoggle':
a.onclick=toggleSize;
a.title=popupString('Toggle image size');
return;
case 'linkfull':
a.href = imageinfo.url;
a.title=popupString('Open full-size image');
return;
}
}
// Toggles the image between inline small and navpop fullwidth.
// It's the same image, no actual sizechange occurs, only display width.
function toggleSize() {
var imgContainer=this;
if (!imgContainer) {
alert('imgContainer is null :/');
return;
}
var img=imgContainer.firstChild;
if (!img) {
alert('img is null :/');
return;
}
if (!img.style.width || img.style.width==='') {
img.style.width='100%';
} else {
img.style.width='';
}
}
// Returns one title of an image from wikiText.
function getValidImageFromWikiText(wikiText) {
// nb in pg.re.image we're interested in the second bracketed expression
// this may change if the regex changes :-(
//var match=pg.re.image.exec(wikiText);
var matched=null;
var match;
// strip html comments, used by evil bots :-(
var t = removeMatchesUnless(wikiText, RegExp('(<!--[\\s\\S]*?-->)'), 1,
RegExp('^<!--[^[]*popup', 'i'));
while ( ( match = pg.re.image.exec(t) ) ) {
// now find a sane image name - exclude templates by seeking {
var m = match[2] || match[6];
if ( isValidImageName(m) ) {
matched=m;
break;
}
}
pg.re.image.lastIndex=0;
if (!matched) { return null; }
return mw.config.get('wgFormattedNamespaces')[pg.nsImageId]+':'+upcaseFirst(matched);
}
function removeMatchesUnless(str, re1, parencount, re2) {
var split=str.parenSplit(re1);
var c=parencount + 1;
for (var i=0; i<split.length; ++i) {
if ( i%c === 0 || re2.test(split[i]) ) { continue; }
split[i]='';
}
return split.join('');
}
//</NOLITE>
// ENDFILE: images.js
// STARTFILE: namespaces.js
// Set up namespaces and other non-strings.js localization
// (currently that means redirs too)
function setNamespaces() {
pg.nsSpecialId = -1;
pg.nsMainspaceId = 0;
pg.nsImageId = 6;
pg.nsUserId = 2;
pg.nsUsertalkId = 3;
pg.nsCategoryId = 14;
pg.nsTemplateId = 10;
}
function setRedirs() {
var r='redirect';
var R='REDIRECT';
var redirLists={
//<NOLITE>
'ar': [ R, 'تحويل' ],
'be': [ r, 'перанакіраваньне' ],
'bg': [ r, 'пренасочване', 'виж' ],
'bs': [ r, 'Preusmjeri', 'preusmjeri', 'PREUSMJERI' ],
'cs': [ R, 'PŘESMĚRUJ' ],
'cy': [ r, 'ail-cyfeirio' ],
'de': [ R, 'WEITERLEITUNG' ],
'el': [ R, 'ΑΝΑΚΑΤΕΥΘΥΝΣΗ'],
'eo': [ R, 'ALIDIREKTU', 'ALIDIREKTI' ],
'es': [ R, 'REDIRECCIÓN' ],
'et': [ r, 'suuna' ],
'ga': [ r, 'athsheoladh' ],
'gl': [ r, 'REDIRECCIÓN', 'REDIRECIONAMENTO'],
'he': [ R, 'הפניה' ],
'hu': [ R, 'ÁTIRÁNYÍTÁS' ],
'is': [ r, 'tilvísun', 'TILVÍSUN' ],
'it': [ R, 'RINVIA', 'Rinvia'],
'ja': [ R, '転送' ],
'mk': [ r, 'пренасочување', 'види' ],
'nds': [ r, 'wiederleiden' ],
'nl': [ R, 'DOORVERWIJZING' ],
'nn': [ r, 'omdiriger' ],
'pl': [ R, 'PATRZ', 'PRZEKIERUJ', 'TAM' ],
'pt': [ R, 'redir' ],
'ru': [ R, 'ПЕРЕНАПРАВЛЕНИЕ', 'ПЕРЕНАПР' ],
'sk': [ r, 'presmeruj' ],
'sr': [ r, 'Преусмери', 'преусмери', 'ПРЕУСМЕРИ', 'Preusmeri', 'preusmeri', 'PREUSMERI' ],
'tt': [ R, 'yünältü', 'перенаправление', 'перенапр' ],
'uk': [ R, 'ПЕРЕНАПРАВЛЕННЯ', 'ПЕРЕНАПР' ],
'vi': [ r, 'đổi' ],
'zh': [ R, '重定向'] // no comma
//</NOLITE>
};
var redirList=redirLists[ pg.wiki.lang ] || [r, R];
// Mediawiki is very tolerant about what comes after the #redirect at the start
pg.re.redirect=RegExp('^\\s*[#](' + redirList.join('|') + ').*?\\[{2}([^\\|\\]]*)(|[^\\]]*)?\\]{2}\\s*(.*)', 'i');
}
function setInterwiki() {
if (pg.wiki.wikimedia) {
// From https://meta.wikimedia.org/wiki/List_of_Wikipedias
pg.wiki.interwiki='aa|ab|ace|af|ak|als|am|an|ang|ar|arc|arz|as|ast|av|ay|az|ba|bar|bat-smg|bcl|be|be-x-old|bg|bh|bi|bjn|bm|bn|bo|bpy|br|bs|bug|bxr|ca|cbk-zam|cdo|ce|ceb|ch|cho|chr|chy|ckb|co|cr|crh|cs|csb|cu|cv|cy|da|de|diq|dsb|dv|dz|ee|el|eml|en|eo|es|et|eu|ext|fa|ff|fi|fiu-vro|fj|fo|fr|frp|frr|fur|fy|ga|gag|gan|gd|gl|glk|gn|got|gu|gv|ha|hak|haw|he|hi|hif|ho|hr|hsb|ht|hu|hy|hz|ia|id|ie|ig|ii|ik|ilo|io|is|it|iu|ja|jbo|jv|ka|kaa|kab|kbd|kg|ki|kj|kk|kl|km|kn|ko|koi|kr|krc|ks|ksh|ku|kv|kw|ky|la|lad|lb|lbe|lg|li|lij|lmo|ln|lo|lt|ltg|lv|map-bms|mdf|mg|mh|mhr|mi|mk|ml|mn|mo|mr|mrj|ms|mt|mus|mwl|my|myv|mzn|na|nah|nap|nds|nds-nl|ne|new|ng|nl|nn|no|nov|nrm|nv|ny|oc|om|or|os|pa|pag|pam|pap|pcd|pdc|pfl|pi|pih|pl|pms|pnb|pnt|ps|pt|qu|rm|rmy|rn|ro|roa-rup|roa-tara|ru|rue|rw|sa|sah|sc|scn|sco|sd|se|sg|sh|si|simple|sk|sl|sm|sn|so|sq|sr|srn|ss|st|stq|su|sv|sw|szl|ta|te|tet|tg|th|ti|tk|tl|tn|to|tpi|tr|ts|tt|tum|tw|ty|udm|ug|uk|ur|uz|ve|vec|vi|vls|vo|wa|war|wo|wuu|xal|xh|yi|yo|za|zea|zh|zh-classical|zh-min-nan|zh-yue|zu';
pg.re.interwiki=RegExp('^'+pg.wiki.interwiki+':');
} else {
pg.wiki.interwiki=null;
pg.re.interwiki=RegExp('^$');
}
}
// return a regexp pattern matching all variants to write the given namespace
function nsRe(namespaceId) {
var imageNamespaceVariants = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=namespaceId) return;
_localizedNamespaceLc = upcaseFirst(_localizedNamespaceLc);
imageNamespaceVariants.push(mw.util.escapeRegExp(_localizedNamespaceLc).split(' ').join('[ _]'));
imageNamespaceVariants.push(mw.util.escapeRegExp(encodeURI(_localizedNamespaceLc)));
});
return '(?:' + imageNamespaceVariants.join('|') + ')';
}
function nsReImage() {
return nsRe(pg.nsImageId);
}
// ENDFILE: namespaces.js
// STARTFILE: selpop.js
//<NOLITE>
function getEditboxSelection() {
// see http://www.webgurusforum.com/8/12/0
var editbox;
try {
editbox=document.editform.wpTextbox1;
} catch (dang) { return; }
// IE, Opera
if (document.selection) { return document.selection.createRange().text; }
// Mozilla
var selStart = editbox.selectionStart;
var selEnd = editbox.selectionEnd;
return (editbox.value).substring(selStart, selEnd);
}
function doSelectionPopup() {
// popup if the selection looks like [[foo|anything afterwards at all
// or [[foo|bar]]text without ']]'
// or [[foo|bar]]
var sel=getEditboxSelection();
var open=sel.indexOf('[[');
var pipe=sel.indexOf('|');
var close=sel.indexOf(']]');
if (open == -1 || ( pipe == -1 && close == -1) ) { return; }
if (pipe != -1 && open > pipe || close != -1 && open > close) { return; }
if (getValueOf('popupOnEditSelection')=='boxpreview') {
return doSeparateSelectionPopup(sel);
}
var article=new Title(sel.substring(open+2, (pipe < 0) ? close : pipe)).urlString();
if (close > 0 && sel.substring(close+2).indexOf('[[') >= 0) {
return;
}
var a=document.createElement('a');
a.href=pg.wiki.titlebase + article;
mouseOverWikiLink2(a);
if (a.navpopup) {
a.navpopup.addHook(function(){runStopPopupTimer(a.navpopup);}, 'unhide', 'after');
}
}
function doSeparateSelectionPopup(str) {
var div=document.getElementById('selectionPreview');
if (!div) {
div = document.createElement('div');
div.id='selectionPreview';
try {
var box=document.editform.wpTextbox1;
box.parentNode.insertBefore(div, box);
} catch (error) {
return;
}
}
div.innerHTML=wiki2html(str);
div.ranSetupTooltipsAlready = false;
popTipsSoonFn('selectionPreview')();
}
//</NOLITE>
// ENDFILE: selpop.js
// STARTFILE: navpopup.js
/**
@fileoverview Defines two classes: {@link Navpopup} and {@link Mousetracker}.
<code>Navpopup</code> describes popups: when they appear, where, what
they look like and so on.
<code>Mousetracker</code> "captures" the mouse using
<code>document.onmousemove</code>.
*/
/**
Creates a new Mousetracker.
@constructor
@class The Mousetracker class. This monitors mouse movements and manages associated hooks.
*/
function Mousetracker() {
/**
Interval to regularly run the hooks anyway, in milliseconds.
@type Integer
*/
this.loopDelay=400;
/**
Timer for the loop.
@type Timer
*/
this.timer=null;
/**
Flag - are we switched on?
@type Boolean
*/
this.active=false;
/**
Flag - are we probably inaccurate, i.e. not reflecting the actual mouse position?
*/
this.dirty=true;
/**
Array of hook functions.
@private
@type Array
*/
this.hooks=[];
}
/**
Adds a hook, to be called when we get events.
@param {Function} f A function which is called as
<code>f(x,y)</code>. It should return <code>true</code> when it
wants to be removed, and <code>false</code> otherwise.
*/
Mousetracker.prototype.addHook = function (f) {
this.hooks.push(f);
};
/**
Runs hooks, passing them the x
and y coords of the mouse. Hook functions that return true are
passed to {@link Mousetracker#removeHooks} for removal.
@private
*/
Mousetracker.prototype.runHooks = function () {
if (!this.hooks || !this.hooks.length) { return; }
//log('Mousetracker.runHooks; we got some hooks to run');
var remove=false;
var removeObj={};
// this method gets called a LOT -
// pre-cache some variables
var x=this.x, y=this.y, len = this.hooks.length;
for (var i=0; i<len; ++i) {
//~ run the hook function, and remove it if it returns true
if (this.hooks[i](x, y)===true) {
remove=true;
removeObj[i]=true;
}
}
if (remove) { this.removeHooks(removeObj); }
};
/**
Removes hooks.
@private
@param {Object} removeObj An object whose keys are the index
numbers of functions for removal, with values that evaluate to true
*/
Mousetracker.prototype.removeHooks = function(removeObj) {
var newHooks=[];
var len = this.hooks.length;
for (var i=0; i<len; ++i) {
if (! removeObj[i]) { newHooks.push(this.hooks[i]); }
}
this.hooks=newHooks;
};
/**
Event handler for mouse wiggles.
We simply grab the event, set x and y and run the hooks.
This makes the cpu all hot and bothered :-(
@private
@param {Event} e Mousemove event
*/
Mousetracker.prototype.track=function (e) {
//~ Apparently this is needed in IE.
e = e || window.event;
var x, y;
if (e) {
if (e.pageX) { x=e.pageX; y=e.pageY; }
else if (typeof e.clientX!='undefined') {
var left, top, docElt = document.documentElement;
if (docElt) { left=docElt.scrollLeft; }
left = left || document.body.scrollLeft || document.scrollLeft || 0;
if (docElt) { top=docElt.scrollTop; }
top = top || document.body.scrollTop || document.scrollTop || 0;
x=e.clientX + left;
y=e.clientY + top;
} else { return; }
this.setPosition(x,y);
}
};
/**
Sets the x and y coordinates stored and takes appropriate action,
running hooks as appropriate.
@param {Integer} x, y Screen coordinates to set
*/
Mousetracker.prototype.setPosition=function(x,y) {
this.x = x;
this.y = y;
if (this.dirty || this.hooks.length === 0) { this.dirty=false; return; }
if (typeof this.lastHook_x != 'number') { this.lastHook_x = -100; this.lastHook_y=-100; }
var diff = (this.lastHook_x - x)*(this.lastHook_y - y);
diff = (diff >= 0) ? diff : -diff;
if ( diff > 1 ) {
this.lastHook_x=x;
this.lastHook_y=y;
if (this.dirty) { this.dirty = false; }
else { this.runHooks(); }
}
};
/**
Sets things in motion, unless they are already that is, registering an event handler on <code>document.onmousemove</code>.
A half-hearted attempt is made to preserve the old event handler if there is one.
*/
Mousetracker.prototype.enable = function () {
if (this.active) { return; }
this.active=true;
//~ Save the current handler for mousemove events. This isn't too
//~ robust, of course.
this.savedHandler=document.onmousemove;
//~ Gotta save @tt{this} again for the closure, and use apply for
//~ the member function.
var savedThis=this;
document.onmousemove=function (e) {savedThis.track.apply(savedThis, [e]);};
if (this.loopDelay) { this.timer = setInterval(function() { //log('loop delay in mousetracker is working');
savedThis.runHooks();}, this.loopDelay); }
};
/**
Disables the tracker, removing the event handler.
*/
Mousetracker.prototype.disable = function () {
if (!this.active) { return; }
if (typeof this.savedHandler === 'function') {
document.onmousemove=this.savedHandler;
} else { delete document.onmousemove; }
if (this.timer) { clearInterval(this.timer); }
this.active=false;
};
/**
Creates a new Navpopup.
Gets a UID for the popup and
@param init Contructor object. If <code>init.draggable</code> is true or absent, the popup becomes draggable.
@constructor
@class The Navpopup class. This generates popup hints, and does some management of them.
*/
function Navpopup(/*init*/) {
//alert('new Navpopup(init)');
/** UID for each Navpopup instance.
Read-only.
@type integer
*/
this.uid=Navpopup.uid++;
/**
Read-only flag for current visibility of the popup.
@type boolean
@private
*/
this.visible=false;
/** Flag to be set when we want to cancel a previous request to
show the popup in a little while.
@private
@type boolean
*/
this.noshow=false;
/** Categorised list of hooks.
@see #runHooks
@see #addHook
@private
@type Object
*/
this.hooks={
'create': [],
'unhide': [],
'hide': []
};
/** list of unique IDs of hook functions, to avoid duplicates
@private
*/
this.hookIds={};
/** List of downloads associated with the popup.
@private
@type Array
*/
this.downloads=[];
/** Number of uncompleted downloads.
@type integer
*/
this.pending=null;
/** Tolerance in pixels when detecting whether the mouse has left the popup.
@type integer
*/
this.fuzz=5;
/** Flag to toggle running {@link #limitHorizontalPosition} to regulate the popup's position.
@type boolean
*/
this.constrained=true;
/** The popup width in pixels.
@private
@type integer
*/
this.width=0;
/** The popup width in pixels.
@private
@type integer
*/
this.height=0;
/** The main content DIV element.
@type HTMLDivElement
*/
this.mainDiv=null;
this.createMainDiv();
// if (!init || typeof init.popups_draggable=='undefined' || init.popups_draggable) {
// this.makeDraggable(true);
// }
}
/**
A UID for each Navpopup. This constructor property is just a counter.
@type integer
@private
*/
Navpopup.uid=0;
/**
Retrieves the {@link #visible} attribute, indicating whether the popup is currently visible.
@type boolean
*/
Navpopup.prototype.isVisible=function() {
return this.visible;
};
/**
Repositions popup using CSS style.
@private
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@param {boolean} noLimitHor Don't call {@link #limitHorizontalPosition}
*/
Navpopup.prototype.reposition= function (x,y, noLimitHor) {
log ('reposition('+x+','+y+','+noLimitHor+')');
if (typeof x != 'undefined' && x !== null) { this.left=x; }
if (typeof y != 'undefined' && y !== null) { this.top=y; }
if (typeof this.left != 'undefined' && typeof this.top != 'undefined') {
this.mainDiv.style.left=this.left + 'px';
this.mainDiv.style.top=this.top + 'px';
}
if (!noLimitHor) { this.limitHorizontalPosition(); }
//console.log('navpop'+this.uid+' - (left,top)=(' + this.left + ',' + this.top + '), css=('
//+ this.mainDiv.style.left + ',' + this.mainDiv.style.top + ')');
};
/**
Prevents popups from being in silly locations. Hopefully.
Should not be run if {@link #constrained} is true.
@private
*/
Navpopup.prototype.limitHorizontalPosition=function() {
if (!this.constrained || this.tooWide) { return; }
this.updateDimensions();
var x=this.left;
var w=this.width;
var cWidth=document.body.clientWidth;
// log('limitHorizontalPosition: x='+x+
// ', this.left=' + this.left +
// ', this.width=' + this.width +
// ', cWidth=' + cWidth);
if ( (x+w) >= cWidth ||
( x > 0 &&
this.maxWidth &&
this.width < this.maxWidth &&
this.height > this.width &&
x > cWidth - this.maxWidth ) ) {
// This is a very nasty hack. There has to be a better way!
// We find the "natural" width of the div by positioning it at the far left
// then reset it so that it should be flush right (well, nearly)
this.mainDiv.style.left='-10000px';
this.mainDiv.style.width = this.maxWidth + 'px';
var naturalWidth=parseInt(this.mainDiv.offsetWidth, 10);
var newLeft=cWidth - naturalWidth - 1;
if (newLeft < 0) { newLeft = 0; this.tooWide=true; } // still unstable for really wide popups?
log ('limitHorizontalPosition: moving to ('+newLeft + ','+ this.top+');' + ' naturalWidth=' + naturalWidth + ', clientWidth=' + cWidth);
this.reposition(newLeft, null, true);
}
};
/**
Counter indicating the z-order of the "highest" popup.
We start the z-index at 1000 so that popups are above everything
else on the screen.
@private
@type integer
*/
Navpopup.highest=1000;
/**
Brings popup to the top of the z-order.
We increment the {@link #highest} property of the contructor here.
@private
*/
Navpopup.prototype.raise = function () {
this.mainDiv.style.zIndex=Navpopup.highest + 1;
++Navpopup.highest;
};
/**
Shows the popup provided {@link #noshow} is not true.
Updates the position, brings the popup to the top of the z-order and unhides it.
*/
Navpopup.prototype.show = function () {
//document.title+='s';
if (this.noshow) { return; }
//document.title+='t';
this.reposition();
this.raise();
this.unhide();
};
/**
Checks to see if the mouse pointer has
stabilised (checking every <code>time</code>/2 milliseconds) and runs the
{@link #show} method if it has.
@param {integer} time The minimum time (ms) before the popup may be shown.
*/
Navpopup.prototype.showSoonIfStable = function (time) {
log ('showSoonIfStable, time='+time);
if (this.visible) { return; }
this.noshow = false;
//~ initialize these variables so that we never run @tt{show} after
//~ just half the time
this.stable_x = -10000; this.stable_y = -10000;
var stableShow = function() {
log('stableShow called');
var new_x = Navpopup.tracker.x, new_y = Navpopup.tracker.y;
var dx = savedThis.stable_x - new_x, dy = savedThis.stable_y - new_y;
var fuzz2 = 0; // savedThis.fuzz * savedThis.fuzz;
//document.title += '[' + [savedThis.stable_x,new_x, savedThis.stable_y,new_y, dx, dy, fuzz2].join(',') + '] ';
if ( dx * dx <= fuzz2 && dy * dy <= fuzz2 ) {
log ('mouse is stable');
clearInterval(savedThis.showSoonStableTimer);
savedThis.reposition.apply(savedThis, [new_x + 2, new_y + 2]);
savedThis.show.apply(savedThis, []);
savedThis.limitHorizontalPosition.apply(savedThis, []);
return;
}
savedThis.stable_x = new_x; savedThis.stable_y = new_y;
};
var savedThis = this;
this.showSoonStableTimer = setInterval(stableShow, time/2);
};
/**
Sets the {@link #noshow} flag and hides the popup. This should be called
when the mouse leaves the link before
(or after) it's actually been displayed.
*/
Navpopup.prototype.banish = function () {
log ('banish called');
// hide and prevent showing with showSoon in the future
this.noshow=true;
if (this.showSoonStableTimer) {
log('clearing showSoonStableTimer');
clearInterval(this.showSoonStableTimer);
}
this.hide();
};
/**
Runs hooks added with {@link #addHook}.
@private
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
*/
Navpopup.prototype.runHooks = function (key, when) {
if (!this.hooks[key]) { return; }
var keyHooks=this.hooks[key];
var len=keyHooks.length;
for (var i=0; i< len; ++i) {
if (keyHooks[i] && keyHooks[i].when == when) {
if (keyHooks[i].hook.apply(this, [])) {
// remove the hook
if (keyHooks[i].hookId) {
delete this.hookIds[keyHooks[i].hookId];
}
keyHooks[i]=null;
}
}
}
};
/**
Adds a hook to the popup. Hook functions are run with <code>this</code> set to refer to the Navpopup instance, and no arguments.
@param {Function} hook The hook function. Functions that return true are deleted.
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
@param {String} uid A truthy string identifying the hook function; if it matches another hook in this position, it won't be added again.
*/
Navpopup.prototype.addHook = function ( hook, key, when, uid ) {
when = when || 'after';
if (!this.hooks[key]) { return; }
// if uid is specified, don't add duplicates
var hookId=null;
if (uid) {
hookId=[key,when,uid].join('|');
if (this.hookIds[hookId]) {
return;
}
this.hookIds[hookId]=true;
}
this.hooks[key].push( {hook: hook, when: when, hookId: hookId} );
};
/**
Creates the main DIV element, which contains all the actual popup content.
Runs hooks with key 'create'.
@private
*/
Navpopup.prototype.createMainDiv = function () {
if (this.mainDiv) { return; }
this.runHooks('create', 'before');
var mainDiv=document.createElement('div');
var savedThis=this;
mainDiv.onclick=function(e) {savedThis.onclickHandler(e);};
mainDiv.className=(this.className) ? this.className : 'navpopup_maindiv';
mainDiv.id=mainDiv.className + this.uid;
mainDiv.style.position='absolute';
mainDiv.style.minWidth = '350px';
mainDiv.style.display='none';
mainDiv.className='navpopup';
// easy access to javascript object through DOM functions
mainDiv.navpopup=this;
this.mainDiv=mainDiv;
document.body.appendChild(mainDiv);
this.runHooks('create', 'after');
};
/**
Calls the {@link #raise} method.
@private
*/
Navpopup.prototype.onclickHandler=function(/*e*/) {
this.raise();
};
/**
Makes the popup draggable, using a {@link Drag} object.
@private
*/
Navpopup.prototype.makeDraggable=function(handleName) {
if (!this.mainDiv) { this.createMainDiv(); }
var drag=new Drag();
if (!handleName) {
drag.startCondition=function(e) {
try { if (!e.shiftKey) { return false; } } catch (err) { return false; }
return true;
};
}
var dragHandle;
if (handleName) dragHandle = document.getElementById(handleName);
if (!dragHandle) dragHandle = this.mainDiv;
var np=this;
drag.endHook=function(x,y) {
Navpopup.tracker.dirty=true;
np.reposition(x,y);
};
drag.init(dragHandle,this.mainDiv);
};
/** Hides the popup using CSS. Runs hooks with key 'hide'.
Sets {@link #visible} appropriately. {@link #banish} should be called externally instead of this method.
@private
*/
Navpopup.prototype.hide = function () {
this.runHooks('hide', 'before');
this.abortDownloads();
if (typeof this.visible != 'undefined' && this.visible) {
this.mainDiv.style.display='none';
this.visible=false;
}
this.runHooks('hide', 'after');
};
/** Shows the popup using CSS. Runs hooks with key 'unhide'.
Sets {@link #visible} appropriately. {@link #show} should be called externally instead of this method.
@private
*/
Navpopup.prototype.unhide = function () {
this.runHooks('unhide', 'before');
if (typeof this.visible != 'undefined' && !this.visible) {
this.mainDiv.style.display='inline';
this.visible=true;
}
this.runHooks('unhide', 'after');
};
/**
Sets the <code>innerHTML</code> attribute of the main div containing the popup content.
@param {String} html The HTML to set.
*/
Navpopup.prototype.setInnerHTML = function (html) {
this.mainDiv.innerHTML = html;
};
/**
Updates the {@link #width} and {@link #height} attributes with the CSS properties.
@private
*/
Navpopup.prototype.updateDimensions = function () {
this.width=parseInt(this.mainDiv.offsetWidth, 10);
this.height=parseInt(this.mainDiv.offsetHeight, 10);
};
/**
Checks if the point (x,y) is within {@link #fuzz} of the
{@link #mainDiv}.
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@type boolean
*/
Navpopup.prototype.isWithin = function(x,y) {
//~ If we're not even visible, no point should be considered as
//~ being within the popup.
if (!this.visible) { return false; }
this.updateDimensions();
var fuzz=this.fuzz || 0;
//~ Use a simple box metric here.
return (x+fuzz >= this.left && x-fuzz <= this.left + this.width &&
y+fuzz >= this.top && y-fuzz <= this.top + this.height);
};
/**
Adds a download to {@link #downloads}.
@param {Downloader} download
*/
Navpopup.prototype.addDownload=function(download) {
if (!download) { return; }
this.downloads.push(download);
};
/**
Aborts the downloads listed in {@link #downloads}.
@see Downloader#abort
*/
Navpopup.prototype.abortDownloads=function() {
for(var i=0; i<this.downloads.length; ++i) {
var d=this.downloads[i];
if (d && d.abort) { d.abort(); }
}
this.downloads=[];
};
/**
A {@link Mousetracker} instance which is a property of the constructor (pseudo-global).
*/
Navpopup.tracker=new Mousetracker();
// ENDFILE: navpopup.js
// STARTFILE: diff.js
//<NOLITE>
/*
* Javascript Diff Algorithm
* By John Resig (http://ejohn.org/) and [[:en:User:Lupin]]
*
* More Info:
* http://ejohn.org/projects/javascript-diff-algorithm/
*/
function delFmt(x) {
if (!x.length) { return ''; }
return "<del class='popupDiff'>" + x.join('') +"</del>";
}
function insFmt(x) {
if (!x.length) { return ''; }
return "<ins class='popupDiff'>" + x.join('') +"</ins>";
}
function countCrossings(a, b, i, eject) {
// count the crossings on the edge starting at b[i]
if (!b[i].row && b[i].row !== 0) { return -1; }
var count=0;
for (var j=0; j<a.length; ++j) {
if (!a[j].row && a[j].row !== 0) { continue; }
if ( (j-b[i].row)*(i-a[j].row) > 0) {
if(eject) { return true; }
count++;
}
}
return count;
}
function shortenDiffString(str, context) {
var re=RegExp('(<del[\\s\\S]*?</del>|<ins[\\s\\S]*?</ins>)');
var splitted=str.parenSplit(re);
var ret=[''];
for (var i=0; i<splitted.length; i+=2) {
if (splitted[i].length < 2*context) {
ret[ret.length-1] += splitted[i];
if (i+1<splitted.length) { ret[ret.length-1] += splitted[i+1]; }
continue;
}
else {
if (i > 0) { ret[ret.length-1] += splitted[i].substring(0,context); }
if (i+1 < splitted.length) {
ret.push(splitted[i].substring(splitted[i].length-context) +
splitted[i+1]);
}
}
}
while (ret.length > 0 && !ret[0]) { ret = ret.slice(1); }
return ret;
}
function diffString( o, n, simpleSplit ) {
var splitRe=RegExp('([[]{2}|[\\]]{2}|[{]{2,3}|[}]{2,3}|[|]|=|<|>|[*:]+|\\s|\\b)');
// We need to split the strings o and n first, and entify() the parts
// individually, so that the HTML entities are never cut apart. (AxelBoldt)
var out, i, oSplitted, nSplitted;
if (simpleSplit) {
oSplitted=o.split(/\b/);
nSplitted=n.split(/\b/);
} else {
oSplitted=o.parenSplit(splitRe);
nSplitted=n.parenSplit(splitRe);
}
for (i=0; i<oSplitted.length; ++i) {oSplitted[i]=oSplitted[i].entify();}
for (i=0; i<nSplitted.length; ++i) {nSplitted[i]=nSplitted[i].entify();}
out = diff (oSplitted, nSplitted);
var str = "";
var acc=[]; // accumulator for prettier output
// crossing pairings -- eg 'A B' vs 'B A' -- cause problems, so let's iron them out
// this doesn't always do things optimally but it should be fast enough
var maxOutputPair=0;
for (i=0; i<out.n.length; ++i) {
if ( out.n[i].paired ) {
if( maxOutputPair > out.n[i].row ) {
// tangle - delete pairing
out.o[ out.n[i].row ]=out.o[ out.n[i].row ].text;
out.n[i]=out.n[i].text;
}
if (maxOutputPair < out.n[i].row) { maxOutputPair = out.n[i].row; }
}
}
// output the stuff preceding the first paired old line
for (i=0; i<out.o.length && !out.o[i].paired; ++i) { acc.push( out.o[i] ); }
str += delFmt(acc); acc=[];
// main loop
for ( i = 0; i < out.n.length; ++i ) {
// output unpaired new "lines"
while ( i < out.n.length && !out.n[i].paired ) { acc.push( out.n[i++] ); }
str += insFmt(acc); acc=[];
if ( i < out.n.length ) { // this new "line" is paired with the (out.n[i].row)th old "line"
str += out.n[i].text;
// output unpaired old rows starting after this new line's partner
var m = out.n[i].row + 1;
while ( m < out.o.length && !out.o[m].paired ) { acc.push ( out.o[m++] ); }
str += delFmt(acc); acc=[];
}
}
return str;
}
// see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Object
// FIXME: use obj.hasOwnProperty instead of this kludge!
var jsReservedProperties=RegExp('^(constructor|prototype|__((define|lookup)[GS]etter)__' +
'|eval|hasOwnProperty|propertyIsEnumerable' +
'|to(Source|String|LocaleString)|(un)?watch|valueOf)$');
function diffBugAlert(word) {
if (!diffBugAlert.list[word]) {
diffBugAlert.list[word]=1;
alert('Bad word: '+word+'\n\nPlease report this bug.');
}
}
diffBugAlert.list={};
function makeDiffHashtable(src) {
var ret={};
for ( var i = 0; i < src.length; i++ ) {
if ( jsReservedProperties.test(src[i]) ) { src[i] += '<!-- -->'; }
if ( !ret[ src[i] ] ) { ret[ src[i] ] = []; }
try { ret[ src[i] ].push( i ); } catch (err) { diffBugAlert(src[i]); }
}
return ret;
}
function diff( o, n ) {
// pass 1: make hashtable ns with new rows as keys
var ns = makeDiffHashtable(n);
// pass 2: make hashtable os with old rows as keys
var os = makeDiffHashtable(o);
// pass 3: pair unique new rows and matching unique old rows
var i;
for ( i in ns ) {
if ( ns[i].length == 1 && os[i] && os[i].length == 1 ) {
n[ ns[i][0] ] = { text: n[ ns[i][0] ], row: os[i][0], paired: true };
o[ os[i][0] ] = { text: o[ os[i][0] ], row: ns[i][0], paired: true };
}
}
// pass 4: pair matching rows immediately following paired rows (not necessarily unique)
for ( i = 0; i < n.length - 1; i++ ) {
if ( n[i].paired && ! n[i+1].paired && n[i].row + 1 < o.length && ! o[ n[i].row + 1 ].paired &&
n[i+1] == o[ n[i].row + 1 ] ) {
n[i+1] = { text: n[i+1], row: n[i].row + 1, paired: true };
o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1, paired: true };
}
}
// pass 5: pair matching rows immediately preceding paired rows (not necessarily unique)
for ( i = n.length - 1; i > 0; i-- ) {
if ( n[i].paired && ! n[i-1].paired && n[i].row > 0 && ! o[ n[i].row - 1 ].paired &&
n[i-1] == o[ n[i].row - 1 ] ) {
n[i-1] = { text: n[i-1], row: n[i].row - 1, paired: true };
o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1, paired: true };
}
}
return { o: o, n: n };
}
//</NOLITE>
// ENDFILE: diff.js
// STARTFILE: init.js
function setSiteInfo() {
if (window.popupLocalDebug) {
pg.wiki.hostname = 'en.wikipedia.org';
} else {
pg.wiki.hostname = location.hostname; // use in preference to location.hostname for flexibility (?)
}
pg.wiki.wikimedia=RegExp('(wiki([pm]edia|source|books|news|quote|versity)|wiktionary|mediawiki)[.]org').test(pg.wiki.hostname);
pg.wiki.wikia=RegExp('[.]wikia[.]com$', 'i').test(pg.wiki.hostname);
pg.wiki.isLocal=RegExp('^localhost').test(pg.wiki.hostname);
pg.wiki.commons=( pg.wiki.wikimedia && pg.wiki.hostname != 'commons.wikimedia.org') ? 'commons.wikimedia.org' : null;
pg.wiki.lang = mw.config.get('wgContentLanguage');
var port = location.port ? ':' + location.port : '';
pg.wiki.sitebase = pg.wiki.hostname + port;
}
function setUserInfo() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
list: 'users',
ususers: mw.config.get('wgUserName'),
usprop: 'rights'
};
pg.user.canReview = false;
if (getValueOf('popupReview')) {
api.get(params).done(function(data){
var rights = data.query.users[0].rights;
pg.user.canReview = rights.indexOf('review') !== -1; // TODO: Should it be a getValueOf('ReviewRight') ?
});
}
}
function setTitleBase() {
var protocol = ( window.popupLocalDebug ? 'http:' : location.protocol );
pg.wiki.articlePath = mw.config.get('wgArticlePath').replace(/\/\$1/, ""); // as in http://some.thing.com/wiki/Article
pg.wiki.botInterfacePath = mw.config.get('wgScript');
pg.wiki.APIPath = mw.config.get('wgScriptPath') +"/api.php";
// default mediawiki setting is paths like http://some.thing.com/articlePath/index.php?title=foo
var titletail = pg.wiki.botInterfacePath + '?title=';
//var titletail2 = joinPath([pg.wiki.botInterfacePath, 'wiki.phtml?title=']);
// other sites may need to add code here to set titletail depending on how their urls work
pg.wiki.titlebase = protocol + '//' + pg.wiki.sitebase + titletail;
//pg.wiki.titlebase2 = protocol + '//' + joinPath([pg.wiki.sitebase, titletail2]);
pg.wiki.wikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.botInterfacePath;
pg.wiki.apiwikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.APIPath;
pg.wiki.articlebase = protocol + '//' + pg.wiki.sitebase + pg.wiki.articlePath;
pg.wiki.commonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.botInterfacePath;
pg.wiki.apicommonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.APIPath;
pg.re.basenames = RegExp( '^(' +
map( literalizeRegex, [ pg.wiki.titlebase, //pg.wiki.titlebase2,
pg.wiki.articlebase ]).join('|') + ')' );
}
//////////////////////////////////////////////////
// Global regexps
function setMainRegex() {
var reStart='[^:]*://';
var preTitles = literalizeRegex( mw.config.get('wgScriptPath') ) + '/(?:index[.]php|wiki[.]phtml)[?]title=';
preTitles += '|' + literalizeRegex( pg.wiki.articlePath + '/' );
var reEnd='(' + preTitles + ')([^&?#]*)[^#]*(?:#(.+))?';
pg.re.main = RegExp(reStart + literalizeRegex(pg.wiki.sitebase) + reEnd);
}
function setRegexps() {
// TODO: We shoud use an api call to get the aliases for special pages, now it does not work for non-English wikipedias:
// E.g., https://ru.wikipedia.org/w/api.php?action=query&meta=siteinfo&siprop=specialpagealiases&formatversion=2
setMainRegex();
var sp=nsRe(pg.nsSpecialId);
pg.re.urlNoPopup=RegExp('((title=|/)' + sp + '(?:%3A|:)|section=[0-9]|^#$)') ;
pg.re.contribs =RegExp('(title=|/)' + sp + '(?:%3A|:)Contributions' + '(&target=|/|/' + nsRe(pg.nsUserId)+':)(.*)') ;
pg.re.email =RegExp('(title=|/)' + sp + '(?:%3A|:)EmailUser' + '(&target=|/|/(?:' + nsRe(pg.nsUserId)+':)?)(.*)') ;
pg.re.backlinks =RegExp('(title=|/)' + sp + '(?:%3A|:)WhatLinksHere' + '(&target=|/)([^&]*)');
pg.re.specialdiff=RegExp('/' + sp + '(?:%3A|:)Diff/([^?#]*)');
//<NOLITE>
var im=nsReImage();
// note: tries to get images in infobox templates too, e.g. movie pages, album pages etc
// (^|\[\[)image: *([^|\]]*[^|\] ]) *
// (^|\[\[)image: *([^|\]]*[^|\] ])([^0-9\]]*([0-9]+) *px)?
// $4 = 120 as in 120px
pg.re.image = RegExp('(^|\\[\\[)' + im + ': *([^|\\]]*[^|\\] ])' +
'([^0-9\\]]*([0-9]+) *px)?|(?:\\n *[|]?|[|]) *' +
'(' + getValueOf('popupImageVarsRegexp') + ')' +
' *= *(?:\\[\\[ *)?(?:' + im + ':)?' +
'([^|]*?)(?:\\]\\])? *[|]? *\\n', 'img') ;
pg.re.imageBracketCount = 6;
pg.re.category = RegExp('\\[\\[' +nsRe(pg.nsCategoryId) +
': *([^|\\]]*[^|\\] ]) *', 'i');
pg.re.categoryBracketCount = 1;
pg.re.ipUser=RegExp('^' +
// IPv6
'(?::(?::|(?::[0-9A-Fa-f]{1,4}){1,7})|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,6}::|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){7})' +
// IPv4
'|(((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}' +
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9]))$');
pg.re.stub= RegExp(getValueOf('popupStubRegexp'), 'im');
pg.re.disambig=RegExp(getValueOf('popupDabRegexp'), 'im');
//</NOLITE>
// FIXME replace with general parameter parsing function, this is daft
pg.re.oldid=RegExp('[?&]oldid=([^&]*)');
pg.re.diff=RegExp('[?&]diff=([^&]*)');
}
//////////////////////////////////////////////////
// miscellany
function setupCache() {
// page caching
pg.cache.pages = [];
}
function setMisc() {
pg.current.link=null;
pg.current.links=[];
pg.current.linksHash={};
setupCache();
pg.timer.checkPopupPosition=null;
pg.counter.loop=0;
// ids change with each popup: popupImage0, popupImage1 etc
pg.idNumber=0;
// for myDecodeURI
pg.misc.decodeExtras = [
{from: '%2C', to: ',' },
{from: '_', to: ' ' },
{from: '%24', to: '$'},
{from: '%26', to: '&' } // no ,
];
pg.misc.userAgent = 'Navigation popups/1.0 (' + mw.config.get( 'wgServerName' ) +')';
}
// We need a callback since this might end up asynchronous because of
// the mw.loader.using() call.
function setupPopups( callback ) {
if ( setupPopups.completed ) {
if ( typeof callback === 'function' ) {
callback();
}
return;
}
// These dependencies are also enforced from the gadget,
// but not everyone loads this as a gadget, so double check
mw.loader.using( ['mediawiki.util', 'mediawiki.user', 'user.options'] ).then( function() {
// NB translatable strings should be set up first (strings.js)
// basics
setupDebugging();
setSiteInfo();
setTitleBase();
setOptions(); // see options.js
setUserInfo();
// namespaces etc
setNamespaces();
setInterwiki();
// regexps
setRegexps();
setRedirs();
// other stuff
setMisc();
setupLivePreview();
// main deal here
setupTooltips();
log('In setupPopups(), just called setupTooltips()');
Navpopup.tracker.enable();
setupPopups.completed = true;
if ( typeof callback === 'function' ) {
callback();
}
});
}
// ENDFILE: init.js
// STARTFILE: navlinks.js
//<NOLITE>
//////////////////////////////////////////////////
// navlinks... let the fun begin
//
function defaultNavlinkSpec() {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
if (getValueOf('popupLastEditLink')) {
str += '*<<lastEdit|shortcut=/>>|<<lastContrib>>|<<sinceMe>>if(oldid){|<<oldEdit>>|<<diffCur>>}';
}
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>*<<userlog|shortcut=L|log>>';
str+='if(ipuser){*<<arin>>}if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>|<<blocklog|log>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' + editstr + '}';
var historystr='<<history|shortcut=h>>|<<editors|shortcut=E|>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str+='<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>*<<move|shortcut=m>>';
// admin links
str += 'if(admin){<br><<unprotect|unprotectShort>>|<<protect|shortcut=p>>|<<protectlog|log>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>|<<deletelog|log>>}';
return str;
}
function navLinksHTML (article, hint, params) { //oldid, rcid) {
var str = '<span class="popupNavLinks">' + defaultNavlinkSpec() + '</span>';
// BAM
return navlinkStringToHTML(str, article, params);
}
function expandConditionalNavlinkString(s,article,z,recursionCount) {
var oldid=z.oldid, rcid=z.rcid, diff=z.diff;
// nested conditionals (up to 10 deep) are ok, hopefully! (work from the inside out)
if (typeof recursionCount!=typeof 0) { recursionCount=0; }
var conditionalSplitRegex=RegExp(
//(1 if \\( (2 2) \\) {(3 3)} (4 else {(5 5)} 4)1)
'(;?\\s*if\\s*\\(\\s*([\\w]*)\\s*\\)\\s*\\{([^{}]*)\\}(\\s*else\\s*\\{([^{}]*?)\\}|))', 'i');
var splitted=s.parenSplit(conditionalSplitRegex);
// $1: whole conditional
// $2: test condition
// $3: true expansion
// $4: else clause (possibly empty)
// $5: false expansion (possibly null)
var numParens=5;
var ret = splitted[0];
for (var i=1; i<splitted.length; i=i+numParens+1) {
var testString=splitted[i+2-1];
var trueString=splitted[i+3-1];
var falseString=splitted[i+5-1];
if (typeof falseString=='undefined' || !falseString) { falseString=''; }
var testResult=null;
switch (testString) {
case 'user':
testResult=(article.userName())?true:false;
break;
case 'talk':
testResult=(article.talkPage())?false:true; // talkPage converts _articles_ to talkPages
break;
case 'admin':
testResult=getValueOf('popupAdminLinks')?true:false;
break;
case 'oldid':
testResult=(typeof oldid != 'undefined' && oldid)?true:false;
break;
case 'rcid':
testResult=(typeof rcid != 'undefined' && rcid)?true:false;
break;
case 'ipuser':
testResult=(article.isIpUser())?true:false;
break;
case 'mainspace_en':
testResult=isInMainNamespace(article) &&
pg.wiki.hostname=='en.wikipedia.org';
break;
case 'wikimedia':
testResult=(pg.wiki.wikimedia) ? true : false;
break;
case 'diff':
testResult=(typeof diff != 'undefined' && diff)?true:false;
break;
}
switch(testResult) {
case null: ret+=splitted[i]; break;
case true: ret+=trueString; break;
case false: ret+=falseString; break;
}
// append non-conditional string
ret += splitted[i+numParens];
}
if (conditionalSplitRegex.test(ret) && recursionCount < 10) {
return expandConditionalNavlinkString(ret,article,z,recursionCount+1);
}
return ret;
}
function navlinkStringToArray(s, article, params) {
s=expandConditionalNavlinkString(s,article,params);
var splitted=s.parenSplit(RegExp('<<(.*?)>>'));
var ret=[];
for (var i=0; i<splitted.length; ++i) {
if (i%2) { // i odd, so s is a tag
var t=new navlinkTag();
var ss=splitted[i].split('|');
t.id=ss[0];
for (var j=1; j<ss.length; ++j) {
var sss=ss[j].split('=');
if (sss.length>1) {
t[sss[0]]=sss[1];
}
else { // no assignment (no "="), so treat this as a title (overwriting the last one)
t.text=popupString(sss[0]);
}
}
t.article=article;
var oldid=params.oldid, rcid=params.rcid, diff=params.diff;
if (typeof oldid !== 'undefined' && oldid !== null) { t.oldid=oldid; }
if (typeof rcid !== 'undefined' && rcid !== null) { t.rcid=rcid; }
if (typeof diff !== 'undefined' && diff !== null) { t.diff=diff; }
if (!t.text && t.id !== 'mainlink') { t.text=popupString(t.id); }
ret.push(t);
}
else { // plain HTML
ret.push(splitted[i]);
}
}
return ret;
}
function navlinkSubstituteHTML(s) {
return s.split('*').join(getValueOf('popupNavLinkSeparator'))
.split('<menurow>').join('<li class="popup_menu_row">')
.split('</menurow>').join('</li>')
.split('<menu>').join('<ul class="popup_menu">')
.split('</menu>').join('</ul>');
}
function navlinkDepth(magic,s) {
return s.split('<' + magic + '>').length - s.split('</' + magic + '>').length;
}
// navlinkString: * becomes the separator
// <<foo|bar=baz|fubar>> becomes a foo-link with attribute bar='baz'
// and visible text 'fubar'
// if(test){...} and if(test){...}else{...} work too (nested ok)
function navlinkStringToHTML(s,article,params) {
//limitAlert(navlinkStringToHTML, 5, 'navlinkStringToHTML\n' + article + '\n' + (typeof article));
var p=navlinkStringToArray(s,article,params);
var html='';
var menudepth = 0; // nested menus not currently allowed, but doesn't do any harm to code for it
var menurowdepth = 0;
for (var i=0; i<p.length; ++i) {
if (typeof p[i] == typeof '') {
html+=navlinkSubstituteHTML(p[i]);
menudepth += navlinkDepth('menu', p[i]);
menurowdepth += navlinkDepth('menurow', p[i]);
// if (menudepth === 0) {
// tagType='span';
// } else if (menurowdepth === 0) {
// tagType='li';
// } else {
// tagType = null;
// }
} else if (typeof p[i].type != 'undefined' && p[i].type=='navlinkTag') {
if (menudepth > 0 && menurowdepth === 0) {
html += '<li class="popup_menu_item">' + p[i].html() + '</li>';
} else {
html+=p[i].html();
}
}
}
return html;
}
function navlinkTag() {
this.type='navlinkTag';
}
navlinkTag.prototype.html=function () {
this.getNewWin();
this.getPrintFunction();
var html='';
var opening, closing;
var tagType='span';
if (!tagType) {
opening = ''; closing = '';
} else {
opening = '<' + tagType + ' class="popup_' + this.id + '">';
closing = '</' + tagType + '>';
}
if (typeof this.print!='function') {
errlog ('Oh dear - invalid print function for a navlinkTag, id='+this.id);
} else {
html=this.print(this);
if (typeof html != typeof '') {html='';}
else if (typeof this.shortcut!='undefined') html=addPopupShortcut(html, this.shortcut);
}
return opening + html + closing;
};
navlinkTag.prototype.getNewWin=function() {
getValueOf('popupLinksNewWindow');
if (typeof pg.option.popupLinksNewWindow[this.id] === 'undefined') { this.newWin=null; }
this.newWin=pg.option.popupLinksNewWindow[this.id];
};
navlinkTag.prototype.getPrintFunction=function() { //think about this some more
// this.id and this.article should already be defined
if (typeof this.id!=typeof '' || typeof this.article!=typeof {} ) { return; }
this.noPopup=1;
switch (this.id) {
case 'contribs': case 'history': case 'whatLinksHere':
case 'userPage': case 'monobook': case 'userTalk':
case 'talk': case 'article': case 'lastEdit':
this.noPopup=null;
}
switch (this.id) {
case 'email': case 'contribs': case 'block': case 'unblock':
case 'userlog': case 'userSpace': case 'deletedContribs':
this.article=this.article.userName();
}
switch (this.id) {
case 'userTalk': case 'newUserTalk': case 'editUserTalk':
case 'userPage': case 'monobook': case 'editMonobook': case 'blocklog':
this.article=this.article.userName(true);
/* fall through */
case 'pagelog': case 'deletelog': case 'protectlog':
delete this.oldid;
}
if (this.id=='editMonobook' || this.id=='monobook') { this.article.append('/monobook.js'); }
if (this.id != 'mainlink') {
// FIXME anchor handling should be done differently with Title object
this.article=this.article.removeAnchor();
// if (typeof this.text=='undefined') this.text=popupString(this.id);
}
switch (this.id) {
case 'undelete': this.print=specialLink; this.specialpage='Undelete'; this.sep='/'; break;
case 'whatLinksHere': this.print=specialLink; this.specialpage='Whatlinkshere'; break;
case 'relatedChanges': this.print=specialLink; this.specialpage='Recentchangeslinked'; break;
case 'move': this.print=specialLink; this.specialpage='Movepage'; break;
case 'contribs': this.print=specialLink; this.specialpage='Contributions'; break;
case 'deletedContribs':this.print=specialLink; this.specialpage='Deletedcontributions'; break;
case 'email': this.print=specialLink; this.specialpage='EmailUser'; this.sep='/'; break;
case 'block': this.print=specialLink; this.specialpage='Blockip'; this.sep='&ip='; break;
case 'unblock': this.print=specialLink; this.specialpage='Ipblocklist'; this.sep='&action=unblock&ip='; break;
case 'userlog': this.print=specialLink; this.specialpage='Log'; this.sep='&user='; break;
case 'blocklog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=block&page='; break;
case 'pagelog': this.print=specialLink; this.specialpage='Log'; this.sep='&page='; break;
case 'protectlog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=protect&page='; break;
case 'deletelog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=delete&page='; break;
case 'userSpace': this.print=specialLink; this.specialpage='PrefixIndex'; this.sep='&namespace=2&prefix='; break;
case 'search': this.print=specialLink; this.specialpage='Search'; this.sep='&fulltext=Search&search='; break;
case 'thank': this.print=specialLink; this.specialpage='Thanks'; this.sep='/'; this.article.value = this.diff; break;
case 'unwatch': case 'watch':
this.print=magicWatchLink; this.action=this.id+'&autowatchlist=1&autoimpl=' + popupString('autoedit_version') + '&actoken='+autoClickToken(); break;
case 'history': case 'historyfeed':
case 'unprotect': case 'protect':
this.print=wikiLink; this.action=this.id; break;
case 'delete':
this.print=wikiLink; this.action='delete';
if (this.article.namespaceId()==pg.nsImageId) {
var img=this.article.stripNamespace();
this.action+='&image='+img;
}
break;
case 'markpatrolled':
case 'edit': // editOld should keep the oldid, but edit should not.
delete this.oldid;
/* fall through */
case 'view': case 'purge': case 'render':
this.print=wikiLink;
this.action=this.id; break;
case 'raw':
this.print=wikiLink; this.action='raw'; break;
case 'new':
this.print=wikiLink; this.action='edit§ion=new'; break;
case 'mainlink':
if (typeof this.text=='undefined') { this.text=this.article.toString().entify(); }
if (getValueOf('popupSimplifyMainLink') && isInStrippableNamespace(this.article)) {
// only show the /subpage part of the title text
var s=this.text.split('/'); this.text=s[s.length-1];
if (this.text==='' && s.length > 1) { this.text=s[s.length-2]; }
}
this.print=titledWikiLink;
if (typeof this.title==='undefined' && pg.current.link && typeof pg.current.link.href !== 'undefined') {
this.title=safeDecodeURI((pg.current.link.originalTitle)?pg.current.link.originalTitle:this.article);
if (typeof this.oldid !== 'undefined' && this.oldid) {
this.title=tprintf('Revision %s of %s', [this.oldid, this.title]);
}
}
this.action='view'; break;
case 'userPage':
case 'article':
case 'monobook':
case 'editMonobook':
case 'editArticle':
delete this.oldid;
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.article=this.article.articleFromTalkOrArticle();
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.print=wikiLink;
if (this.id.indexOf('edit')===0) {
this.action='edit';
} else { this.action='view';}
break;
case 'userTalk':
case 'talk':
this.article=this.article.talkPage();
delete this.oldid;
this.print=wikiLink;
this.action='view'; break;
case 'arin':
this.print=arinLink; break;
case 'count':
this.print=editCounterLink; break;
case 'google':
this.print=googleLink; break;
case 'editors':
this.print=editorListLink; break;
case 'globalsearch':
this.print=globalSearchLink; break;
case 'lastEdit':
this.print=titledDiffLink;
this.title=popupString('Show the last edit');
this.from='prev'; this.to='cur'; break;
case 'oldEdit':
this.print=titledDiffLink;
this.title=popupString('Show the edit made to get revision') + ' ' + this.oldid;
this.from='prev'; this.to=this.oldid; break;
case 'editOld':
this.print=wikiLink; this.action='edit'; break;
case 'undo':
this.print=wikiLink; this.action='edit&undo='; break;
case 'revert':
this.print=wikiLink; this.action='revert'; break;
case 'nullEdit':
this.print=wikiLink; this.action='nullEdit'; break;
case 'diffCur':
this.print=titledDiffLink;
this.title=tprintf('Show changes since revision %s', [this.oldid]);
this.from=this.oldid; this.to='cur'; break;
case 'editUserTalk':
case 'editTalk':
delete this.oldid;
this.article=this.article.talkPage();
this.action='edit'; this.print=wikiLink; break;
case 'newUserTalk':
case 'newTalk':
this.article=this.article.talkPage();
this.action='edit§ion=new'; this.print=wikiLink; break;
case 'lastContrib':
case 'sinceMe':
this.print=magicHistoryLink;
break;
case 'togglePreviews':
this.text=popupString(pg.option.simplePopups ? 'enable previews' : 'disable previews');
/* fall through */
case 'disablePopups': case 'purgePopups':
this.print=popupMenuLink;
break;
default:
this.print=function () {return 'Unknown navlink type: '+this.id+'';};
}
};
//
// end navlinks
//////////////////////////////////////////////////
//</NOLITE>
// ENDFILE: navlinks.js
// STARTFILE: shortcutkeys.js
//<NOLITE>
function popupHandleKeypress(evt) {
var keyCode = window.event ? window.event.keyCode : ( evt.keyCode ? evt.keyCode : evt.which);
if (!keyCode || !pg.current.link || !pg.current.link.navpopup) { return; }
if (keyCode==27) { // escape
killPopup();
return false; // swallow keypress
}
var letter=String.fromCharCode(keyCode);
var links=pg.current.link.navpopup.mainDiv.getElementsByTagName('A');
var startLink=0;
var i,j;
if (popupHandleKeypress.lastPopupLinkSelected) {
for (i=0; i<links.length; ++i) {
if (links[i]==popupHandleKeypress.lastPopupLinkSelected) { startLink=i; }
}
}
for (j=0; j<links.length; ++j) {
i=(startLink + j + 1) % links.length;
if (links[i].getAttribute('popupkey')==letter) {
if (evt && evt.preventDefault) evt.preventDefault();
links[i].focus();
popupHandleKeypress.lastPopupLinkSelected=links[i];
return false; // swallow keypress
}
}
// pass keypress on
if (document.oldPopupOnkeypress) { return document.oldPopupOnkeypress(evt); }
return true;
}
function addPopupShortcuts() {
if (document.onkeypress!=popupHandleKeypress) {
document.oldPopupOnkeypress=document.onkeypress;
}
document.onkeypress=popupHandleKeypress;
}
function rmPopupShortcuts() {
popupHandleKeypress.lastPopupLinkSelected=null;
try {
if (document.oldPopupOnkeypress && document.oldPopupOnkeypress==popupHandleKeypress) {
// panic
document.onkeypress=null; //function () {};
return;
}
document.onkeypress=document.oldPopupOnkeypress;
} catch (nasties) { /* IE goes here */ }
}
function addLinkProperty(html, property) {
// take "<a href=...>...</a> and add a property
// not sophisticated at all, easily broken
var i=html.indexOf('>');
if (i<0) { return html; }
return html.substring(0,i) + ' ' + property + html.substring(i);
}
function addPopupShortcut(html, key) {
if (!getValueOf('popupShortcutKeys')) { return html; }
var ret= addLinkProperty(html, 'popupkey="'+key+'"');
if (key==' ') { key=popupString('spacebar'); }
return ret.replace(RegExp('^(.*?)(title=")(.*?)(".*)$', 'i'),'$1$2$3 ['+key+']$4');
}
//</NOLITE>
// ENDFILE: shortcutkeys.js
// STARTFILE: diffpreview.js
//<NOLITE>
//lets jump through hoops to find the rev ids we need to retrieve
function loadDiff(article, oldid, diff, navpop) {
navpop.diffData={ oldRev: {}, newRev: {} };
mw.loader.using( 'mediawiki.api' ).then( function() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'compare',
prop: 'ids|title'
};
if(article.title){
params.fromtitle = article.title;
}
switch (diff) {
case 'cur':
switch ( oldid ) {
case null:
case '':
case 'prev':
// this can only work if we have the title
// cur -> prev
params.torelative = 'prev';
break;
default:
params.fromrev = oldid;
params.torelative = 'cur';
break;
}
break;
case 'prev':
if( oldid ) {
params.fromrev = oldid;
} else {
params.fromtitle;
}
params.torelative = 'prev';
break;
case 'next':
params.fromrev = oldid || 0;
params.torelative = 'next';
break;
default:
params.fromrev = oldid || 0;
params.torev = diff || 0;
break;
}
api.get( params ).then( function( data ) {
navpop.diffData.oldRev.revid = data.compare.fromrevid;
navpop.diffData.newRev.revid = data.compare.torevid;
addReviewLink(navpop, 'popupMiscTools');
var go = function() {
pendingNavpopTask(navpop);
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
url += 'revids=' + navpop.diffData.oldRev.revid + '|' + navpop.diffData.newRev.revid;
url += '&prop=revisions&rvprop=ids|timestamp|content';
getPageWithCaching(url, doneDiff, navpop);
return true; // remove hook once run
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_DIFFS'); }
} );
} );
}
// Put a "mark patrolled" link to an element target
// TODO: Allow patrol a revision, as well as a diff
function addReviewLink (navpop, target) {
if (! pg.user.canReview) return;
// If 'newRev' is older than 'oldRev' than it could be confusing, so we do not show the review link.
if (navpop.diffData.newRev.revid <= navpop.diffData.oldRev.revid) return;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
prop: 'info|flagged',
revids: navpop.diffData.oldRev.revid,
formatversion : 2
};
api.get (params).then(function(data){
var stable_revid = data.query.pages[0].flagged && data.query.pages[0].flagged.stable_revid || 0;
// The diff can be reviewed if the old version is the last reviewed version
// TODO: Other possible conditions that we may want to implement instead of this one:
// * old version is patrolled and the new version is not patrolled
// * old version is patrolled and the new version is more recent than the last reviewed version
if (stable_revid == navpop.diffData.oldRev.revid) {
var a = document.createElement('a');
a.innerHTML = popupString('mark patrolled');
a.title=popupString('markpatrolledHint');
a.onclick = function() {
var params = {
action: 'review',
revid: navpop.diffData.newRev.revid,
comment: tprintf('defaultpopupReviewedSummary', [navpop.diffData.oldRev.revid, navpop.diffData.newRev.revid])
};
api.postWithToken('csrf',params).done(function(){
a.style.display = "none";
// TODO: Update current page and other already constructed popups
} ).fail(function(){
alert(popupString('Could not marked this edit as patrolled'));
});
};
setPopupHTML(a, target, navpop.idNumber,null,true);
}
});
}
function doneDiff(download) {
if (!download.owner || !download.owner.diffData) { return; }
var navpop=download.owner;
completedNavpopTask(navpop);
var pages, revisions=[];
try{
// Process the downloads
pages = getJsObj(download.data).query.pages;
for(var i=0; i < pages.length; i++ ) {
revisions = revisions.concat(pages[i].revisions);
}
for(i=0; i< revisions.length; i++){
if(revisions[i].revid == navpop.diffData.oldRev.revid) {
navpop.diffData.oldRev.revision = revisions[i];
} else if (revisions[i].revid == navpop.diffData.newRev.revid) {
navpop.diffData.newRev.revision = revisions[i];
}
}
} catch(someError) {
errlog( 'Could not get diff' );
}
insertDiff(navpop);
}
function rmBoringLines(a,b,context) {
if (typeof context == 'undefined') { context=2; }
// this is fairly slow... i think it's quicker than doing a word-based diff from the off, though
var aa=[], aaa=[];
var bb=[], bbb=[];
var i, j;
// first, gather all disconnected nodes in a and all crossing nodes in a and b
for (i=0; i<a.length; ++i ) {
if(!a[i].paired) { aa[i]=1; }
else if (countCrossings(b,a,i, true)) {
aa[i]=1;
bb[ a[i].row ] = 1;
}
}
// pick up remaining disconnected nodes in b
for (i=0; i<b.length; ++i ) {
if (bb[i]==1) { continue; }
if(!b[i].paired) { bb[i]=1; }
}
// another pass to gather context: we want the neighbours of included nodes which are not yet included
// we have to add in partners of these nodes, but we don't want to add context for *those* nodes in the next pass
for (i=0; i<b.length; ++i) {
if ( bb[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(b.length, i+context); ++j) {
if ( !bb[j] ) { bb[j] = 1; aa[ b[j].row ] = 0.5; }
}
}
}
for (i=0; i<a.length; ++i) {
if ( aa[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(a.length, i+context); ++j) {
if ( !aa[j] ) { aa[j] = 1; bb[ a[j].row ] = 0.5; }
}
}
}
for (i=0; i<bb.length; ++i) {
if (bb[i] > 0) { // it's a row we need
if (b[i].paired) { bbb.push(b[i].text); } // joined; partner should be in aa
else {
bbb.push(b[i]);
}
}
}
for (i=0; i<aa.length; ++i) {
if (aa[i] > 0) { // it's a row we need
if (a[i].paired) { aaa.push(a[i].text); } // joined; partner should be in aa
else {
aaa.push(a[i]);
}
}
}
return { a: aaa, b: bbb};
}
function stripOuterCommonLines(a,b,context) {
var i=0;
while (i<a.length && i < b.length && a[i]==b[i]) { ++i; }
var j=a.length-1; var k=b.length-1;
while ( j>=0 && k>=0 && a[j]==b[k] ) { --j; --k; }
return { a: a.slice(Math.max(0,i - 1 - context), Math.min(a.length+1, j + context+1)),
b: b.slice(Math.max(0,i - 1 - context), Math.min(b.length+1, k + context+1)) };
}
function insertDiff(navpop) {
// for speed reasons, we first do a line-based diff, discard stuff that seems boring, then do a word-based diff
// FIXME: sometimes this gives misleading diffs as distant chunks are squashed together
var oldlines = navpop.diffData.oldRev.revision.content.split('\n');
var newlines = navpop.diffData.newRev.revision.content.split('\n');
var inner=stripOuterCommonLines(oldlines,newlines,getValueOf('popupDiffContextLines'));
oldlines=inner.a; newlines=inner.b;
var truncated=false;
getValueOf('popupDiffMaxLines');
if (oldlines.length > pg.option.popupDiffMaxLines || newlines.length > pg.option.popupDiffMaxLines) {
// truncate
truncated=true;
inner=stripOuterCommonLines(oldlines.slice(0,pg.option.popupDiffMaxLines),
newlines.slice(0,pg.option.popupDiffMaxLines),
pg.option.popupDiffContextLines);
oldlines=inner.a; newlines=inner.b;
}
var lineDiff=diff(oldlines, newlines);
var lines2=rmBoringLines(lineDiff.o, lineDiff.n);
var oldlines2=lines2.a; var newlines2=lines2.b;
var simpleSplit = !String.prototype.parenSplit.isNative;
var html='<hr />';
if (getValueOf('popupDiffDates')) {
html += diffDatesTable(navpop);
html += '<hr />';
}
html += shortenDiffString(
diffString(oldlines2.join('\n'), newlines2.join('\n'), simpleSplit),
getValueOf('popupDiffContextCharacters') ).join('<hr />');
setPopupTipsAndHTML(html.split('\n').join('<br>') +
(truncated ? '<hr /><b>'+popupString('Diff truncated for performance reasons')+'</b>' : '') ,
'popupPreview', navpop.idNumber);
}
function diffDatesTable( navpop ) {
var html='<table class="popup_diff_dates">';
html += diffDatesTableRow( navpop.diffData.newRev.revision, tprintf('New revision'));
html += diffDatesTableRow( navpop.diffData.oldRev.revision, tprintf('Old revision'));
html += '</table>';
return html;
}
function diffDatesTableRow( revision, label ) {
var txt='';
var lastModifiedDate = new Date(revision.timestamp);
var datePrint=getValueOf('popupDiffDatePrinter');
if (typeof lastModifiedDate[datePrint] == 'function') {
var d2 = adjustDate(lastModifiedDate, getTimeOffset());
txt = dayFormat(d2, true) + ' ' + timeFormat(d2, true);
} else {
txt = tprintf('Invalid %s %s', ['popupDiffDatePrinter', datePrint]);
}
var revlink = generalLink({url: mw.config.get('wgScript') + '?oldid='+revision.revid,
text: label, title: label});
return simplePrintf('<tr><td>%s</td><td>%s</td></tr>', [ revlink, txt ]);
}
//</NOLITE>
// ENDFILE: diffpreview.js
// STARTFILE: links.js
//<NOLITE>
/////////////////////
// LINK GENERATION //
/////////////////////
// titledDiffLink --> titledWikiLink --> generalLink
// wikiLink --> titledWikiLink --> generalLink
// editCounterLink --> generalLink
// TODO Make these functions return Element objects, not just raw HTML strings.
function titledDiffLink(l) { // article, text, title, from, to) {
return titledWikiLink({article: l.article, action: l.to + '&oldid=' + l.from,
newWin: l.newWin,
noPopup: l.noPopup,
text: l.text, title: l.title,
/* hack: no oldid here */
actionName: 'diff'});
}
function wikiLink(l) {
//{article:article, action:action, text:text, oldid, newid}) {
if (! (typeof l.article == typeof {} &&
typeof l.action == typeof '' &&
typeof l.text==typeof '')) return null;
if (typeof l.oldid == 'undefined') { l.oldid=null; }
var savedOldid = l.oldid;
if (!/^(edit|view|revert|render)$|^raw/.test(l.action)) { l.oldid=null; }
var hint=popupString(l.action + 'Hint'); // revertHint etc etc etc
var oldidData=[l.oldid, safeDecodeURI(l.article)];
var revisionString = tprintf('revision %s of %s', oldidData);
log('revisionString='+revisionString);
switch (l.action) {
case 'edit§ion=new': hint = popupString('newSectionHint'); break;
case 'edit&undo=':
if (l.diff && l.diff != 'prev' && savedOldid ) {
l.action += l.diff + '&undoafter=' + savedOldid;
} else if (savedOldid) {
l.action += savedOldid;
}
hint = popupString('undoHint');
break;
case 'raw&ctype=text/css': hint=popupString('rawHint'); break;
case 'revert':
var p=parseParams(pg.current.link.href);
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=' + revertSummary(l.oldid, p.diff);
if (p.diff=='prev') {
l.action += '&direction=prev';
revisionString = tprintf('the revision prior to revision %s of %s', oldidData);
}
if (getValueOf('popupRevertSummaryPrompt')) { l.action += '&autosummaryprompt=true'; }
if (getValueOf('popupMinorReverts')) { l.action += '&autominor=true'; }
log('revisionString is now '+revisionString);
break;
case 'nullEdit':
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=null';
break;
case 'historyfeed':
l.action='history&feed=rss';
break;
case 'markpatrolled':
l.action='markpatrolled&rcid='+l.rcid;
}
if (hint) {
if (l.oldid) {
hint = simplePrintf(hint, [revisionString]);
}
else {
hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
}
}
else {
hint = safeDecodeURI(l.article + '&action=' + l.action) + (l.oldid) ? '&oldid='+l.oldid : '';
}
return titledWikiLink({article: l.article, action: l.action, text: l.text, newWin:l.newWin,
title: hint, oldid: l.oldid, noPopup: l.noPopup, onclick: l.onclick});
}
function revertSummary(oldid, diff) {
var ret='';
if (diff == 'prev') {
ret=getValueOf('popupQueriedRevertToPreviousSummary');
} else { ret = getValueOf('popupQueriedRevertSummary'); }
return ret + '&autorv=' + oldid;
}
function titledWikiLink(l) {
// possible properties of argument:
// article, action, text, title, oldid, actionName, className, noPopup
// oldid = null is fine here
// article and action are mandatory args
if (typeof l.article == 'undefined' || typeof l.action=='undefined') {
errlog('got undefined article or action in titledWikiLink');
return null;
}
var base = pg.wiki.titlebase + l.article.urlString();
var url=base;
if (typeof l.actionName=='undefined' || !l.actionName) { l.actionName='action'; }
// no need to add &action=view, and this confuses anchors
if (l.action != 'view') { url = base + '&' + l.actionName + '=' + l.action; }
if (typeof l.oldid!='undefined' && l.oldid) { url+='&oldid='+l.oldid; }
var cssClass=pg.misc.defaultNavlinkClassname;
if (typeof l.className!='undefined' && l.className) { cssClass=l.className; }
return generalNavLink({url: url, newWin: l.newWin,
title: (typeof l.title != 'undefined') ? l.title : null,
text: (typeof l.text!='undefined')?l.text:null,
className: cssClass, noPopup:l.noPopup, onclick:l.onclick});
}
pg.fn.getLastContrib = function getLastContrib(wikipage, newWin) {
getHistoryInfo(wikipage, function(x) {
processLastContribInfo(x, {page: wikipage, newWin: newWin});
});
};
function processLastContribInfo(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: an odd thing happened. Please retry.'); return; }
if(!info.firstNewEditor) {
alert(tprintf('Only found one editor: %s made %s edits', [info.edits[0].editor,info.edits.length]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+info.firstNewEditor.oldid;
displayUrl(newUrl, stuff.newWin);
}
pg.fn.getDiffSinceMyEdit = function getDiffSinceMyEdit(wikipage, newWin) {
getHistoryInfo(wikipage, function(x){
processDiffSinceMyEdit(x, {page: wikipage, newWin: newWin});
});
};
function processDiffSinceMyEdit(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: something fishy happened. Please try again.'); return; }
var friendlyName=stuff.page.split('_').join(' ');
if(!info.myLastEdit) {
alert(tprintf('Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
[info.userName, getValueOf('popupHistoryLimit'), friendlyName]));
return;
}
if(info.myLastEdit.index === 0) {
alert(tprintf("%s seems to be the last editor to the page %s", [info.userName, friendlyName]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+ info.myLastEdit.oldid;
displayUrl(newUrl, stuff.newWin);
}
function displayUrl(url, newWin){
if(newWin) { window.open(url); }
else { document.location=url; }
}
pg.fn.purgePopups = function purgePopups() {
processAllPopups(true);
setupCache(); // deletes all cached items (not browser cached, though...)
pg.option={};
abortAllDownloads();
};
function processAllPopups(nullify, banish) {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
if (!pg.current.links[i].navpopup) { continue; }
if (nullify || banish) pg.current.links[i].navpopup.banish();
pg.current.links[i].simpleNoMore=false;
if (nullify) pg.current.links[i].navpopup=null;
}
}
pg.fn.disablePopups = function disablePopups(){
processAllPopups(false, true);
setupTooltips(null, true);
};
pg.fn.togglePreviews = function togglePreviews() {
processAllPopups(true, true);
pg.option.simplePopups=!pg.option.simplePopups;
abortAllDownloads();
};
function magicWatchLink(l) {
//Yuck!! Would require a thorough redesign to add this as a click event though ...
l.onclick = simplePrintf( 'pg.fn.modifyWatchlist(\'%s\',\'%s\');return false;', [l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), this.id] );
return wikiLink(l);
}
pg.fn.modifyWatchlist = function modifyWatchlist(title, action) {
var reqData = {
'action': 'watch',
'formatversion': 2,
'titles': title,
'uselang': mw.config.get('wgUserLanguage')
};
if ( action === 'unwatch' ) reqData.unwatch = true;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
// Load the Addedwatchtext or Removedwatchtext message and show it
var mwTitle = mw.Title.newFromText( title );
var messageName;
if ( mwTitle && mwTitle.getNamespaceId() > 0 && mwTitle.getNamespaceId() % 2 === 1 ) {
messageName = action === 'watch' ? 'addedwatchtext-talk' : 'removedwatchtext-talk';
} else {
messageName = action === 'watch' ? 'addedwatchtext' : 'removedwatchtext';
}
$.when(
api.postWithToken( 'watch', reqData ),
mw.loader.using( [ 'mediawiki.api', 'mediawiki.jqueryMsg' ] ).then( function () {
return api.loadMessagesIfMissing( [ messageName ] );
} )
).done( function () {
mw.notify( mw.message( messageName, title ).parseDom() );
} );
};
function magicHistoryLink(l) {
// FIXME use onclick change href trick to sort this out instead of window.open
var jsUrl='', title='', onClick='';
switch(l.id) {
case 'lastContrib':
onClick=simplePrintf('pg.fn.getLastContrib(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('lastContribHint');
break;
case 'sinceMe':
onClick=simplePrintf('pg.fn.getDiffSinceMyEdit(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('sinceMeHint');
break;
}
jsUrl = 'javascript:' + onClick; // jshint ignore:line
onClick += ';return false;';
return generalNavLink({url: jsUrl, newWin: false, // can't have new windows with JS links, I think
title: title, text: l.text, noPopup: l.noPopup, onclick: onClick });
}
function popupMenuLink(l) {
var jsUrl=simplePrintf('javascript:pg.fn.%s()', [l.id]); // jshint ignore:line
var title=popupString(simplePrintf('%sHint', [l.id]));
var onClick=simplePrintf('pg.fn.%s();return false;', [l.id]);
return generalNavLink({url: jsUrl, newWin:false, title:title, text:l.text, noPopup:l.noPopup, onclick: onClick});
}
function specialLink(l) {
// properties: article, specialpage, text, sep
if (typeof l.specialpage=='undefined'||!l.specialpage) return null;
var base = pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId]+':'+l.specialpage;
if (typeof l.sep == 'undefined' || l.sep === null) l.sep='&target=';
var article=l.article.urlString({keepSpaces: l.specialpage=='Search'});
var hint=popupString(l.specialpage+'Hint');
switch (l.specialpage) {
case 'Log':
switch (l.sep) {
case '&user=': hint=popupString('userLogHint'); break;
case '&type=block&page=': hint=popupString('blockLogHint'); break;
case '&page=': hint=popupString('pageLogHint'); break;
case '&type=protect&page=': hint=popupString('protectLogHint'); break;
case '&type=delete&page=': hint=popupString('deleteLogHint'); break;
default: log('Unknown log type, sep=' + l.sep); hint='Missing hint (FIXME)';
}
break;
case 'PrefixIndex': article += '/'; break;
}
if (hint) hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
else hint = safeDecodeURI(l.specialpage+':'+l.article) ;
var url = base + l.sep + article;
return generalNavLink({url: url, title: hint, text: l.text, newWin:l.newWin, noPopup:l.noPopup});
}
function generalLink(l) {
// l.url, l.text, l.title, l.newWin, l.className, l.noPopup, l.onclick
if (typeof l.url=='undefined') return null;
// only quotation marks in the url can screw us up now... I think
var url=l.url.split('"').join('%22');
var ret='<a href="' + url + '"';
if (typeof l.title!='undefined' && l.title) { ret += ' title="' + pg.escapeQuotesHTML(l.title) + '"'; }
if (typeof l.onclick!='undefined' && l.onclick) { ret += ' onclick="' + pg.escapeQuotesHTML(l.onclick) + '"'; }
if (l.noPopup) { ret += ' noPopup=1'; }
var newWin;
if (typeof l.newWin=='undefined' || l.newWin === null) { newWin=getValueOf('popupNewWindows'); }
else { newWin=l.newWin; }
if (newWin) { ret += ' target="_blank"'; }
if (typeof l.className!='undefined'&&l.className) { ret+=' class="'+l.className+'"'; }
ret += '>';
if (typeof l.text==typeof '') { ret+= l.text; }
ret +='</a>';
return ret;
}
function appendParamsToLink(linkstr, params) {
var sp=linkstr.parenSplit(RegExp('(href="[^"]+?)"', 'i'));
if (sp.length<2) return null;
var ret=sp.shift() + sp.shift();
ret += '&' + params + '"';
ret += sp.join('');
return ret;
}
function changeLinkTargetLink(x) { // newTarget, text, hint, summary, clickButton, minor, title (optional), alsoChangeLabel {
if (x.newTarget) {
log ('changeLinkTargetLink: newTarget=' + x.newTarget);
}
if (x.oldTarget !== decodeURIComponent( x.oldTarget ) ) {
log ('This might be an input problem: ' + x.oldTarget );
}
// FIXME: first character of page title as well as namespace should be case insensitive
// eg [[category:X1]] and [[Category:X1]] are equivalent
// this'll break if charAt(0) is nasty
var cA = mw.util.escapeRegExp(x.oldTarget);
var chs = cA.charAt(0).toUpperCase();
chs='['+chs + chs.toLowerCase()+']';
var currentArticleRegexBit=chs+cA.substring(1);
currentArticleRegexBit=currentArticleRegexBit
.split(RegExp('(?:[_ ]+|%20)', 'g')).join('(?:[_ ]+|%20)')
.split('\\(').join('(?:%28|\\()')
.split('\\)').join('(?:%29|\\))'); // why does this need to match encoded strings ? links in the document ?
// leading and trailing space should be ignored, and anchor bits optional:
currentArticleRegexBit = '\\s*(' + currentArticleRegexBit + '(?:#[^\\[\\|]*)?)\\s*';
// e.g. Computer (archaic) -> \s*([Cc]omputer[_ ](?:%2528|\()archaic(?:%2528|\)))\s*
// autoedit=s~\[\[([Cc]ad)\]\]~[[Computer-aided%20design|$1]]~g;s~\[\[([Cc]AD)[|]~[[Computer-aided%20design|~g
var title=x.title || mw.config.get('wgPageName').split('_').join(' ');
var lk=titledWikiLink({article: new Title(title), newWin:x.newWin,
action: 'edit',
text: x.text,
title: x.hint,
className: 'popup_change_title_link'
});
var cmd='';
if (x.newTarget) {
// escape '&' and other nasties
var t = x.newTarget;
var s = mw.util.escapeRegExp(x.newTarget);
if (x.alsoChangeLabel) {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + ']]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
} else {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + '|$1]]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
}
} else {
cmd += 's~\\[\\['+currentArticleRegexBit+'\\]\\]~$1~g;';
cmd += 's~\\[\\['+currentArticleRegexBit+'[|](.*?)\\]\\]~$2~g';
}
// Build query
cmd = 'autoedit=' + encodeURIComponent ( cmd );
cmd += '&autoclick='+ encodeURIComponent( x.clickButton ) + '&actoken=' + encodeURIComponent( autoClickToken() );
cmd += ( x.minor === null ) ? '' : '&autominor='+ encodeURIComponent( x.minor );
cmd += ( x.watch === null ) ? '' : '&autowatch='+ encodeURIComponent( x.watch );
cmd += '&autosummary='+encodeURIComponent(x.summary);
cmd += '&autoimpl='+encodeURIComponent( popupString('autoedit_version') );
return appendParamsToLink(lk, cmd);
}
function redirLink(redirMatch, article) {
// NB redirMatch is in wikiText
var ret='';
if (getValueOf('popupAppendRedirNavLinks') && getValueOf('popupNavLinks')) {
ret += '<hr />';
if (getValueOf('popupFixRedirs') && typeof autoEdit != 'undefined' && autoEdit) {
ret += popupString('Redirects to: (Fix ');
log('redirLink: newTarget=' + redirMatch);
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target'),
hint: popupString('Fix this redirect, changing just the link target'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages')
}), 'R');
ret += popupString(' or ');
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target & label'),
hint: popupString('Fix this redirect, changing the link target and label'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages'),
alsoChangeLabel: true
}), 'R');
ret += popupString(')');
}
else ret += popupString('Redirects') + popupString(' to ');
return ret;
}
else return '<br> ' + popupString('Redirects') + popupString(' to ') +
titledWikiLink({article: new Title().fromWikiText(redirMatch), action: 'view', /* FIXME: newWin */
text: safeDecodeURI(redirMatch), title: popupString('Bypass redirect')});
}
function arinLink(l) {
if (!saneLinkCheck(l)) { return null; }
if ( ! l.article.isIpUser() || ! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
return generalNavLink({url:'http://ws.arin.net/cgi-bin/whois.pl?queryinput=' + encodeURIComponent(uN), newWin:l.newWin,
title: tprintf('Look up %s in ARIN whois database', [uN]),
text: l.text, noPopup:1});
}
function toolDbName(cookieStyle) {
var ret = mw.config.get('wgDBname');
if (!cookieStyle) { ret+= '_p'; }
return ret;
}
function saneLinkCheck(l) {
if (typeof l.article != typeof {} || typeof l.text != typeof '') { return false; }
return true;
}
function editCounterLink(l) {
if(!saneLinkCheck(l)) return null;
if (! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
var tool=getValueOf('popupEditCounterTool');
var url;
var defaultToolUrl='//tools.wmflabs.org/supercount/index.php?user=$1&project=$2.$3';
switch(tool) {
case 'custom':
url=simplePrintf(getValueOf('popupEditCounterUrl'), [ encodeURIComponent(uN), toolDbName() ]);
break;
case 'soxred': // no longer available
case 'kate': // no longer available
case 'interiot':// no longer available
/* fall through */
case 'supercount':
default:
var theWiki=pg.wiki.hostname.split('.');
url=simplePrintf(defaultToolUrl, [ encodeURIComponent(uN), theWiki[0], theWiki[1] ]);
}
return generalNavLink({url:url, title: tprintf('editCounterLinkHint', [uN]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function globalSearchLink(l) {
if(!saneLinkCheck(l)) return null;
var base='http://vs.aka-online.de/cgi-bin/globalwpsearch.pl?timeout=120&search=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + article, newWin:l.newWin,
title: tprintf('globalSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function googleLink(l) {
if(!saneLinkCheck(l)) return null;
var base='https://www.google.com/search?q=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + '%22' + article + '%22', newWin:l.newWin,
title: tprintf('googleSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function editorListLink(l) {
if(!saneLinkCheck(l)) return null;
var article= l.article.articleFromTalkPage() || l.article;
var url='https://xtools.wmflabs.org/articleinfo/' +
encodeURI( pg.wiki.hostname ) + '/' +
article.urlString() +
'?uselang=' + mw.config.get('wgUserLanguage');
return generalNavLink({url:url,
title: tprintf('editorListHint', [article]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function generalNavLink(l) {
l.className = (l.className === null) ? 'popupNavLink' : l.className;
return generalLink(l);
}
//////////////////////////////////////////////////
// magic history links
//
function getHistoryInfo(wikipage, whatNext) {
log('getHistoryInfo');
getHistory(wikipage, whatNext ? function(d){whatNext(processHistory(d));} : processHistory);
}
// FIXME eliminate pg.idNumber ... how? :-(
function getHistory(wikipage, onComplete) {
log('getHistory');
var url = pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&prop=revisions&titles=' +
new Title(wikipage).urlString() + '&rvlimit=' + getValueOf('popupHistoryLimit');
log('getHistory: url='+url);
return startDownload(url, pg.idNumber+'history', onComplete);
}
function processHistory(download) {
var jsobj = getJsObj(download.data);
try {
var revisions = anyChild(jsobj.query.pages).revisions;
var edits=[];
for (var i=0; i<revisions.length; ++i) {
edits.push({ oldid: revisions[i].revid, editor: revisions[i].user });
}
log('processed ' + edits.length + ' edits');
return finishProcessHistory( edits, mw.config.get('wgUserName') );
} catch (someError) {
log('Something went wrong with JSON business');
return finishProcessHistory([]);
}
}
function finishProcessHistory(edits, userName) {
var histInfo={};
histInfo.edits=edits;
histInfo.userName=userName;
for (var i=0; i<edits.length; ++i) {
if (typeof histInfo.myLastEdit === 'undefined' && userName && edits[i].editor==userName) {
histInfo.myLastEdit={index: i, oldid: edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
if (typeof histInfo.firstNewEditor === 'undefined' && edits[i].editor != edits[0].editor) {
histInfo.firstNewEditor={index:i, oldid:edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
}
//pg.misc.historyInfo=histInfo;
return histInfo;
}
//</NOLITE>
// ENDFILE: links.js
// STARTFILE: options.js
//////////////////////////////////////////////////
// options
// check for existing value, else use default
function defaultize(x) {
if (pg.option[x]===null || typeof pg.option[x]=='undefined') {
if (typeof window[x] != 'undefined' ) pg.option[x]=window[x];
else pg.option[x]=pg.optionDefault[x];
}
}
function newOption(x, def) {
pg.optionDefault[x]=def;
}
function setDefault(x, def) {
return newOption(x, def);
}
function getValueOf(varName) {
defaultize(varName);
return pg.option[varName];
}
/*eslint-disable */
function useDefaultOptions() { // for testing
for (var p in pg.optionDefault) {
pg.option[p]=pg.optionDefault[p];
if (typeof window[p]!='undefined') { delete window[p]; }
}
}
/*eslint-enable */
function setOptions() {
// user-settable parameters and defaults
var userIsSysop = false;
if ( mw.config.get('wgUserGroups') ) {
for ( var g = 0; g < mw.config.get('wgUserGroups').length; ++g ) {
if ( mw.config.get('wgUserGroups')[g] == "sysop" )
userIsSysop = true;
}
}
// Basic options
newOption('popupDelay', 0.5);
newOption('popupHideDelay', 0.5);
newOption('simplePopups', false);
newOption('popupStructure', 'shortmenus'); // see later - default for popupStructure is 'original' if simplePopups is true
newOption('popupActionsMenu', true);
newOption('popupSetupMenu', true);
newOption('popupAdminLinks', userIsSysop);
newOption('popupShortcutKeys', false);
newOption('popupHistoricalLinks', true);
newOption('popupOnlyArticleLinks', true);
newOption('removeTitles', true);
newOption('popupMaxWidth', 350);
newOption('popupSimplifyMainLink', true);
newOption('popupAppendRedirNavLinks', true);
newOption('popupTocLinks', false);
newOption('popupSubpopups', true);
newOption('popupDragHandle', false /* 'popupTopLinks'*/);
newOption('popupLazyPreviews', true);
newOption('popupLazyDownloads', true);
newOption('popupAllDabsStubs', false);
newOption('popupDebugging', false);
newOption('popupActiveNavlinks', true);
newOption('popupModifier', false); // ctrl, shift, alt or meta
newOption('popupModifierAction', 'enable'); // or 'disable'
newOption('popupDraggable', true);
newOption('popupReview', false);
//<NOLITE>
// images
newOption('popupImages', true);
newOption('imagePopupsForImages', true);
newOption('popupNeverGetThumbs', false);
//newOption('popupImagesToggleSize', true);
newOption('popupThumbAction', 'imagepage'); //'sizetoggle');
newOption('popupImageSize', 60);
newOption('popupImageSizeLarge', 200);
// redirs, dabs, reversion
newOption('popupFixRedirs', false);
newOption('popupRedirAutoClick', 'wpDiff');
newOption('popupFixDabs', false);
newOption('popupDabsAutoClick', 'wpDiff');
newOption('popupRevertSummaryPrompt', false);
newOption('popupMinorReverts', false);
newOption('popupRedlinkRemoval', false);
newOption('popupRedlinkAutoClick', 'wpDiff');
newOption('popupWatchDisambiggedPages', null);
newOption('popupWatchRedirredPages', null);
newOption('popupDabWiktionary', 'last');
// navlinks
newOption('popupNavLinks', true);
newOption('popupNavLinkSeparator', ' ⋅ ');
newOption('popupLastEditLink', true);
newOption('popupEditCounterTool', 'supercount');
newOption('popupEditCounterUrl', '');
//</NOLITE>
// previews etc
newOption('popupPreviews', true);
newOption('popupSummaryData', true);
newOption('popupMaxPreviewSentences', 9);
newOption('popupMaxPreviewCharacters', 900);
newOption('popupLastModified', true);
newOption('popupPreviewKillTemplates', true);
newOption('popupPreviewRawTemplates', true);
newOption('popupPreviewFirstParOnly', false);
newOption('popupPreviewCutHeadings', false);
newOption('popupPreviewButton', false);
newOption('popupPreviewButtonEvent', 'click');
//<NOLITE>
// diffs
newOption('popupPreviewDiffs', true);
newOption('popupDiffMaxLines', 100);
newOption('popupDiffContextLines', 2);
newOption('popupDiffContextCharacters', 40);
newOption('popupDiffDates', true);
newOption('popupDiffDatePrinter', 'toLocaleString');
// edit summaries. God, these are ugly.
newOption('popupReviewedSummary', popupString('defaultpopupReviewedSummary') );
newOption('popupFixDabsSummary', popupString('defaultpopupFixDabsSummary') );
newOption('popupExtendedRevertSummary', popupString('defaultpopupExtendedRevertSummary') );
newOption('popupRevertSummary', popupString('defaultpopupRevertSummary') );
newOption('popupRevertToPreviousSummary', popupString('defaultpopupRevertToPreviousSummary') );
newOption('popupQueriedRevertSummary', popupString('defaultpopupQueriedRevertSummary') );
newOption('popupQueriedRevertToPreviousSummary', popupString('defaultpopupQueriedRevertToPreviousSummary') );
newOption('popupFixRedirsSummary', popupString('defaultpopupFixRedirsSummary') );
newOption('popupRedlinkSummary', popupString('defaultpopupRedlinkSummary') );
newOption('popupRmDabLinkSummary', popupString('defaultpopupRmDabLinkSummary') );
//</NOLITE>
// misc
newOption('popupHistoryLimit', 50);
//<NOLITE>
newOption('popupFilters', [popupFilterStubDetect, popupFilterDisambigDetect,
popupFilterPageSize, popupFilterCountLinks,
popupFilterCountImages, popupFilterCountCategories,
popupFilterLastModified]);
newOption('extraPopupFilters', []);
newOption('popupOnEditSelection', 'cursor');
newOption('popupPreviewHistory', true);
newOption('popupImageLinks', true);
newOption('popupCategoryMembers', true);
newOption('popupUserInfo', true);
newOption('popupHistoryPreviewLimit', 25);
newOption('popupContribsPreviewLimit',25);
newOption('popupRevDelUrl', '//en.wikipedia.org/wiki/Wikipedia:Revision_deletion');
newOption('popupShowGender', true);
//</NOLITE>
// new windows
newOption('popupNewWindows', false);
newOption('popupLinksNewWindow', {'lastContrib': true, 'sinceMe': true});
// regexps
newOption('popupDabRegexp', '(\\{\\{\\s*disambig(?!uation needed)|disambig(uation|)\\s*\\}\\}|disamb\\s*\\}\\}|dab\\s*\\}\\})|\\{\\{\\s*(((geo|hn|road?|school|number)dis)|[234][lc][acw]|(road|ship)index)(\\s*[|][^}]*)?\\s*[}][}]|is a .*disambiguation.*page');
newOption('popupAnchorRegexp', 'anchors?'); //how to identify an anchors template
newOption('popupStubRegexp', '(sect)?stub[}][}]|This .*-related article is a .*stub');
newOption('popupImageVarsRegexp', 'image|image_(?:file|skyline|name|flag|seal)|cover|badge|logo');
}
// ENDFILE: options.js
// STARTFILE: strings.js
//<NOLITE>
//////////////////////////////////////////////////
// Translatable strings
//////////////////////////////////////////////////
//
// See instructions at
// https://en.wikipedia.org/wiki/Wikipedia:Tools/Navigation_popups/Translation
pg.string = {
/////////////////////////////////////
// summary data, searching etc.
/////////////////////////////////////
'article': 'article',
'category': 'category',
'categories': 'categories',
'image': 'image',
'images': 'images',
'stub': 'stub',
'section stub': 'section stub',
'Empty page': 'Empty page',
'kB': 'kB',
'bytes': 'bytes',
'day': 'day',
'days': 'days',
'hour': 'hour',
'hours': 'hours',
'minute': 'minute',
'minutes': 'minutes',
'second': 'second',
'seconds': 'seconds',
'week': 'week',
'weeks': 'weeks',
'search': 'search',
'SearchHint': 'Find English Wikipedia articles containing %s',
'web': 'web',
'global': 'global',
'globalSearchHint': 'Search across Wikipedias in different languages for %s',
'googleSearchHint': 'Google for %s',
/////////////////////////////////////
// article-related actions and info
// (some actions also apply to user pages)
/////////////////////////////////////
'actions': 'actions', ///// view articles and view talk
'popupsMenu': 'popups',
'togglePreviewsHint': 'Toggle preview generation in popups on this page',
'enable previews': 'enable previews',
'disable previews': 'disable previews',
'toggle previews': 'toggle previews',
'show preview': 'show preview',
'reset': 'reset',
'more...': 'more...',
'disable': 'disable popups',
'disablePopupsHint': 'Disable popups on this page. Reload page to re-enable.',
'historyfeedHint': 'RSS feed of recent changes to this page',
'purgePopupsHint': 'Reset popups, clearing all cached popup data.',
'PopupsHint': 'Reset popups, clearing all cached popup data.',
'spacebar': 'space',
'view': 'view',
'view article': 'view article',
'viewHint': 'Go to %s',
'talk': 'talk',
'talk page': 'talk page',
'this revision': 'this revision',
'revision %s of %s': 'revision %s of %s',
'Revision %s of %s': 'Revision %s of %s',
'the revision prior to revision %s of %s': 'the revision prior to revision %s of %s',
'Toggle image size': 'Click to toggle image size',
'del': 'del', ///// delete, protect, move
'delete': 'delete',
'deleteHint': 'Delete %s',
'undeleteShort': 'un',
'UndeleteHint': 'Show the deletion history for %s',
'protect': 'protect',
'protectHint': 'Restrict editing rights to %s',
'unprotectShort': 'un',
'unprotectHint': 'Allow %s to be edited by anyone again',
'send thanks': 'send thanks',
'ThanksHint': 'Send a thank you notification to this user',
'move': 'move',
'move page': 'move page',
'MovepageHint': 'Change the title of %s',
'edit': 'edit', ///// edit articles and talk
'edit article': 'edit article',
'editHint': 'Change the content of %s',
'edit talk': 'edit talk',
'new': 'new',
'new topic': 'new topic',
'newSectionHint': 'Start a new section on %s',
'null edit': 'null edit',
'nullEditHint': 'Submit an edit to %s, making no changes ',
'hist': 'hist', ///// history, diffs, editors, related
'history': 'history',
'historyHint': 'List the changes made to %s',
'last': 'prev', // For labelling the previous revision in history pages; the key is "last" for backwards compatibility
'lastEdit': 'lastEdit',
'mark patrolled': 'mark patrolled',
'markpatrolledHint': 'Mark this edit as patrolled',
'Could not marked this edit as patrolled': 'Could not marked this edit as patrolled',
'show last edit': 'most recent edit',
'Show the last edit': 'Show the effects of the most recent change',
'lastContrib': 'lastContrib',
'last set of edits': 'latest edits',
'lastContribHint': 'Show the net effect of changes made by the last editor',
'cur': 'cur',
'diffCur': 'diffCur',
'Show changes since revision %s': 'Show changes since revision %s',
'%s old': '%s old', // as in 4 weeks old
'oldEdit': 'oldEdit',
'purge': 'purge',
'purgeHint': 'Demand a fresh copy of %s',
'raw': 'source',
'rawHint': 'Download the source of %s',
'render': 'simple',
'renderHint': 'Show a plain HTML version of %s',
'Show the edit made to get revision': 'Show the edit made to get revision',
'sinceMe': 'sinceMe',
'changes since mine': 'diff my edit',
'sinceMeHint': 'Show changes since my last edit',
'Couldn\'t find an edit by %s\nin the last %s edits to\n%s': 'Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
'eds': 'eds',
'editors': 'editors',
'editorListHint': 'List the users who have edited %s',
'related': 'related',
'relatedChanges': 'relatedChanges',
'related changes': 'related changes',
'RecentchangeslinkedHint': 'Show changes in articles related to %s',
'editOld': 'editOld', ///// edit old version, or revert
'rv': 'rv',
'revert': 'revert',
'revertHint': 'Revert to %s',
'defaultpopupReviewedSummary': 'Accepted by reviewing the [[Special:diff/%s/%s|difference]] between this version and previously accepted version using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRedlinkSummary': 'Removing link to empty page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixDabsSummary': 'Disambiguate [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixRedirsSummary': 'Redirect bypass from [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupExtendedRevertSummary': 'Revert to revision dated %s by %s, oldid %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertToPreviousSummary': 'Revert to the revision prior to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertSummary': 'Revert to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertToPreviousSummary': 'Revert to the revision prior to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertSummary': 'Revert to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRmDabLinkSummary': 'Remove link to dab page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'Redirects': 'Redirects', // as in Redirects to ...
' to ': ' to ', // as in Redirects to ...
'Bypass redirect': 'Bypass redirect',
'Fix this redirect': 'Fix this redirect',
'disambig': 'disambig', ///// add or remove dab etc.
'disambigHint': 'Disambiguate this link to [[%s]]',
'Click to disambiguate this link to:': 'Click to disambiguate this link to:',
'remove this link': 'remove this link',
'remove all links to this page from this article': 'remove all links to this page from this article',
'remove all links to this disambig page from this article': 'remove all links to this disambig page from this article',
'mainlink': 'mainlink', ///// links, watch, unwatch
'wikiLink': 'wikiLink',
'wikiLinks': 'wikiLinks',
'links here': 'links here',
'whatLinksHere': 'whatLinksHere',
'what links here': 'what links here',
'WhatlinkshereHint': 'List the pages that are hyperlinked to %s',
'unwatchShort': 'un',
'watchThingy': 'watch', // called watchThingy because {}.watch is a function
'watchHint': 'Add %s to my watchlist',
'unwatchHint': 'Remove %s from my watchlist',
'Only found one editor: %s made %s edits': 'Only found one editor: %s made %s edits',
'%s seems to be the last editor to the page %s': '%s seems to be the last editor to the page %s',
'rss': 'rss',
/////////////////////////////////////
// diff previews
/////////////////////////////////////
'Diff truncated for performance reasons': 'Diff truncated for performance reasons',
'Old revision': 'Old revision',
'New revision': 'New revision',
'Something went wrong :-(': 'Something went wrong :-(',
'Empty revision, maybe non-existent': 'Empty revision, maybe non-existent',
'Unknown date': 'Unknown date',
/////////////////////////////////////
// other special previews
/////////////////////////////////////
'Empty category': 'Empty category',
'Category members (%s shown)': 'Category members (%s shown)',
'No image links found': 'No image links found',
'File links': 'File links',
'No image found': 'No image found',
'Image from Commons': 'Image from Commons',
'Description page': 'Description page',
'Alt text:': 'Alt text:',
'revdel':'Hidden revision',
/////////////////////////////////////
// user-related actions and info
/////////////////////////////////////
'user': 'user', ///// user page, talk, email, space
'user page': 'user page',
'user talk': 'user talk',
'edit user talk': 'edit user talk',
'leave comment': 'leave comment',
'email': 'email',
'email user': 'email user',
'EmailuserHint': 'Send an email to %s',
'space': 'space', // short form for userSpace link
'PrefixIndexHint': 'Show pages in the userspace of %s',
'count': 'count', ///// contributions, log
'edit counter': 'edit counter',
'editCounterLinkHint': 'Count the contributions made by %s',
'contribs': 'contribs',
'contributions': 'contributions',
'deletedContribs': 'deleted contributions',
'DeletedcontributionsHint': 'List deleted edits made by %s',
'ContributionsHint': 'List the contributions made by %s',
'log': 'log',
'user log': 'user log',
'userLogHint': 'Show %s\'s user log',
'arin': 'ARIN lookup', ///// ARIN lookup, block user or IP
'Look up %s in ARIN whois database': 'Look up %s in the ARIN whois database',
'unblockShort': 'un',
'block': 'block',
'block user': 'block user',
'IpblocklistHint': 'Unblock %s',
'BlockipHint': 'Prevent %s from editing',
'block log': 'block log',
'blockLogHint': 'Show the block log for %s',
'protectLogHint': 'Show the protection log for %s',
'pageLogHint': 'Show the page log for %s',
'deleteLogHint': 'Show the deletion log for %s',
'Invalid %s %s': 'The option %s is invalid: %s',
'No backlinks found': 'No backlinks found',
' and more': ' and more',
'undo': 'undo',
'undoHint': 'undo this edit',
'Download preview data': 'Download preview data',
'Invalid or IP user': 'Invalid or IP user',
'Not a registered username': 'Not a registered username',
'CẤM': 'CẤM',
'Has blocks': 'Has blocks',
' sửa đổi từ: ': ' sửa đổi từ: ',
'sửa đổi lần cuối vào ': 'sửa đổi lần cuối vào ',
/////////////////////////////////////
// Autoediting
/////////////////////////////////////
'Enter a non-empty edit summary or press cancel to abort': 'Enter a non-empty edit summary or press cancel to abort',
'Failed to get revision information, please edit manually.\n\n': 'Failed to get revision information, please edit manually.\n\n',
'The %s button has been automatically clicked. Please wait for the next page to load.': 'The %s button has been automatically clicked. Please wait for the next page to load.',
'Could not find button %s. Please check the settings in your javascript file.': 'Could not find button %s. Please check the settings in your javascript file.',
/////////////////////////////////////
// Popups setup
/////////////////////////////////////
'Open full-size image': 'Open full-size image',
'zxy': 'zxy',
'autoedit_version': 'np20140416'
};
function popupString(str) {
if (typeof popupStrings != 'undefined' && popupStrings && popupStrings[str]) { return popupStrings[str]; }
if (pg.string[str]) { return pg.string[str]; }
return str;
}
function tprintf(str,subs) {
if (typeof subs != typeof []) { subs = [subs]; }
return simplePrintf(popupString(str), subs);
}
//</NOLITE>
// ENDFILE: strings.js
// STARTFILE: run.js
////////////////////////////////////////////////////////////////////
// Run things
////////////////////////////////////////////////////////////////////
// For some reason popups requires a fully loaded page jQuery.ready(...) causes problems for some.
// The old addOnloadHook did something similar to the below
if (document.readyState=="complete")
autoEdit(); //will setup popups
else
$( window ).on( 'load', autoEdit );
// Support for MediaWiki's live preview, VisualEditor's saves and Echo's flyout.
( function () {
var once = true;
function dynamicContentHandler( $content ) {
// Try to detect the hook fired on initial page load and disregard
// it, we already hook to onload (possibly to different parts of
// page - it's configurable) and running twice might be bad. Ugly…
if ( $content.attr( 'id' ) == 'mw-content-text' ) {
if ( once ) {
once = false;
return;
}
}
function registerHooksForVisibleNavpops () {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
var navpop = pg.current.links[i].navpopup;
if (!navpop || !navpop.isVisible()) { continue; }
Navpopup.tracker.addHook(posCheckerHook(navpop));
}
}
function doIt () {
registerHooksForVisibleNavpops();
$content.each( function () {
this.ranSetupTooltipsAlready = false;
setupTooltips( this );
} );
}
setupPopups( doIt );
}
// This hook is also fired after page load.
mw.hook( 'wikipage.content' ).add( dynamicContentHandler );
mw.hook( 'ext.echo.overlay.beforeShowingOverlay' ).add( function($overlay){
dynamicContentHandler( $overlay.find(".mw-echo-state") );
});
} )();
// Forked from User:Connel MacKenzie/mess-with-popups.js
Previewmaker.prototype.firstBit = function() {
try{
if (mw.config.get("wgNamespaceNumber") !== 0){
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d = this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data = this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data = this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data = this.data.replace(RegExp('^[\\s\\n]*'), '');
stuff = (RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) {
d = stuff[0];
}
if (!getValueOf('popupPreviewFirstParOnly')) {
d = this.data;
}
/// now put \n\n after sections so that bullets and numbered lists work
d = d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["' + "'" + ']*\\s)', 'g'));
// leading space is bad, mmkay?
d[0] = d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds = RegExp('([^.][a-z][.][a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
var maxChars=getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd;
do {
dd = this.firstSentences(d, n);
--n;
}
while (dd.length > maxChars && n > 0);
this.data = dd;
} else {
//TODO: Limit this override to urls that start with http://*.wiktionary.org/
//TODO: extend the above section to also preview Wikipedia links
var charsWikt = 0;
var linesWikt = this.data.split('\n');
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[in]transitive}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[un]countable}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{cattag\|(.*?)}}/g, "");
linesWikt[jWikt] = linesWikt[jWikt].replace(/[\{\}]/g, "'");
if (linesWikt[jWikt].search(/^[#]/) == -1) {
if (linesWikt[jWikt].search(/^==[A-Z]/) == -1) {
linesWikt[jWikt] = '';
}
}
charsWikt += linesWikt[jWikt].length;
}
if (charsWikt < 2) {
linesWikt[0] = "No properly formatted content";
}
var maxChars = getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd = '';
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
if (linesWikt[jWikt] !== '') {
dd += linesWikt[jWikt] + '\n';
}
if (dd.length < maxChars) {
this.data = dd;
}
}
}
}
catch (e) {
console.log("error from mess with popups: ", e);
}
};
});
iqm3e9u946v48zncugkw15jz5v1cn7x
2513982
2513951
2026-08-14T08:12:27Z
Hiyuune
50834
2513982
javascript
text/javascript
// {{documentation}}
// _________________________________________________________________________________________
// | |
// | === WARNING: GLOBAL GADGET FILE === |
// | Changes to this page affect many users. |
// | Please discuss changes on the [[Wiktionary:Grease Pit]] before editing. |
// |_________________________________________________________________________________________|
mw.loader.load('//en.wikipedia.org/w/index.php?action=raw&title=MediaWiki:Gadget-navpop.css&ctype=text/css', "text/css");
// Forked from [[:w:MediaWiki:Gadget-popups.js]]
$(function () {
//////////////////////////////////////////////////
// Globals
//
// Trying to shove as many of these as possible into the pg (popup globals) object
var pg = {
re: {}, // regexps
ns: {}, // namespaces
string: {}, // translatable strings
wiki: {}, // local site info
user: {}, // current user info
misc: {}, // YUCK PHOOEY
option: {}, // options, see newOption etc
optionDefault: {}, // default option values
flag: {}, // misc flags
cache: {}, // page and image cache
structures: {}, // navlink structures
timer: {}, // all sorts of timers (too damn many)
counter: {}, // .. and all sorts of counters
current: {}, // state info
fn: {}, // functions
endoflist: null
};
/* Bail if the gadget/script is being loaded twice */
if( window.pg ) {
return;
}
/* Export to global context */
window.pg = pg;
/// Local Variables: ///
/// mode:c ///
/// End: ///
// ENDFILE: main.js
// STARTFILE: actions.js
function setupTooltips(container, remove, force, popData) {
log('setupTooltips, container='+container+', remove='+remove);
if (!container) {
//<NOLITE>
// the main initial call
if (getValueOf('popupOnEditSelection') && document && document.editform && document.editform.wpTextbox1) {
document.editform.wpTextbox1.onmouseup=doSelectionPopup;
}
//</NOLITE>
// article/content is a structure-dependent thing
container = defaultPopupsContainer();
}
if (!remove && !force && container.ranSetupTooltipsAlready) { return; }
container.ranSetupTooltipsAlready = !remove;
var anchors;
anchors=container.getElementsByTagName('A');
setupTooltipsLoop(anchors, 0, 250, 100, remove, popData);
}
function defaultPopupsContainer() {
if (getValueOf('popupOnlyArticleLinks')) {
return document.getElementById('mw_content') ||
document.getElementById('content') ||
document.getElementById('article') || document;
}
return document;
}
function setupTooltipsLoop(anchors,begin,howmany,sleep, remove, popData) {
log(simplePrintf('setupTooltipsLoop(%s,%s,%s,%s,%s)', arguments));
var finish=begin+howmany;
var loopend = Math.min(finish, anchors.length);
var j=loopend - begin;
log ('setupTooltips: anchors.length=' + anchors.length + ', begin=' + begin +
', howmany=' + howmany + ', loopend=' + loopend + ', remove=' + remove);
var doTooltip= remove ? removeTooltip : addTooltip;
// try a faster (?) loop construct
if (j > 0) {
do {
var a=anchors[loopend - j];
if (typeof a==='undefined' || !a || !a.href) {
log('got null anchor at index ' + loopend - j);
continue;
}
doTooltip(a, popData);
} while (--j);
}
if (finish < anchors.length) {
setTimeout(function() {
setupTooltipsLoop(anchors,finish,howmany,sleep,remove,popData);},
sleep);
} else {
if ( !remove && ! getValueOf('popupTocLinks')) { rmTocTooltips(); }
pg.flag.finishedLoading=true;
}
}
// eliminate popups from the TOC
// This also kills any onclick stuff that used to be going on in the toc
function rmTocTooltips() {
var toc=document.getElementById('toc');
if (toc) {
var tocLinks=toc.getElementsByTagName('A');
var tocLen = tocLinks.length;
for (var j=0; j<tocLen; ++j) {
removeTooltip(tocLinks[j], true);
}
}
}
function addTooltip(a, popData) {
if ( !isPopupLink(a) ) { return; }
a.onmouseover=mouseOverWikiLink;
a.onmouseout= mouseOutWikiLink;
a.onmousedown = killPopup;
a.hasPopup = true;
a.popData = popData;
}
function removeTooltip(a) {
if ( !a.hasPopup ) { return; }
a.onmouseover = null;
a.onmouseout = null;
if (a.originalTitle) { a.title = a.originalTitle; }
a.hasPopup=false;
}
function removeTitle(a) {
if (!a.originalTitle) {
a.originalTitle=a.title;
}
a.title='';
}
function restoreTitle(a) {
if ( a.title || !a.originalTitle ) { return; }
a.title = a.originalTitle;
}
function registerHooks(np) {
var popupMaxWidth=getValueOf('popupMaxWidth');
if (typeof popupMaxWidth === 'number') {
var setMaxWidth = function () {
np.mainDiv.style.maxWidth = popupMaxWidth + 'px';
np.maxWidth = popupMaxWidth;
};
np.addHook(setMaxWidth, 'unhide', 'before');
}
//<NOLITE>
np.addHook(addPopupShortcuts, 'unhide', 'after');
np.addHook(rmPopupShortcuts, 'hide', 'before');
//</NOLITE>
}
function removeModifierKeyHandler(a) {
//remove listeners for modifier key if any that were added in mouseOverWikiLink
document.removeEventListener('keydown', a.modifierKeyHandler, false);
document.removeEventListener('keyup', a.modifierKeyHandler, false);
}
function mouseOverWikiLink(evt) {
if (!evt && window.event) {evt=window.event;}
// if the modifier is needed, listen for it,
// we will remove the listener when we mouseout of this link or kill popup.
if (getValueOf('popupModifier')) {
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
var key = action=='disable' ? 'keyup' : 'keydown';
var a = this;
a.modifierKeyHandler = function(evt) {
mouseOverWikiLink2(a, evt);
};
document.addEventListener(key, a.modifierKeyHandler, false);
}
return mouseOverWikiLink2(this, evt);
}
/**
* Gets the references list item that the provided footnote link targets. This
* is typically a li element within the ol.references element inside the reflist.
* @param {Element} a - A footnote link.
* @returns {Element|boolean} The targeted element, or false if one can't be found.
*/
function footnoteTarget(a) {
var aTitle=Title.fromAnchor(a);
// We want ".3A" rather than "%3A" or "?" here, so use the anchor property directly
var anch = aTitle.anchor;
if ( ! /^(cite_note-|_note-|endnote)/.test(anch) ) { return false; }
var lTitle=Title.fromURL(location.href);
if ( lTitle.toString(true) !== aTitle.toString(true) ) { return false; }
var el=document.getElementById(anch);
while ( el && typeof el.nodeName === 'string') {
var nt = el.nodeName.toLowerCase();
if ( nt === 'li' ) { return el; }
else if ( nt === 'body' ) { return false; }
else if ( el.parentNode ) { el=el.parentNode; }
else { return false; }
}
return false;
}
function footnotePreview(x, navpop) {
setPopupHTML('<hr />' + x.innerHTML, 'popupPreview', navpop.idNumber);
}
function modifierPressed(evt) {
var mod=getValueOf('popupModifier');
if (!mod) { return false; }
if (!evt && window.event) {evt=window.event;}
return ( evt && mod && evt[mod.toLowerCase() + 'Key'] );
}
// Checks if the correct modifier pressed/unpressed if needed
function isCorrectModifier(a,evt) {
if (!getValueOf('popupModifier')) { return true; }
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
return ( action == 'enable' && modifierPressed(evt) ||
action == 'disable' && !modifierPressed(evt) );
}
function mouseOverWikiLink2(a, evt) {
if (!isCorrectModifier(a,evt)) { return; }
if ( getValueOf('removeTitles') ) { removeTitle(a); }
if ( a==pg.current.link && a.navpopup && a.navpopup.isVisible() ) { return; }
pg.current.link=a;
if (getValueOf('simplePopups') && !pg.option.popupStructure) {
// reset *default value* of popupStructure
setDefault('popupStructure', 'original');
}
var article=(new Title()).fromAnchor(a);
// set global variable (ugh) to hold article (wikipage)
pg.current.article = article;
if (!a.navpopup) {
a.navpopup=newNavpopup(a, article);
pg.current.linksHash[a.href] = a.navpopup;
pg.current.links.push(a);
}
if (a.navpopup.pending === null || a.navpopup.pending !== 0) {
// either fresh popups or those with unfinshed business are redone from scratch
simplePopupContent(a, article);
}
a.navpopup.showSoonIfStable(a.navpopup.delay);
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=setInterval(checkPopupPosition, 600);
if(getValueOf('simplePopups')) {
if (getValueOf('popupPreviewButton') && !a.simpleNoMore) {
var d=document.createElement('div');
d.className='popupPreviewButtonDiv';
var s=document.createElement('span');
d.appendChild(s);
s.className='popupPreviewButton';
s['on' + getValueOf('popupPreviewButtonEvent')] = function() {
a.simpleNoMore=true;
d.style.display = "none";
nonsimplePopupContent(a,article);
};
s.innerHTML=popupString('show preview');
setPopupHTML(d, 'popupPreview', a.navpopup.idNumber);
}
}
if (a.navpopup.pending !== 0 ) {
nonsimplePopupContent(a, article);
}
}
// simplePopupContent: the content that do not require additional download
// (it is shown even when simplePopups is true)
function simplePopupContent(a, article) {
/* FIXME hack */ a.navpopup.hasPopupMenu=false;
a.navpopup.setInnerHTML(popupHTML(a));
fillEmptySpans({navpopup:a.navpopup});
if (getValueOf('popupDraggable'))
{
var dragHandle = getValueOf('popupDragHandle') || null;
if (dragHandle && dragHandle != 'all') {
dragHandle += a.navpopup.idNumber;
}
setTimeout(function(){a.navpopup.makeDraggable(dragHandle);}, 150);
}
//<NOLITE>
if (getValueOf('popupRedlinkRemoval') && a.className=='new') {
setPopupHTML('<br>'+popupRedlinkHTML(article), 'popupRedlink', a.navpopup.idNumber);
}
//</NOLITE>
}
function debugData(navpopup) {
if(getValueOf('popupDebugging') && navpopup.idNumber) {
setPopupHTML('idNumber='+navpopup.idNumber + ', pending=' + navpopup.pending,
'popupError', navpopup.idNumber);
}
}
function newNavpopup(a, article) {
var navpopup = new Navpopup();
navpopup.fuzz=5;
navpopup.delay=getValueOf('popupDelay')*1000;
// increment global counter now
navpopup.idNumber = ++pg.idNumber;
navpopup.parentAnchor = a;
navpopup.parentPopup = (a.popData && a.popData.owner);
navpopup.article = article;
registerHooks(navpopup);
return navpopup;
}
// Should we show nonsimple context?
// If simplePopups is set to true, then we do not show nonsimple context,
// but if a bottom "show preview" was clicked we do show nonsimple context
function shouldShowNonSimple(a) {
return !getValueOf('simplePopups') || a.simpleNoMore;
}
// Should we show nonsimple context govern by the option (e.g. popupUserInfo)?
// If the user explicitly asked for nonsimple context by setting the option to true,
// then we show it even in nonsimple mode.
function shouldShow(a,option) {
if (shouldShowNonSimple(a)) {
return getValueOf(option);
} else {
return (typeof window[option] != 'undefined' ) && window[option];
}
}
function nonsimplePopupContent(a, article) {
var diff=null, history=null;
var params=parseParams(a.href);
var oldid=(typeof params.oldid=='undefined' ? null : params.oldid);
//<NOLITE>
if(shouldShow(a,'popupPreviewDiffs')) {
diff=params.diff;
}
if(shouldShow(a,'popupPreviewHistory')) {
history=(params.action=='history');
}
//</NOLITE>
a.navpopup.pending=0;
var referenceElement = footnoteTarget(a);
if (referenceElement) {
footnotePreview(referenceElement, a.navpopup);
//<NOLITE>
} else if ( diff || diff === 0 ) {
loadDiff(article, oldid, diff, a.navpopup);
} else if ( history ) {
loadAPIPreview('history', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.contribs.test(a.href) ) {
loadAPIPreview('contribs', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.backlinks.test(a.href) ) {
loadAPIPreview('backlinks', article, a.navpopup);
} else if ( // FIXME should be able to get all preview combinations with options
article.namespaceId()==pg.nsImageId &&
( shouldShow(a,'imagePopupsForImages') || ! anchorContainsImage(a) )
) {
loadAPIPreview('imagepagepreview', article, a.navpopup);
loadImage(article, a.navpopup);
//</NOLITE>
} else {
if (article.namespaceId() == pg.nsCategoryId &&
shouldShow(a,'popupCategoryMembers')) {
loadAPIPreview('category', article, a.navpopup);
} else if ((article.namespaceId() == pg.nsUserId || article.namespaceId() == pg.nsUsertalkId) &&
shouldShow(a,'popupUserInfo')) {
loadAPIPreview('userinfo', article, a.navpopup);
}
if (shouldShowNonSimple(a)) startArticlePreview(article, oldid, a.navpopup);
}
}
function pendingNavpopTask(navpop) {
if (navpop && navpop.pending === null) { navpop.pending=0; }
++navpop.pending;
debugData(navpop);
}
function completedNavpopTask(navpop) {
if (navpop && navpop.pending) { --navpop.pending; }
debugData(navpop);
}
function startArticlePreview(article, oldid, navpop) {
navpop.redir=0;
loadPreview(article, oldid, navpop);
}
function loadPreview(article, oldid, navpop) {
if (!navpop.redir) { navpop.originalArticle=article; }
article.oldid = oldid;
loadAPIPreview('revision', article, navpop);
}
function loadPreviewFromRedir(redirMatch, navpop) {
// redirMatch is a regex match
var target = new Title().fromWikiText(redirMatch[2]);
// overwrite (or add) anchor from original target
// mediawiki does overwrite; eg User:Lupin/foo3#Done
if ( navpop.article.anchor ) { target.anchor = navpop.article.anchor; }
navpop.redir++;
navpop.redirTarget=target;
//<NOLITE>
var warnRedir = redirLink(target, navpop.article);
setPopupHTML(warnRedir, 'popupWarnRedir', navpop.idNumber);
//</NOLITE>
navpop.article=target;
fillEmptySpans({redir: true, redirTarget: target, navpopup:navpop});
return loadPreview(target, null, navpop);
}
function insertPreview(download) {
if (!download.owner) { return; }
var redirMatch = pg.re.redirect.exec(download.data);
if (download.owner.redir === 0 && redirMatch) {
loadPreviewFromRedir(redirMatch, download.owner);
return;
}
if (download.owner.visible || !getValueOf('popupLazyPreviews')) {
insertPreviewNow(download);
} else {
var id=(download.owner.redir) ? 'PREVIEW_REDIR_HOOK' : 'PREVIEW_HOOK';
download.owner.addHook( function(){insertPreviewNow(download); return true;},
'unhide', 'after', id );
}
}
function insertPreviewNow(download) {
if (!download.owner) { return; }
var wikiText=download.data;
var navpop=download.owner;
var art=navpop.redirTarget || navpop.originalArticle;
//<NOLITE>
makeFixDabs(wikiText, navpop);
if (getValueOf('popupSummaryData')) {
getPageInfo(wikiText, download);
setPopupTrailer(getPageInfo(wikiText, download), navpop.idNumber);
}
var imagePage='';
if (art.namespaceId()==pg.nsImageId) { imagePage=art.toString(); }
else { imagePage=getValidImageFromWikiText(wikiText); }
if(imagePage) { loadImage(Title.fromWikiText(imagePage), navpop); }
//</NOLITE>
if (getValueOf('popupPreviews')) { insertArticlePreview(download, art, navpop); }
}
function insertArticlePreview(download, art, navpop) {
if (download && typeof download.data == typeof ''){
if (art.namespaceId()==pg.nsTemplateId && getValueOf('popupPreviewRawTemplates')) {
// FIXME compare/consolidate with diff escaping code for wikitext
var h='<hr /><span style="font-family: monospace;">' + download.data.entify().split('\\n').join('<br />\\n') + '</span>';
setPopupHTML(h, 'popupPreview', navpop.idNumber);
}
else {
var p=prepPreviewmaker(download.data, art, navpop);
p.showPreview();
}
}
}
function prepPreviewmaker(data, article, navpop) {
// deal with tricksy anchors
var d=anchorize(data, article.anchorString());
var urlBase=joinPath([pg.wiki.articlebase, article.urlString()]);
var p=new Previewmaker(d, urlBase, navpop);
return p;
}
// Try to imitate the way mediawiki generates HTML anchors from section titles
function anchorize(d, anch) {
if (!anch) { return d; }
var anchRe=RegExp('(?:=+\\s*' + literalizeRegex(anch).replace(/[_ ]/g, '[_ ]') + '\\s*=+|\\{\\{\\s*'+getValueOf('popupAnchorRegexp')+'\\s*(?:\\|[^|}]*)*?\\s*'+literalizeRegex(anch)+'\\s*(?:\\|[^}]*)?}})');
var match=d.match(anchRe);
if(match && match.length > 0 && match[0]) { return d.substring(d.indexOf(match[0])); }
// now try to deal with == foo [[bar|baz]] boom == -> #foo_baz_boom
var lines=d.split('\n');
for (var i=0; i<lines.length; ++i) {
lines[i]=lines[i].replace(RegExp('[[]{2}([^|\\]]*?[|])?(.*?)[\\]]{2}', 'g'), '$2')
.replace(/'''([^'])/g, '$1').replace(RegExp("''([^'])", 'g'), '$1');
if (lines[i].match(anchRe)) {
return d.split('\n').slice(i).join('\n').replace(RegExp('^[^=]*'), '');
}
}
return d;
}
function killPopup() {
removeModifierKeyHandler(this);
if (getValueOf('popupShortcutKeys')) { rmPopupShortcuts(); }
if (!pg) { return; }
if (pg.current.link && pg.current.link.navpopup) { pg.current.link.navpopup.banish(); }
pg.current.link=null;
abortAllDownloads();
if (pg.timer.checkPopupPosition) {
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=null;
}
return true; // preserve default action
}
// ENDFILE: actions.js
// STARTFILE: domdrag.js
/**
@fileoverview
The {@link Drag} object, which enables objects to be dragged around.
<pre>
*************************************************
dom-drag.js
09.25.2001
www.youngpup.net
**************************************************
10.28.2001 - fixed minor bug where events
sometimes fired off the handle, not the root.
*************************************************
Pared down, some hooks added by User:Lupin
Copyright Aaron Boodman.
Saying stupid things daily since March 2001.
</pre>
*/
/**
Creates a new Drag object. This is used to make various DOM elements draggable.
@constructor
*/
function Drag () {
/**
Condition to determine whether or not to drag. This function should take one parameter, an Event.
To disable this, set it to <code>null</code>.
@type Function
*/
this.startCondition = null;
/**
Hook to be run when the drag finishes. This is passed the final coordinates of
the dragged object (two integers, x and y). To disables this, set it to <code>null</code>.
@type Function
*/
this.endHook = null;
}
/**
Gets an event in a cross-browser manner.
@param {Event} e
@private
*/
Drag.prototype.fixE = function(e) {
if (typeof e == 'undefined') { e = window.event; }
if (typeof e.layerX == 'undefined') { e.layerX = e.offsetX; }
if (typeof e.layerY == 'undefined') { e.layerY = e.offsetY; }
return e;
};
/**
Initialises the Drag instance by telling it which object you want to be draggable, and what you want to drag it by.
@param {DOMElement} o The "handle" by which <code>oRoot</code> is dragged.
@param {DOMElement} oRoot The object which moves when <code>o</code> is dragged, or <code>o</code> if omitted.
*/
Drag.prototype.init = function(o, oRoot) {
var dragObj = this;
this.obj = o;
o.onmousedown = function(e) { dragObj.start.apply( dragObj, [e]); };
o.dragging = false;
o.popups_draggable = true;
o.hmode = true;
o.vmode = true;
o.root = oRoot ? oRoot : o ;
if (isNaN(parseInt(o.root.style.left, 10))) { o.root.style.left = "0px"; }
if (isNaN(parseInt(o.root.style.top, 10))) { o.root.style.top = "0px"; }
o.root.onthisStart = function(){};
o.root.onthisEnd = function(){};
o.root.onthis = function(){};
};
/**
Starts the drag.
@private
@param {Event} e
*/
Drag.prototype.start = function(e) {
var o = this.obj; // = this;
e = this.fixE(e);
if (this.startCondition && !this.startCondition(e)) { return; }
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10);
o.root.onthisStart(x, y);
o.lastMouseX = e.clientX;
o.lastMouseY = e.clientY;
var dragObj = this;
o.onmousemoveDefault = document.onmousemove;
o.dragging = true;
document.onmousemove = function(e) { dragObj.drag.apply( dragObj, [e] ); };
document.onmouseup = function(e) { dragObj.end.apply( dragObj, [e] ); };
return false;
};
/**
Does the drag.
@param {Event} e
@private
*/
Drag.prototype.drag = function(e) {
e = this.fixE(e);
var o = this.obj;
var ey = e.clientY;
var ex = e.clientX;
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10 );
var nx, ny;
nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));
this.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
this.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
this.obj.lastMouseX = ex;
this.obj.lastMouseY = ey;
this.obj.root.onthis(nx, ny);
return false;
};
/**
Ends the drag.
@private
*/
Drag.prototype.end = function() {
document.onmousemove=this.obj.onmousemoveDefault;
document.onmouseup = null;
this.obj.dragging = false;
if (this.endHook) {
this.endHook( parseInt(this.obj.root.style[this.obj.hmode ? "left" : "right"], 10),
parseInt(this.obj.root.style[this.obj.vmode ? "top" : "bottom"], 10));
}
};
// ENDFILE: domdrag.js
// STARTFILE: structures.js
//<NOLITE>
pg.structures.original={};
pg.structures.original.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle',
'popupUserData', 'popupData', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.original.popupRedirSpans=function () {
return ['popupRedir', 'popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'];
};
pg.structures.original.popupTitle=function (x) {
log ('defaultstructure.popupTitle');
if (!getValueOf('popupNavLinks')) {
return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
}
return '';
};
pg.structures.original.popupTopLinks=function (x) {
log ('defaultstructure.popupTopLinks');
if (getValueOf('popupNavLinks')) { return navLinksHTML(x.article, x.hint, x.params); }
return '';
};
pg.structures.original.popupImage=function(x) {
log ('original.popupImage, x.article='+x.article+', x.navpop.idNumber='+x.navpop.idNumber);
return imageHTML(x.article, x.navpop.idNumber);
};
pg.structures.original.popupRedirTitle=pg.structures.original.popupTitle;
pg.structures.original.popupRedirTopLinks=pg.structures.original.popupTopLinks;
function copyStructure(oldStructure, newStructure) {
pg.structures[newStructure]={};
for (var prop in pg.structures[oldStructure]) {
pg.structures[newStructure][prop]=pg.structures[oldStructure][prop];
}
}
copyStructure('original', 'nostalgia');
pg.structures.nostalgia.popupTopLinks=function(x) {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>';
str+='if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' +
editstr + '}';
var historystr='<<history|shortcut=h>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str += '<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>';
str += 'if(admin){<br>}else{*}<<move|shortcut=m>>';
// admin links
str += 'if(admin){*<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>}';
return navlinkStringToHTML(str, x.article, x.params);
};
pg.structures.nostalgia.popupRedirTopLinks=pg.structures.nostalgia.popupTopLinks;
/** -- fancy -- **/
copyStructure('original', 'fancy');
pg.structures.fancy.popupTitle=function (x) {
return navlinkStringToHTML('<font size=+0><<mainlink>></font>',x.article,x.params);
};
pg.structures.fancy.popupTopLinks=function(x) {
var hist='<<history|shortcut=h|hist>>|<<lastEdit|shortcut=/|last>>|<<editors|shortcut=E|eds>>';
var watch='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
var move='<<move|shortcut=m|move>>';
return navlinkStringToHTML('if(talk){' +
'<<edit|shortcut=e>>|<<new|shortcut=+|+>>*' + hist + '*' +
'<<article|shortcut=a>>|<<editArticle|edit>>' + '*' + watch + '*' + move +
'}else{<<edit|shortcut=e>>*' + hist +
'*<<talk|shortcut=t|>>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>' +
'*' + watch + '*' + move+'}<br>', x.article, x.params);
};
pg.structures.fancy.popupOtherLinks=function(x) {
var admin='<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*<<undelete|undeleteShort>>|<<delete|shortcut=d|del>>';
var user='<<contribs|shortcut=c>>if(wikimedia){|<<count|shortcut=#|#>>}';
user+='if(ipuser){|<<arin>>}else{*<<email|shortcut=E|'+
popupString('email')+'>>}if(admin){*<<block|shortcut=b>>}';
var normal='<<whatLinksHere|shortcut=l|links here>>*<<relatedChanges|shortcut=r|related>>';
return navlinkStringToHTML('<br>if(user){' + user + '*}if(admin){'+admin+'if(user){<br>}else{*}}' + normal,
x.article, x.params);
};
pg.structures.fancy.popupRedirTitle=pg.structures.fancy.popupTitle;
pg.structures.fancy.popupRedirTopLinks=pg.structures.fancy.popupTopLinks;
pg.structures.fancy.popupRedirOtherLinks=pg.structures.fancy.popupOtherLinks;
/** -- fancy2 -- **/
// hack for User:MacGyverMagic
copyStructure('fancy', 'fancy2');
pg.structures.fancy2.popupTopLinks=function(x) { // hack out the <br> at the end and put one at the beginning
return '<br>'+pg.structures.fancy.popupTopLinks(x).replace(RegExp('<br>$','i'),'');
};
pg.structures.fancy2.popupLayout=function () { // move toplinks to after the title
return ['popupError', 'popupImage', 'popupTitle', 'popupUserData', 'popupData', 'popupTopLinks', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
/** -- menus -- **/
copyStructure('original', 'menus');
pg.structures.menus.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupUserData', 'popupData', 'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.menus.popupTopLinks = function (x, shorter) {
// FIXME maybe this stuff should be cached
var s=[];
var dropdiv='<div class="popup_drop">';
var enddiv='</div>';
var hist='<<history|shortcut=h>>';
if (!shorter) { hist = '<menurow>' + hist +
'|<<historyfeed|rss>>|<<editors|shortcut=E>></menurow>'; }
var lastedit='<<lastEdit|shortcut=/|show last edit>>';
var thank='if(diff){<<thank|send thanks>>}';
var jsHistory='<<lastContrib|last set of edits>><<sinceMe|changes since mine>>';
var linkshere='<<whatLinksHere|shortcut=l|what links here>>';
var related='<<relatedChanges|shortcut=r|related changes>>';
var search='<menurow><<search|shortcut=s>>if(wikimedia){|<<globalsearch|shortcut=g|global>>}' +
'|<<google|shortcut=G|web>></menurow>';
var watch='<menurow><<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>></menurow>';
var protect='<menurow><<unprotect|unprotectShort>>|' +
'<<protect|shortcut=p>>|<<protectlog|log>></menurow>';
var del='<menurow><<undelete|undeleteShort>>|<<delete|shortcut=d>>|' +
'<<deletelog|log>></menurow>';
var move='<<move|shortcut=m|move page>>';
var nullPurge='<menurow><<nullEdit|shortcut=n|null edit>>|<<purge|shortcut=P>></menurow>';
var viewOptions='<menurow><<view|shortcut=v>>|<<render|shortcut=S>>|<<raw>></menurow>';
var editRow='if(oldid){' +
'<menurow><<edit|shortcut=e>>|<<editOld|shortcut=e|this revision>></menurow>' +
'<menurow><<revert|shortcut=v>>|<<undo>></menurow>' + '}else{<<edit|shortcut=e>>}';
var markPatrolled='if(rcid){<<markpatrolled|mark patrolled>>}';
var newTopic='if(talk){<<new|shortcut=+|new topic>>}';
var protectDelete='if(admin){' + protect + del + '}';
if (getValueOf('popupActionsMenu')) {
s.push( '<<mainlink>>*' + dropdiv + menuTitle('tác vụ'));
} else {
s.push( dropdiv + '<<mainlink>>');
}
s.push( '<menu>');
s.push( editRow + markPatrolled + newTopic + hist + lastedit + thank );
if (!shorter) { s.push(jsHistory); }
s.push( move + linkshere + related);
if (!shorter) { s.push(nullPurge + search); }
if (!shorter) { s.push(viewOptions); }
s.push('<hr />' + watch + protectDelete);
s.push('<hr />' +
'if(talk){<<article|shortcut=a|view article>><<editArticle|edit article>>}' +
'else{<<talk|shortcut=t|talk page>><<editTalk|edit talk>>' +
'<<newTalk|shortcut=+|new topic>>}</menu>' + enddiv);
// user menu starts here
var email='<<email|shortcut=E|email user>>';
var contribs= 'if(wikimedia){<menurow>}<<contribs|shortcut=c|contributions>>if(wikimedia){</menurow>}' +
'if(admin){<menurow><<deletedContribs>></menurow>}';
s.push('if(user){*' + dropdiv + menuTitle('user'));
s.push('<menu>');
s.push('<menurow><<userPage|shortcut=u|user page>>|<<userSpace|space>></menurow>');
s.push('<<userTalk|shortcut=t|user talk>><<editUserTalk|edit user talk>>' +
'<<newUserTalk|shortcut=+|leave comment>>');
if(!shorter) { s.push( 'if(ipuser){<<arin>>}else{' + email + '}' ); }
else { s.push( 'if(ipuser){}else{' + email + '}' ); }
s.push('<hr />' + contribs + '<<userlog|shortcut=L|user log>>');
s.push('if(wikimedia){<<count|shortcut=#|edit counter>>}');
s.push('if(admin){<menurow><<unblock|unblockShort>>|<<block|shortcut=b|block user>></menurow>}');
s.push('<<blocklog|shortcut=B|block log>>');
s.push('</menu>' + enddiv + '}');
// popups menu starts here
if (getValueOf('popupSetupMenu') && !x.navpop.hasPopupMenu /* FIXME: hack */) {
x.navpop.hasPopupMenu=true;
s.push('*' + dropdiv + menuTitle('popupsMenu') + '<menu>');
s.push('<<togglePreviews|toggle previews>>');
s.push('<<purgePopups|reset>>');
s.push('<<disablePopups|disable>>');
s.push('</menu>'+enddiv);
}
return navlinkStringToHTML(s.join(''), x.article, x.params);
};
function menuTitle(s) {
return '<a href="#" noPopup=1>' + popupString(s) + '</a>';
}
pg.structures.menus.popupRedirTitle=pg.structures.menus.popupTitle;
pg.structures.menus.popupRedirTopLinks=pg.structures.menus.popupTopLinks;
copyStructure('menus', 'shortmenus');
pg.structures.shortmenus.popupTopLinks=function(x) {
return pg.structures.menus.popupTopLinks(x,true);
};
pg.structures.shortmenus.popupRedirTopLinks=pg.structures.shortmenus.popupTopLinks;
//</NOLITE>
pg.structures.lite={};
pg.structures.lite.popupLayout=function () {
return ['popupTitle', 'popupPreview' ];
};
pg.structures.lite.popupTitle=function (x) {
log (x.article + ': structures.lite.popupTitle');
//return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
return '<div><span class="popup_mainlink"><b>' + x.article.toString() + '</b></span></div>';
};
// ENDFILE: structures.js
// STARTFILE: autoedit.js
//<NOLITE>
function substitute(data,cmdBody) {
// alert('sub\nfrom: '+cmdBody.from+'\nto: '+cmdBody.to+'\nflags: '+cmdBody.flags);
var fromRe=RegExp(cmdBody.from, cmdBody.flags);
return data.replace(fromRe, cmdBody.to);
}
function execCmds(data, cmdList) {
for (var i=0; i<cmdList.length; ++i) {
data=cmdList[i].action(data, cmdList[i]);
}
return data;
}
function parseCmd(str) {
// returns a list of commands
if (!str.length) { return []; }
var p=false;
switch (str.charAt(0)) {
case 's':
p=parseSubstitute(str);
break;
default:
return false;
}
if (p) { return [p].concat(parseCmd(p.remainder)); }
return false;
}
function unEscape(str, sep) {
return str.split('\\\\').join('\\').split('\\'+sep).join(sep).split('\\n').join('\n');
}
function parseSubstitute(str) {
// takes a string like s/a/b/flags;othercmds and parses it
var from,to,flags,tmp;
if (str.length<4) { return false; }
var sep=str.charAt(1);
str=str.substring(2);
tmp=skipOver(str,sep);
if (tmp) { from=tmp.segment; str=tmp.remainder; }
else { return false; }
tmp=skipOver(str,sep);
if (tmp) { to=tmp.segment; str=tmp.remainder; }
else { return false; }
flags='';
if (str.length) {
tmp=skipOver(str,';') || skipToEnd(str, ';');
if (tmp) {flags=tmp.segment; str=tmp.remainder; }
}
return {action: substitute, from: from, to: to, flags: flags, remainder: str};
}
function skipOver(str,sep) {
var endSegment=findNext(str,sep);
if (endSegment<0) { return false; }
var segment=unEscape(str.substring(0,endSegment), sep);
return {segment: segment, remainder: str.substring(endSegment+1)};
}
/*eslint-disable*/
function skipToEnd(str,sep) {
return {segment: str, remainder: ''};
}
/*eslint-enable */
function findNext(str, ch) {
for (var i=0; i<str.length; ++i) {
if (str.charAt(i)=='\\') { i+=2; }
if (str.charAt(i)==ch) { return i; }
}
return -1;
}
function setCheckbox(param, box) {
var val=mw.util.getParamValue(param);
if (val) {
switch (val) {
case '1': case 'yes': case 'true':
box.checked=true;
break;
case '0': case 'no': case 'false':
box.checked=false;
}
}
}
function autoEdit() {
setupPopups( function () {
if (mw.util.getParamValue('autoimpl') !== popupString('autoedit_version') ) { return false; }
if (mw.util.getParamValue('autowatchlist') && mw.util.getParamValue('actoken')===autoClickToken()) {
pg.fn.modifyWatchlist(mw.util.getParamValue('title'), mw.util.getParamValue('action'));
}
if (!document.editform) { return false; }
if (autoEdit.alreadyRan) { return false; }
autoEdit.alreadyRan=true;
var cmdString=mw.util.getParamValue('autoedit');
if (cmdString) {
try {
var editbox=document.editform.wpTextbox1;
var cmdList=parseCmd(cmdString);
var input=editbox.value;
var output=execCmds(input, cmdList);
editbox.value=output;
} catch (dang) { return; }
// wikEd user script compatibility
if (typeof(wikEdUseWikEd) != 'undefined') {
if (wikEdUseWikEd === true) {
WikEdUpdateFrame();
}
}
}
setCheckbox('autominor', document.editform.wpMinoredit);
setCheckbox('autowatch', document.editform.wpWatchthis);
var rvid = mw.util.getParamValue('autorv');
if (rvid) {
var url=pg.wiki.apiwikibase + '?action=query&format=json&formatversion=2&prop=revisions&revids='+rvid;
startDownload(url, null, autoEdit2);
} else { autoEdit2(); }
} );
}
function autoEdit2(d) {
var summary=mw.util.getParamValue('autosummary');
var summaryprompt=mw.util.getParamValue('autosummaryprompt');
var summarynotice='';
if (d && d.data && mw.util.getParamValue('autorv')) {
var s = getRvSummary(summary, d.data);
if (s === false) {
summaryprompt=true;
summarynotice=popupString('Failed to get revision information, please edit manually.\n\n');
summary = simplePrintf(summary, [mw.util.getParamValue('autorv'), '(unknown)', '(unknown)']);
} else { summary = s; }
}
if (summaryprompt) {
var txt= summarynotice +
popupString('Enter a non-empty edit summary or press cancel to abort');
var response=prompt(txt, summary);
if (response) { summary=response; }
else { return; }
}
if (summary) { document.editform.wpSummary.value=summary; }
// Attempt to avoid possible premature clicking of the save button
// (maybe delays in updates to the DOM are to blame?? or a red herring)
setTimeout(autoEdit3, 100);
}
function autoClickToken() {
return mw.user.sessionId();
}
function autoEdit3() {
if( mw.util.getParamValue('actoken') != autoClickToken()) { return; }
var btn=mw.util.getParamValue('autoclick');
if (btn) {
if (document.editform && document.editform[btn]) {
var button=document.editform[btn];
var msg=tprintf('The %s button has been automatically clicked. Please wait for the next page to load.',
[ button.value ]);
bannerMessage(msg);
document.title='('+document.title+')';
button.click();
} else {
alert(tprintf('Could not find button %s. Please check the settings in your javascript file.',
[ btn ]));
}
}
}
function bannerMessage(s) {
var headings=document.getElementsByTagName('h1');
if (headings) {
var div=document.createElement('div');
div.innerHTML='<font size=+1><b>' + s + '</b></font>';
headings[0].parentNode.insertBefore(div, headings[0]);
}
}
function getRvSummary(template, json) {
try {
var o=getJsObj(json);
var edit = anyChild(o.query.pages).revisions[0];
var timestamp = edit.timestamp.split(/[A-Z]/g).join(' ').replace(/^ *| *$/g, '');
return simplePrintf(template, [edit.revid, timestamp, edit.userhidden ? '(hidden)' : edit.user ]);
} catch (badness) {
return false;
}
}
//</NOLITE>
// ENDFILE: autoedit.js
// STARTFILE: downloader.js
/**
@fileoverview
{@link Downloader}, a xmlhttprequest wrapper, and helper functions.
*/
/**
Creates a new Downloader
@constructor
@class The Downloader class. Create a new instance of this class to download stuff.
@param {String} url The url to download. This can be omitted and supplied later.
*/
function Downloader(url) {
if (typeof XMLHttpRequest!='undefined') { this.http = new XMLHttpRequest(); }
/**
The url to download
@type String
*/
this.url = url;
/**
A universally unique ID number
@type integer
*/
this.id=null;
/**
Modification date, to be culled from the incoming headers
@type Date
@private
*/
this.lastModified = null;
/**
What to do when the download completes successfully
@type Function
@private
*/
this.callbackFunction = null;
/**
What to do on failure
@type Function
@private
*/
this.onFailure = null;
/**
Flag set on <code>abort</code>
@type boolean
*/
this.aborted = false;
/**
HTTP method. See https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html for possibilities.
@type String
*/
this.method='GET';
/**
Async flag.
@type boolean
*/
this.async=true;
}
new Downloader();
/** Submits the http request. */
Downloader.prototype.send = function (x) {
if (!this.http) { return null; }
return this.http.send(x);
};
/** Aborts the download, setting the <code>aborted</code> field to true. */
Downloader.prototype.abort = function () {
if (!this.http) { return null; }
this.aborted=true;
return this.http.abort();
};
/** Returns the downloaded data. */
Downloader.prototype.getData = function () {if (!this.http) { return null; } return this.http.responseText;};
/** Prepares the download. */
Downloader.prototype.setTarget = function () {
if (!this.http) { return null; }
this.http.open(this.method, this.url, this.async);
this.http.setRequestHeader( 'Api-User-Agent', pg.misc.userAgent );
};
/** Gets the state of the download. */
Downloader.prototype.getReadyState=function () {if (!this.http) { return null; } return this.http.readyState;};
pg.misc.downloadsInProgress = { };
/** Starts the download.
Note that setTarget {@link Downloader#setTarget} must be run first
*/
Downloader.prototype.start=function () {
if (!this.http) { return; }
pg.misc.downloadsInProgress[this.id] = this;
this.http.send(null);
};
/** Gets the 'Last-Modified' date from the download headers.
Should be run after the download completes.
Returns <code>null</code> on failure.
@return {Date}
*/
Downloader.prototype.getLastModifiedDate=function () {
if(!this.http) { return null; }
var lastmod=null;
try {
lastmod=this.http.getResponseHeader('Last-Modified');
} catch (err) {}
if (lastmod) { return new Date(lastmod); }
return null;
};
/** Sets the callback function.
@param {Function} f callback function, called as <code>f(this)</code> on success
*/
Downloader.prototype.setCallback = function (f) {
if(!this.http) { return; }
this.http.onreadystatechange = f;
};
Downloader.prototype.getStatus = function() { if (!this.http) { return null; } return this.http.status; };
//////////////////////////////////////////////////
// helper functions
/** Creates a new {@link Downloader} and prepares it for action.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function newDownload(url, id, callback, onfailure) {
var d=new Downloader(url);
if (!d.http) { return 'ohdear'; }
d.id=id;
d.setTarget();
if (!onfailure) {
onfailure=2;
}
var f = function () {
if (d.getReadyState() == 4) {
delete pg.misc.downloadsInProgress[this.id];
try {
if ( d.getStatus() == 200 ) {
d.data=d.getData();
d.lastModified=d.getLastModifiedDate();
callback(d);
} else if (typeof onfailure == typeof 1) {
if (onfailure > 0) {
// retry
newDownload(url, id, callback, onfailure - 1);
}
} else if (typeof onfailure === 'function') {
onfailure(d,url,id,callback);
}
} catch (somerr) { /* ignore it */ }
}
};
d.setCallback(f);
return d;
}
/** Simulates a download from cached data.
The supplied data is put into a {@link Downloader} as if it had downloaded it.
@param {String} url The url.
@param {integer} id The ID.
@param {Function} callback The callback, which is invoked immediately as <code>callback(d)</code>,
where <code>d</code> is the new {@link Downloader}.
@param {String} data The (cached) data.
@param {Date} lastModified The (cached) last modified date.
*/
function fakeDownload(url, id, callback, data, lastModified, owner) {
var d=newDownload(url,callback);
d.owner=owner;
d.id=id; d.data=data;
d.lastModified=lastModified;
return callback(d);
}
/**
Starts a download.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function startDownload(url, id, callback) {
var d=newDownload(url, id, callback);
if (typeof d == typeof '' ) { return d; }
d.start();
return d;
}
/**
Aborts all downloads which have been started.
*/
function abortAllDownloads() {
for ( var x in pg.misc.downloadsInProgress ) {
try {
pg.misc.downloadsInProgress[x].aborted=true;
pg.misc.downloadsInProgress[x].abort();
delete pg.misc.downloadsInProgress[x];
} catch (e) {}
}
}
// ENDFILE: downloader.js
// STARTFILE: livepreview.js
// TODO: location is often not correct (eg relative links in previews)
// NOTE: removed md5 and image and math parsing. was broken, lots of bytes.
/**
* InstaView - a Mediawiki to HTML converter in JavaScript
* Version 0.6.1
* Copyright (C) Pedro Fayolle 2005-2006
* https://en.wikipedia.org/wiki/User:Pilaf
* Distributed under the BSD license
*
* Changelog:
*
* 0.6.1
* - Fixed problem caused by \r characters
* - Improved inline formatting parser
*
* 0.6
* - Changed name to InstaView
* - Some major code reorganizations and factored out some common functions
* - Handled conversion of relative links (i.e. [[/foo]])
* - Fixed misrendering of adjacent definition list items
* - Fixed bug in table headings handling
* - Changed date format in signatures to reflect Mediawiki's
* - Fixed handling of [[:Image:...]]
* - Updated MD5 function (hopefully it will work with UTF-8)
* - Fixed bug in handling of links inside images
*
* To do:
* - Better support for math tags
* - Full support for <nowiki>
* - Parser-based (as opposed to RegExp-based) inline wikicode handling (make it one-pass and bullet-proof)
* - Support for templates (through AJAX)
* - Support for coloured links (AJAX)
*/
var Insta = {};
function setupLivePreview() {
// options
Insta.conf =
{
baseUrl: '',
user: {},
wiki: {
lang: pg.wiki.lang,
interwiki: pg.wiki.interwiki,
default_thumb_width: 180
},
paths: {
articles: pg.wiki.articlePath + '/',
// Only used for Insta previews with images. (not in popups)
math: '/math/',
images: '//upload.wikimedia.org/wikipedia/en/', // FIXME getImageUrlStart(pg.wiki.hostname),
images_fallback: '//upload.wikimedia.org/wikipedia/commons/',
},
locale: {
user: mw.config.get('wgFormattedNamespaces')[pg.nsUserId],
image: mw.config.get('wgFormattedNamespaces')[pg.nsImageId],
category: mw.config.get('wgFormattedNamespaces')[pg.nsCategoryId],
// shouldn't be used in popup previews, i think
months: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
}
};
// options with default values or backreferences
Insta.conf.user.name = Insta.conf.user.name || 'Wikipedian';
Insta.conf.user.signature = '[['+Insta.conf.locale.user+':'+Insta.conf.user.name+'|'+Insta.conf.user.name+']]';
//Insta.conf.paths.images = '//upload.wikimedia.org/wikipedia/' + Insta.conf.wiki.lang + '/';
// define constants
Insta.BLOCK_IMAGE = new RegExp('^\\[\\[(?:File|Image|'+Insta.conf.locale.image+
'):.*?\\|.*?(?:frame|thumbnail|thumb|none|right|left|center)', 'i');
}
Insta.dump = function(from, to)
{
if (typeof from == 'string') { from = document.getElementById(from); }
if (typeof to == 'string') { to = document.getElementById(to); }
to.innerHTML = this.convert(from.value);
};
Insta.convert = function(wiki)
{
var ll = (typeof wiki == 'string')? wiki.replace(/\r/g,'').split(/\n/): wiki, // lines of wikicode
o = '', // output
p = 0, // para flag
$r; // result of passing a regexp to $()
// some shorthands
function remain() { return ll.length; }
function sh() { return ll.shift(); } // shift
function ps(s) { o += s; } // push
// similar to C's printf, uses ? as placeholders, ?? to escape question marks
function f()
{
var i=1, a=arguments, f=a[0], o='', c, p;
for (; i<a.length; i++) {
if ((p=f.indexOf('?'))+1) {
// allow character escaping
i -= c = f.charAt(p+1)=='?' ? 1 : 0;
o += f.substring(0,p) + (c ? '?' : a[i]);
f = f.substr(p+1+c);
} else { break; }
}
return o+f;
}
function html_entities(s) {
return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
}
// Wiki text parsing to html is a nightmare.
// The below functions deliberately don't escape the ampersand since this would make it more difficult,
// and we don't absolutely need to for how we need it.
// This means that any unescaped ampersands in wikitext will remain unescaped and can cause invalid HTML.
// Browsers should all be able to handle it though.
// We also escape significant wikimarkup characters to prevent further matching on the processed text
function htmlescape_text(s) {
return s.replace(/</g,"<").replace(/>/g,">").replace(/:/g,":").replace(/\[/g,"[").replace(/]/g,"]");
}
function htmlescape_attr(s) {
return htmlescape_text(s).replace(/'/g,"'").replace(/"/g,""");
}
// return the first non matching character position between two strings
function str_imatch(a, b)
{
for (var i=0, l=Math.min(a.length, b.length); i<l; i++) {
if (a.charAt(i)!=b.charAt(i)) { break; }
}
return i;
}
// compare current line against a string or regexp
// if passed a string it will compare only the first string.length characters
// if passed a regexp the result is stored in $r
function $(c) { return (typeof c == 'string') ? (ll[0].substr(0,c.length)==c) : ($r = ll[0].match(c)); }
function $$(c) { return ll[0]==c; } // compare current line against a string
function _(p) { return ll[0].charAt(p); } // return char at pos p
function endl(s) { ps(s); sh(); }
function parse_list()
{
var prev='';
while (remain() && $(/^([*#:;]+)(.*)$/)) {
var l_match = $r;
sh();
var ipos = str_imatch(prev, l_match[1]);
// close uncontinued lists
for (var prevPos=prev.length-1; prevPos >= ipos; prevPos--) {
var pi = prev.charAt(prevPos);
if (pi=='*') { ps('</ul>'); }
else if (pi=='#') { ps('</ol>'); }
// close a dl only if the new item is not a dl item (:, ; or empty)
else if($.inArray(l_match[1].charAt(prevPos), ['','*','#'])) { ps('</dl>'); }
}
// open new lists
for (var matchPos=ipos; matchPos<l_match[1].length; matchPos++) {
var li = l_match[1].charAt(matchPos);
if (li=='*') { ps('<ul>'); }
else if (li=='#') { ps('<ol>'); }
// open a new dl only if the prev item is not a dl item (:, ; or empty)
else if ($.inArray(prev.charAt(matchPos), ['','*','#'])) { ps('<dl>'); }
}
switch (l_match[1].charAt(l_match[1].length-1)) {
case '*': case '#':
ps('<li>' + parse_inline_nowiki(l_match[2]));
break;
case ';':
ps('<dt>');
var dt_match = l_match[2].match(/(.*?)(:.*?)$/);
// handle ;dt :dd format
if (dt_match) {
ps(parse_inline_nowiki(dt_match[1]));
ll.unshift(dt_match[2]);
} else ps(parse_inline_nowiki(l_match[2]));
break;
case ':':
ps('<dd>' + parse_inline_nowiki(l_match[2]));
}
prev=l_match[1];
}
// close remaining lists
for (var i=prev.length-1; i>=0; i--) {
ps(f('</?>', (prev.charAt(i)=='*')? 'ul': ((prev.charAt(i)=='#')? 'ol': 'dl')));
}
}
function parse_table()
{
endl(f('<table>', $(/^\{\|( .*)$/)? $r[1]: ''));
for (;remain();) if ($('|')) switch (_(1)) {
case '}':
endl('</table>');
return;
case '-':
endl(f('<tr>', $(/\|-*(.*)/)[1]));
break;
default:
parse_table_data();
}
else if ($('!')) { parse_table_data(); }
else { sh(); }
}
function parse_table_data()
{
var td_line, match_i;
// 1: "|+", '|' or '+'
// 2: ??
// 3: attributes ??
// TODO: finish commenting this regexp
var td_match = sh().match(/^(\|\+|\||!)((?:([^[|]*?)\|(?!\|))?(.*))$/);
if (td_match[1] == '|+') ps('<caption');
else ps('<t' + ((td_match[1]=='|')?'d':'h'));
if (typeof td_match[3] != 'undefined') {
//ps(' ' + td_match[3])
match_i = 4;
} else match_i = 2;
ps('>');
if (td_match[1] != '|+') {
// use || or !! as a cell separator depending on context
// NOTE: when split() is passed a regexp make sure to use non-capturing brackets
td_line = td_match[match_i].split((td_match[1] == '|')? '||': /(?:\|\||!!)/);
ps(parse_inline_nowiki(td_line.shift()));
while (td_line.length) ll.unshift(td_match[1] + td_line.pop());
} else ps(td_match[match_i]);
var tc = 0, td = [];
while (remain()) {
td.push(sh());
if ($('|')) {
if (!tc) break; // we're at the outer-most level (no nested tables), skip to td parse
else if (_(1)=='}') tc--;
}
else if (!tc && $('!')) break;
else if ($('{|')) tc++;
}
if (td.length) ps(Insta.convert(td));
}
function parse_pre()
{
ps('<pre>');
do {
endl(parse_inline_nowiki(ll[0].substring(1)) + "\n");
} while (remain() && $(' '));
ps('</pre>');
}
function parse_block_image()
{
ps(parse_image(sh()));
}
function parse_image(str)
{
//<NOLITE>
// get what's in between "[[Image:" and "]]"
var tag = str.substring(str.indexOf(':') + 1, str.length - 2);
/* eslint-disable no-unused-vars */
var width;
var attr = [], filename, caption = '';
var thumb=0, frame=0, center=0;
var align='';
/* eslint-enable no-unused-vars */
if (tag.match(/\|/)) {
// manage nested links
var nesting = 0;
var last_attr;
for (var i = tag.length-1; i > 0; i--) {
if (tag.charAt(i) == '|' && !nesting) {
last_attr = tag.substr(i+1);
tag = tag.substring(0, i);
break;
} else switch (tag.substr(i-1, 2)) {
case ']]':
nesting++;
i--;
break;
case '[[':
nesting--;
i--;
}
}
attr = tag.split(/\s*\|\s*/);
attr.push(last_attr);
filename = attr.shift();
var w_match;
for (;attr.length; attr.shift()) {
w_match = attr[0].match(/^(\d*)(?:[px]*\d*)?px$/);
if (w_match) width = w_match[1];
else switch(attr[0]) {
case 'thumb':
case 'thumbnail':
thumb=true;
frame=true;
break;
case 'frame':
frame=true;
break;
case 'none':
case 'right':
case 'left':
center=false;
align=attr[0];
break;
case 'center':
center=true;
align='none';
break;
default:
if (attr.length == 1) caption = attr[0];
}
}
} else filename = tag;
return '';
//</NOLITE>
}
function parse_inline_nowiki(str)
{
var start, lastend=0;
var substart=0, nestlev=0, open, close, subloop;
var html='';
while (-1 != (start = str.indexOf('<nowiki>', substart))) {
html += parse_inline_wiki(str.substring(lastend, start));
start += 8;
substart = start;
subloop = true;
do {
open = str.indexOf('<nowiki>', substart);
close = str.indexOf('</nowiki>', substart);
if (close<=open || open==-1) {
if (close==-1) {
return html + html_entities(str.substr(start));
}
substart = close+9;
if (nestlev) {
nestlev--;
} else {
lastend = substart;
html += html_entities(str.substring(start, lastend-9));
subloop = false;
}
} else {
substart = open+8;
nestlev++;
}
} while (subloop);
}
return html + parse_inline_wiki(str.substr(lastend));
}
function parse_inline_images(str)
{
//<NOLITE>
var start, substart=0, nestlev=0;
var loop, close, open, wiki, html;
while (-1 != (start=str.indexOf('[[', substart))) {
if(str.substr(start+2).match(RegExp('^(Image|File|' + Insta.conf.locale.image + '):','i'))) {
loop=true;
substart=start;
do {
substart+=2;
close=str.indexOf(']]',substart);
open=str.indexOf('[[',substart);
if (close<=open||open==-1) {
if (close==-1) return str;
substart=close;
if (nestlev) {
nestlev--;
} else {
wiki=str.substring(start,close+2);
html=parse_image(wiki);
str=str.replace(wiki,html);
substart=start+html.length;
loop=false;
}
} else {
substart=open;
nestlev++;
}
} while (loop);
} else break;
}
//</NOLITE>
return str;
}
// the output of this function doesn't respect the FILO structure of HTML
// but since most browsers can handle it I'll save myself the hassle
function parse_inline_formatting(str)
{
var em,st,i,li,o='';
while ((i=str.indexOf("''",li))+1) {
o += str.substring(li,i);
li=i+2;
if (str.charAt(i+2)=="'") {
li++;
st=!st;
o+=st?'<strong>':'</strong>';
} else {
em=!em;
o+=em?'<em>':'</em>';
}
}
return o+str.substr(li);
}
function parse_inline_wiki(str)
{
str = parse_inline_images(str);
str = parse_inline_formatting(str);
// math
str = str.replace(/<(?:)math>(.*?)<\/math>/ig, '');
// Build a Mediawiki-formatted date string
var date = new Date();
var minutes = date.getUTCMinutes();
if (minutes < 10) minutes = '0' + minutes;
date = f("?:?, ? ? ? (UTC)", date.getUTCHours(), minutes, date.getUTCDate(), Insta.conf.locale.months[date.getUTCMonth()], date.getUTCFullYear());
// text formatting
return str.
// signatures
replace(/~{5}(?!~)/g, date).
replace(/~{4}(?!~)/g, Insta.conf.user.name+' '+date).
replace(/~{3}(?!~)/g, Insta.conf.user.name).
// [[:Category:...]], [[:Image:...]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):[^|]*?)\\]\\](\\w*)','gi'), function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2));}).
// remove straight category and interwiki tags
replace(RegExp('\\[\\[(?:'+Insta.conf.locale.category+'|'+Insta.conf.wiki.interwiki+'):.*?\\]\\]','gi'),'').
// [[:Category:...|Links]], [[:Image:...|Links]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):.*?)\\|([^\\]]+?)\\]\\](\\w*)','gi'), function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3));}).
// [[/Relative links]]
replace(/\[\[(\/[^|]*?)\]\]/g, function($0,$1){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($1)); }).
// [[/Replaced|Relative links]]
replace(/\[\[(\/.*?)\|(.+?)\]\]/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($2)); }).
// [[Common links]]
replace(/\[\[([^[|]*?)\]\](\w*)/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2)); }).
// [[Replaced|Links]]
replace(/\[\[([^[]*?)\|([^\]]+?)\]\](\w*)/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3)); }).
// [[Stripped:Namespace|Namespace]]
replace(/\[\[([^\]]*?:)?(.*?)( *\(.*?\))?\|\]\]/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1) + htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($2)); }).
// External links
replace(/\[(https?|news|ftp|mailto|gopher|irc):(\/*)([^\]]*?) (.*?)\]/g, function($0,$1,$2,$3,$4){return f("<a class='external' href='?:?'>?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($4)); }).
replace(/\[http:\/\/(.*?)\]/g, function($0,$1){return f("<a class='external' href='http://?'>[#]</a>", htmlescape_attr($1)); }).
replace(/\[(news|ftp|mailto|gopher|irc):(\/*)(.*?)\]/g, function($0,$1,$2,$3){return f("<a class='external' href='?:?'>?:?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($1), htmlescape_text($2) + htmlescape_text($3)); }).
replace(/(^| )(https?|news|ftp|mailto|gopher|irc):(\/*)([^ $]*[^.,!?;: $])/g, function($0,$1,$2,$3,$4){return f("?<a class='external' href='?:?'>?:?</a>", htmlescape_text($1), htmlescape_attr($2), htmlescape_attr($3) + htmlescape_attr($4), htmlescape_text($2), htmlescape_text($3) + htmlescape_text($4)); }).
replace('__NOTOC__','').
replace('__NOEDITSECTION__','');
}
// begin parsing
for (;remain();) if ($(/^(={1,6})(.*)\1(.*)$/)) {
p=0;
endl(f('<h?>?</h?>?', $r[1].length, parse_inline_nowiki($r[2]), $r[1].length, $r[3]));
} else if ($(/^[*#:;]/)) {
p=0;
parse_list();
} else if ($(' ')) {
p=0;
parse_pre();
} else if ($('{|')) {
p=0;
parse_table();
} else if ($(/^----+$/)) {
p=0;
endl('<hr />');
} else if ($(Insta.BLOCK_IMAGE)) {
p=0;
parse_block_image();
} else {
// handle paragraphs
if ($$('')) {
p = (remain()>1 && ll[1]===(''));
if (p) endl('<p><br>');
} else {
if(!p) {
ps('<p>');
p=1;
}
ps(parse_inline_nowiki(ll[0]) + ' ');
}
sh();
}
return o;
};
function wiki2html(txt,baseurl) {
Insta.conf.baseUrl=baseurl;
return Insta.convert(txt);
}
// ENDFILE: livepreview.js
// STARTFILE: pageinfo.js
//<NOLITE>
function popupFilterPageSize(data) {
return formatBytes(data.length);
}
function popupFilterCountLinks(data) {
var num=countLinks(data);
return String(num) + ' ' + ((num!=1)?popupString('wikiLinks'):popupString('wikiLink'));
}
function popupFilterCountImages(data) {
var num=countImages(data);
return String(num) + ' ' + ((num!=1)?popupString('images'):popupString('image'));
}
function popupFilterCountCategories(data) {
var num=countCategories(data);
return String(num) + ' ' + ((num!=1)?popupString('categories'):popupString('category'));
}
function popupFilterLastModified(data,download) {
var lastmod=download.lastModified;
var now=new Date();
var age=now-lastmod;
if (lastmod && getValueOf('popupLastModified')) {
return (tprintf('%s old', [formatAge(age)])).replace(RegExp(' ','g'), ' ');
}
return '';
}
function formatAge(age) {
// coerce into a number
var a=0+age, aa=a;
var seclen = 1000;
var minlen = 60*seclen;
var hourlen = 60*minlen;
var daylen = 24*hourlen;
var weeklen = 7*daylen;
var numweeks = (a-a%weeklen)/weeklen; a = a-numweeks*weeklen; var sweeks = addunit(numweeks, 'week');
var numdays = (a-a%daylen)/daylen; a = a-numdays*daylen; var sdays = addunit(numdays, 'day');
var numhours = (a-a%hourlen)/hourlen; a = a-numhours*hourlen; var shours = addunit(numhours,'hour');
var nummins = (a-a%minlen)/minlen; a = a-nummins*minlen; var smins = addunit(nummins, 'minute');
var numsecs = (a-a%seclen)/seclen; a = a-numsecs*seclen; var ssecs = addunit(numsecs, 'second');
if (aa > 4*weeklen) { return sweeks; }
if (aa > weeklen) { return sweeks + ' ' + sdays; }
if (aa > daylen) { return sdays + ' ' + shours; }
if (aa > 6*hourlen) { return shours; }
if (aa > hourlen) { return shours + ' ' + smins; }
if (aa > 10*minlen) { return smins; }
if (aa > minlen) { return smins + ' ' + ssecs; }
return ssecs;
}
function addunit(num,str) { return '' + num + ' ' + ((num!=1) ? popupString(str+'s') : popupString(str)) ;}
function runPopupFilters(list, data, download) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (list[i] && typeof list[i] == 'function') {
var s=list[i](data, download, download.owner.article);
if (s) { ret.push(s); }
}
}
return ret;
}
function getPageInfo(data, download) {
if (!data || data.length === 0) { return popupString('Empty page'); }
var popupFilters=getValueOf('popupFilters') || [];
var extraPopupFilters = getValueOf('extraPopupFilters') || [];
var pageInfoArray = runPopupFilters(popupFilters.concat(extraPopupFilters), data, download);
var pageInfo=pageInfoArray.join(', ');
if (pageInfo !== '' ) { pageInfo = upcaseFirst(pageInfo); }
return pageInfo;
}
// this could be improved!
function countLinks(wikiText) { return wikiText.split('[[').length - 1; }
// if N = # matches, n = # brackets, then
// String.parenSplit(regex) intersperses the N+1 split elements
// with Nn other elements. So total length is
// L= N+1 + Nn = N(n+1)+1. So N=(L-1)/(n+1).
function countImages(wikiText) {
return (wikiText.parenSplit(pg.re.image).length - 1) / (pg.re.imageBracketCount + 1);
}
function countCategories(wikiText) {
return (wikiText.parenSplit(pg.re.category).length - 1) / (pg.re.categoryBracketCount + 1);
}
function popupFilterStubDetect(data, download, article) {
var counts=stubCount(data, article);
if (counts.real) { return popupString('stub'); }
if (counts.sect) { return popupString('section stub'); }
return '';
}
function popupFilterDisambigDetect(data, download, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return ''; }
return (isDisambig(data, article)) ? popupString('disambig') : '';
}
function formatBytes(num) {
return (num > 949) ? (Math.round(num/100)/10+popupString('kB')) : (num +' ' + popupString('bytes')) ;
}
//</NOLITE>
// ENDFILE: pageinfo.js
// STARTFILE: titles.js
/**
@fileoverview Defines the {@link Title} class, and associated crufty functions.
<code>Title</code> deals with article titles and their various
forms. {@link Stringwrapper} is the parent class of
<code>Title</code>, which exists simply to make things a little
neater.
*/
/**
Creates a new Stringwrapper.
@constructor
@class the Stringwrapper class. This base class is not really
useful on its own; it just wraps various common string operations.
*/
function Stringwrapper() {
/**
Wrapper for this.toString().indexOf()
@param {String} x
@type integer
*/
this.indexOf=function(x){return this.toString().indexOf(x);};
/**
Returns this.value.
@type String
*/
this.toString=function(){return this.value;};
/**
Wrapper for {@link String#parenSplit} applied to this.toString()
@param {RegExp} x
@type Array
*/
this.parenSplit=function(x){return this.toString().parenSplit(x);};
/**
Wrapper for this.toString().substring()
@param {String} x
@param {String} y (optional)
@type String
*/
this.substring=function(x,y){
if (typeof y=='undefined') { return this.toString().substring(x); }
return this.toString().substring(x,y);
};
/**
Wrapper for this.toString().split()
@param {String} x
@type Array
*/
this.split=function(x){return this.toString().split(x);};
/**
Wrapper for this.toString().replace()
@param {String} x
@param {String} y
@type String
*/
this.replace=function(x,y){ return this.toString().replace(x,y); };
}
/**
Creates a new <code>Title</code>.
@constructor
@class The Title class. Holds article titles and converts them into
various forms. Also deals with anchors, by which we mean the bits
of the article URL after a # character, representing locations
within an article.
@param {String} value The initial value to assign to the
article. This must be the canonical title (see {@link
Title#value}. Omit this in the constructor and use another function
to set the title if this is unavailable.
*/
function Title(val) {
/**
The canonical article title. This must be in UTF-8 with no
entities, escaping or nasties. Also, underscores should be
replaced with spaces.
@type String
@private
*/
this.value=null;
/**
The canonical form of the anchor. This should be exactly as
it appears in the URL, i.e. with the .C3.0A bits in.
@type String
*/
this.anchor='';
this.setUtf(val);
}
Title.prototype=new Stringwrapper();
/**
Returns the canonical representation of the article title, optionally without anchor.
@param {boolean} omitAnchor
@fixme Decide specs for anchor
@return String The article title and the anchor.
*/
Title.prototype.toString=function(omitAnchor) {
return this.value + ( (!omitAnchor && this.anchor) ? '#' + this.anchorString() : '' );
};
Title.prototype.anchorString=function() {
if (!this.anchor) { return ''; }
var split=this.anchor.parenSplit(/((?:[.][0-9A-F]{2})+)/);
var len=split.length;
for (var j=1; j<len; j+=2) {
// FIXME s/decodeURI/decodeURIComponent/g ?
split[j]=decodeURIComponent(split[j].split('.').join('%')).split('_').join(' ');
}
return split.join('');
};
Title.prototype.urlAnchor=function() {
var split=this.anchor.parenSplit('/((?:[%][0-9A-F]{2})+)/');
var len=split.length;
for (var j=1; j<len; j+=2) {
split[j]=split[j].split('%').join('.');
}
return split.join('');
};
Title.prototype.anchorFromUtf=function(str) {
this.anchor=encodeURIComponent(str.split(' ').join('_'))
.split('%3A').join(':').split("'").join('%27').split('%').join('.');
};
Title.fromURL=function(h) {
return new Title().fromURL(h);
};
Title.prototype.fromURL=function(h) {
if (typeof h != 'string') {
this.value=null;
return this;
}
// NOTE : playing with decodeURI, encodeURI, escape, unescape,
// we seem to be able to replicate the IE borked encoding
// IE doesn't do this new-fangled utf-8 thing.
// and it's worse than that.
// IE seems to treat the query string differently to the rest of the url
// the query is treated as bona-fide utf8, but the first bit of the url is pissed around with
// we fix up & for all browsers, just in case.
var splitted=h.split('?');
splitted[0]=splitted[0].split('&').join('%26');
h=splitted.join('?');
var contribs=pg.re.contribs.exec(h);
if (contribs) {
if (contribs[1]=='title=') { contribs[3]=contribs[3].split('+').join(' '); }
var u=new Title(contribs[3]);
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + u.stripNamespace()));
return this;
}
var email=pg.re.email.exec(h);
if (email) {
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(email[3]).stripNamespace()));
return this;
}
var backlinks=pg.re.backlinks.exec(h);
if (backlinks) {
this.setUtf(this.decodeNasties(new Title(backlinks[3])));
return this;
}
//A dummy title object for a Special:Diff link.
var specialdiff=pg.re.specialdiff.exec(h);
if (specialdiff) {
this.setUtf(this.decodeNasties(new Title(mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Diff')));
return this;
}
// no more special cases to check --
// hopefully it's not a disguised user-related or specially treated special page
var m=pg.re.main.exec(h);
if(m === null) { this.value=null; }
else {
var fromBotInterface = /[?](.+[&])?title=/.test(h);
if (fromBotInterface) {
m[2]=m[2].split('+').join('_');
}
var extracted = m[2] + (m[3] ? '#' + m[3] : '');
if (pg.flag.isSafari && /%25[0-9A-Fa-f]{2}/.test(extracted)) {
// Fix Safari issue
// Safari sometimes encodes % as %25 in UTF-8 encoded strings like %E5%A3 -> %25E5%25A3.
this.setUtf(decodeURIComponent(unescape(extracted)));
} else {
this.setUtf(this.decodeNasties(extracted));
}
}
return this;
};
Title.prototype.decodeNasties=function(txt) {
var ret= this.decodeEscapes(decodeURI(txt));
ret = ret.replace(/[_ ]*$/, '');
return ret;
};
Title.prototype.decodeEscapes=function(txt) {
var split=txt.parenSplit(/((?:[%][0-9A-Fa-f]{2})+)/);
var len=split.length;
for (var i=1; i<len; i=i+2) {
// FIXME is decodeURIComponent better?
split[i]=unescape(split[i]);
}
return split.join('');
};
Title.fromAnchor=function(a) {
return new Title().fromAnchor(a);
};
Title.prototype.fromAnchor=function(a) {
if (!a) { this.value=null; return this; }
return this.fromURL(a.href);
};
Title.fromWikiText=function(txt) {
return new Title().fromWikiText(txt);
};
Title.prototype.fromWikiText=function(txt) {
// FIXME - testing needed
txt=myDecodeURI(txt);
this.setUtf(txt);
return this;
};
Title.prototype.hintValue=function(){
if(!this.value) { return ''; }
return safeDecodeURI(this.value);
};
//<NOLITE>
Title.prototype.toUserName=function(withNs) {
if (this.namespaceId() != pg.nsUserId && this.namespaceId() != pg.nsUsertalkId) {
this.value=null;
return;
}
this.value = (withNs ? mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' : '') + this.stripNamespace().split('/')[0];
};
Title.prototype.userName=function(withNs) {
var t=(new Title(this.value));
t.toUserName(withNs);
if (t.value) { return t; }
return null;
};
Title.prototype.toTalkPage=function() {
// convert article to a talk page, or if we can't, return null
// In other words: return null if this ALREADY IS a talk page
// and return the corresponding talk page otherwise
//
// Per https://www.mediawiki.org/wiki/Manual:Namespace#Subject_and_talk_namespaces
// * All discussion namespaces have odd-integer indices
// * The discussion namespace index for a specific namespace with index n is n + 1
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId>=0 && namespaceId % 2 === 0) //non-special and subject namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId+1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
//</NOLITE>
// Return canonical, localized namespace
Title.prototype.namespace=function() {
return mw.config.get('wgFormattedNamespaces')[this.namespaceId()];
};
Title.prototype.namespaceId=function() {
var n=this.value.indexOf(':');
if (n<0) { return 0; } //mainspace
var namespaceId = mw.config.get('wgNamespaceIds')[this.value.substring(0,n).split(' ').join('_').toLowerCase()];
if (typeof namespaceId=='undefined') return 0; //mainspace
return namespaceId;
};
//<NOLITE>
Title.prototype.talkPage=function() {
var t=new Title(this.value);
t.toTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.isTalkPage=function() {
if (this.talkPage()===null) { return true; }
return false;
};
Title.prototype.toArticleFromTalkPage=function() {
//largely copy/paste from toTalkPage above.
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId >= 0 && namespaceId % 2 == 1) //non-special and talk namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId-1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
Title.prototype.articleFromTalkPage=function() {
var t=new Title(this.value);
t.toArticleFromTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.articleFromTalkOrArticle=function() {
var t=new Title(this.value);
if ( t.toArticleFromTalkPage() ) { return t; }
return this;
};
Title.prototype.isIpUser=function() {
return pg.re.ipUser.test(this.userName());
};
//</NOLITE>
Title.prototype.stripNamespace=function(){ // returns a string, not a Title
var n=this.value.indexOf(':');
if (n<0) { return this.value; }
var namespaceId = this.namespaceId();
if (namespaceId === pg.nsMainspaceId) return this.value;
return this.value.substring(n+1);
};
Title.prototype.setUtf=function(value){
if (!value) { this.value=''; return; }
var anch=value.indexOf('#');
if(anch < 0) { this.value=value.split('_').join(' '); this.anchor=''; return; }
this.value=value.substring(0,anch).split('_').join(' ');
this.anchor=value.substring(anch+1);
this.ns=null; // wait until namespace() is called
};
Title.prototype.setUrl=function(urlfrag) {
var anch=urlfrag.indexOf('#');
this.value=safeDecodeURI(urlfrag.substring(0,anch));
this.anchor=this.value.substring(anch+1);
};
Title.prototype.append=function(x){
this.setUtf(this.value + x);
};
Title.prototype.urlString=function(x) {
if(!x) { x={}; }
var v=this.toString(true);
if (!x.omitAnchor && this.anchor) { v+= '#' + this.urlAnchor(); }
if (!x.keepSpaces) { v=v.split(' ').join('_'); }
return encodeURI(v).split('&').join('%26').split('?').join('%3F').split('+').join('%2B');
};
Title.prototype.removeAnchor=function() {
return new Title(this.toString(true));
};
Title.prototype.toUrl=function() {
return pg.wiki.titlebase + this.urlString();
};
function parseParams(url) {
var specialDiff = pg.re.specialdiff.exec(url);
if (specialDiff)
{
var split= specialDiff[1].split('/');
if (split.length==1) return {oldid:split[0], diff: 'prev'};
else if (split.length==2) return {oldid: split[0], diff: split[1]};
}
var ret={};
if (url.indexOf('?')==-1) { return ret; }
url = url.split('#')[0];
var s=url.split('?').slice(1).join();
var t=s.split('&');
for (var i=0; i<t.length; ++i) {
var z=t[i].split('=');
z.push(null);
ret[z[0]]=z[1];
}
//Diff revision with no oldid is interpreted as a diff to the previous revision by MediaWiki
if (ret.diff && typeof(ret.oldid)==='undefined')
{
ret.oldid = "prev";
}
//Documentation seems to say something different, but oldid can also accept prev/next, and Echo is emitting such URLs. Simple fixup during parameter decoding:
if (ret.oldid && (ret.oldid==='prev' || ret.oldid==='next' || ret.oldid==='cur'))
{
var helper = ret.diff;
ret.diff = ret.oldid;
ret.oldid = helper;
}
return ret;
}
// (a) myDecodeURI (first standard decodeURI, then pg.re.urlNoPopup)
// (b) change spaces to underscores
// (c) encodeURI (just the straight one, no pg.re.urlNoPopup)
function myDecodeURI (str) {
var ret;
// FIXME decodeURIComponent??
try { ret=decodeURI(str.toString()); }
catch (summat) { return str; }
for (var i=0; i<pg.misc.decodeExtras.length; ++i) {
var from=pg.misc.decodeExtras[i].from;
var to=pg.misc.decodeExtras[i].to;
ret=ret.split(from).join(to);
}
return ret;
}
function safeDecodeURI(str) { var ret=myDecodeURI(str); return ret || str; }
///////////
// TESTS //
///////////
//<NOLITE>
function isDisambig(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
return ! article.isTalkPage() && pg.re.disambig.test(data);
}
function stubCount(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
var sectStub=0;
var realStub=0;
if (pg.re.stub.test(data)) {
var s=data.parenSplit(pg.re.stub);
for (var i=1; i<s.length; i=i+2) {
if (s[i]) { ++sectStub; }
else { ++realStub; }
}
}
return { real: realStub, sect: sectStub };
}
function isValidImageName(str){ // extend as needed...
return ( str.indexOf('{') == -1 );
}
function isInStrippableNamespace(article) {
// Does the namespace allow subpages
// Note, would be better if we had access to wgNamespacesWithSubpages
return ( article.namespaceId() !== 0 );
}
function isInMainNamespace(article) { return article.namespaceId() === 0; }
function anchorContainsImage(a) {
// iterate over children of anchor a
// see if any are images
if (a === null) { return false; }
var kids=a.childNodes;
for (var i=0; i<kids.length; ++i) { if (kids[i].nodeName=='IMG') { return true; } }
return false;
}
//</NOLITE>
function isPopupLink(a) {
// NB for performance reasons, TOC links generally return true
// they should be stripped out later
if (!markNopopupSpanLinks.done) { markNopopupSpanLinks(); }
if (a.inNopopupSpan) { return false; }
// FIXME is this faster inline?
if (a.onmousedown || a.getAttribute('nopopup')) { return false; }
var h=a.href;
if (h === document.location.href+'#') { return false; }
if (!pg.re.basenames.test(h)) { return false; }
if (!pg.re.urlNoPopup.test(h)) { return true; }
return (
(pg.re.email.test(h) || pg.re.contribs.test(h) || pg.re.backlinks.test(h) || pg.re.specialdiff.test(h)) &&
h.indexOf('&limit=') == -1 );
}
function markNopopupSpanLinks() {
if( !getValueOf('popupOnlyArticleLinks'))
fixVectorMenuPopups();
var s = $('.nopopups').toArray();
for (var i=0; i<s.length; ++i) {
var as=s[i].getElementsByTagName('a');
for (var j=0; j<as.length; ++j) {
as[j].inNopopupSpan=true;
}
}
markNopopupSpanLinks.done=true;
}
function fixVectorMenuPopups() {
$('div.vectorMenu h3:first a:first').prop('inNopopupSpan', true);
}
// ENDFILE: titles.js
// STARTFILE: getpage.js
//////////////////////////////////////////////////
// Wiki-specific downloading
//
// Schematic for a getWiki call
//
// getPageWithCaching
// |
// false | true
// getPage<-[findPictureInCache]->-onComplete(a fake download)
// \.
// (async)->addPageToCache(download)->-onComplete(download)
// check cache to see if page exists
function getPageWithCaching(url, onComplete, owner) {
log('getPageWithCaching, url='+url);
var i=findInPageCache(url);
var d;
if (i > -1) {
d=fakeDownload(url, owner.idNumber, onComplete,
pg.cache.pages[i].data, pg.cache.pages[i].lastModified,
owner);
} else {
d=getPage(url, onComplete, owner);
if (d && owner && owner.addDownload) {
owner.addDownload(d);
d.owner=owner;
}
}
}
function getPage(url, onComplete, owner) {
log('getPage');
var callback= function (d) { if (!d.aborted) {addPageToCache(d); onComplete(d);} };
return startDownload(url, owner.idNumber, callback);
}
function findInPageCache(url) {
for (var i=0; i<pg.cache.pages.length; ++i) {
if (url==pg.cache.pages[i].url) { return i; }
}
return -1;
}
function addPageToCache(download) {
log('addPageToCache '+download.url);
var page = {url: download.url, data: download.data, lastModified: download.lastModified};
return pg.cache.pages.push(page);
}
// ENDFILE: getpage.js
// STARTFILE: parensplit.js
//////////////////////////////////////////////////
// parenSplit
// String.prototype.parenSplit should do what ECMAscript says String.prototype.split does,
// interspersing paren matches (regex capturing groups) between the split elements.
// i.e. 'abc'.split(/(b)/)) should return ['a','b','c'], not ['a','c']
if (String('abc'.split(/(b)/))!='a,b,c') {
// broken String.split, e.g. konq, IE < 10
String.prototype.parenSplit=function (re) {
re=nonGlobalRegex(re);
var s=this;
var m=re.exec(s);
var ret=[];
while (m && s) {
// without the following loop, we have
// 'ab'.parenSplit(/a|(b)/) != 'ab'.split(/a|(b)/)
for(var i=0; i<m.length; ++i) {
if (typeof m[i]=='undefined') m[i]='';
}
ret.push(s.substring(0,m.index));
ret = ret.concat(m.slice(1));
s=s.substring(m.index + m[0].length);
m=re.exec(s);
}
ret.push(s);
return ret;
};
} else {
String.prototype.parenSplit=function (re) { return this.split(re); };
String.prototype.parenSplit.isNative=true;
}
function nonGlobalRegex(re) {
var s=re.toString();
var flags='';
for (var j=s.length; s.charAt(j) != '/'; --j) {
if (s.charAt(j) != 'g') { flags += s.charAt(j); }
}
var t=s.substring(1,j);
return RegExp(t,flags);
}
// ENDFILE: parensplit.js
// STARTFILE: tools.js
// IE madness with encoding
// ========================
//
// suppose throughout that the page is in utf8, like wikipedia
//
// if a is an anchor DOM element and a.href should consist of
//
// http://host.name.here/wiki/foo?bar=baz
//
// then IE gives foo as "latin1-encoded" utf8; we have foo = decode_utf8(decodeURI(foo_ie))
// but IE gives bar=baz correctly as plain utf8
//
// ---------------------------------
//
// IE's xmlhttp doesn't understand utf8 urls. Have to use encodeURI here.
//
// ---------------------------------
//
// summat else
// Source: http://aktuell.de.selfhtml.org/artikel/javascript/utf8b64/utf8.htm
//<NOLITE>
function getJsObj(json) {
try {
var json_ret = JSON.parse(json);
if( json_ret.warnings ) {
for( var w=0; w < json_ret.warnings.length; w++ ) {
if( json_ret.warnings[w]['*'] ) {
log( json_ret.warnings[w]['*'] );
} else {
log( json_ret.warnings[w]['warnings'] );
}
}
} else if ( json_ret.error ) {
errlog( json_ret.error.code + ': ' + json_ret.error.info );
}
return json_ret;
} catch (someError) {
errlog('Something went wrong with getJsObj, json='+json);
return 1;
}
}
function anyChild(obj) {
for (var p in obj) {
return obj[p];
}
return null;
}
//</NOLITE>
function upcaseFirst(str) {
if (typeof str != typeof '' || str === '') return '';
return str.charAt(0).toUpperCase() + str.substring(1);
}
function findInArray(arr, foo) {
if (!arr || !arr.length) { return -1; }
var len=arr.length;
for (var i=0; i<len; ++i) { if (arr[i]==foo) { return i; } }
return -1;
}
/* eslint-disable no-unused-vars */
function nextOne (array, value) {
// NB if the array has two consecutive entries equal
// then this will loop on successive calls
var i=findInArray(array, value);
if (i<0) { return null; }
return array[i+1];
}
/* eslint-enable no-unused-vars */
function literalizeRegex(str){
return mw.util.escapeRegExp(str);
}
String.prototype.entify=function() {
//var shy='­';
return this.split('&').join('&').split('<').join('<').split('>').join('>'/*+shy*/).split('"').join('"');
};
// Array filter function
function removeNulls(val) { return val !== null; }
function joinPath(list) {
return list.filter(removeNulls).join('/');
}
function simplePrintf(str, subs) {
if (!str || !subs) { return str; }
var ret=[];
var s=str.parenSplit(/(%s|\$[0-9]+)/);
var i=0;
do {
ret.push(s.shift());
if ( !s.length ) { break; }
var cmd=s.shift();
if (cmd == '%s') {
if ( i < subs.length ) { ret.push(subs[i]); } else { ret.push(cmd); }
++i;
} else {
var j=parseInt( cmd.replace('$', ''), 10 ) - 1;
if ( j > -1 && j < subs.length ) { ret.push(subs[j]); } else { ret.push(cmd); }
}
} while (s.length > 0);
return ret.join('');
}
/* eslint-disable no-unused-vars */
function isString(x) { return (typeof x === 'string' || x instanceof String); }
function isNumber(x) { return (typeof x === 'number' || x instanceof Number); }
function isRegExp(x) { return x instanceof RegExp; }
function isArray (x) { return x instanceof Array; }
function isObject(x) { return x instanceof Object; }
function isFunction(x) {
return !isRegExp(x) && (typeof x === 'function' || x instanceof Function);
}
/* eslint-enable no-unused-vars */
function repeatString(s,mult) {
var ret='';
for (var i=0; i<mult; ++i) { ret += s; }
return ret;
}
function zeroFill(s, min) {
min = min || 2;
var t=s.toString();
return repeatString('0', min - t.length) + t;
}
function map(f, o) {
if (isArray(o)) { return map_array(f,o); }
return map_object(f,o);
}
function map_array(f,o) {
var ret=[];
for (var i=0; i<o.length; ++i) {
ret.push(f(o[i]));
}
return ret;
}
function map_object(f,o) {
var ret={};
for (var i in o) { ret[o]=f(o[i]); }
return ret;
}
pg.escapeQuotesHTML = function ( text ) {
return text
.replace(/&/g, "&")
.replace(/"/g, """)
.replace(/</g, "<")
.replace(/>/g, ">");
};
// ENDFILE: tools.js
// STARTFILE: dab.js
//<NOLITE>
//////////////////////////////////////////////////
// Dab-fixing code
//
function retargetDab(newTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) {
log('retargetDab: newTarget='+newTarget + ' oldTarget=' + oldTarget);
return changeLinkTargetLink(
{newTarget: newTarget,
text: newTarget.split(' ').join(' '),
hint: tprintf('disambigHint', [newTarget]),
summary: simplePrintf(
getValueOf('popupFixDabsSummary'), [friendlyCurrentArticleName, newTarget ]),
clickButton: getValueOf('popupDabsAutoClick'), minor: true, oldTarget: oldTarget,
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit});
}
function listLinks(wikitext, oldTarget, titleToEdit) {
// mediawiki strips trailing spaces, so we do the same
// testcase: https://en.wikipedia.org/w/index.php?title=Radial&oldid=97365633
var reg=RegExp('\\[\\[([^|]*?) *(\\||\\]\\])', 'gi');
var ret=[];
var splitted=wikitext.parenSplit(reg);
// ^[a-z]+ should match interwiki links, hopefully (case-insensitive)
// and ^[a-z]* should match those and [[:Category...]] style links too
var omitRegex=RegExp('^[a-z]*:|^[Ss]pecial:|^[Ii]mage|^[Cc]ategory');
var friendlyCurrentArticleName= oldTarget.toString();
var wikPos = getValueOf('popupDabWiktionary');
for (var i=1; i<splitted.length; i=i+3) {
if (typeof splitted[i] == typeof 'string' && splitted[i].length>0 && !omitRegex.test(splitted[i])) {
ret.push( retargetDab(splitted[i], oldTarget, friendlyCurrentArticleName, titleToEdit) );
} /* if */
} /* for loop */
ret = rmDupesFromSortedList(ret.sort());
if (wikPos) {
var wikTarget='wiktionary:' +
friendlyCurrentArticleName.replace( RegExp('^(.+)\\s+[(][^)]+[)]\\s*$'), '$1' );
var meth;
if (wikPos.toLowerCase() == 'first') { meth = 'unshift'; }
else { meth = 'push'; }
ret[meth]( retargetDab(wikTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) );
}
ret.push(changeLinkTargetLink(
{ newTarget: null,
text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this disambig page from this article"),
clickButton: getValueOf('popupDabsAutoClick'), oldTarget: oldTarget,
summary: simplePrintf(getValueOf('popupRmDabLinkSummary'), [friendlyCurrentArticleName]),
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit
}));
return ret;
}
function rmDupesFromSortedList(list) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (ret.length === 0 || list[i]!=ret[ret.length-1]) { ret.push(list[i]); }
}
return ret;
}
function makeFixDab(data, navpop) {
// grab title from parent popup if there is one; default exists in changeLinkTargetLink
var titleToEdit=(navpop.parentPopup && navpop.parentPopup.article.toString());
var list=listLinks(data, navpop.originalArticle, titleToEdit);
if (list.length === 0) { log('listLinks returned empty list'); return null; }
var html='<hr />' + popupString('Click to disambiguate this link to:') + '<br>';
html+=list.join(', ');
return html;
}
function makeFixDabs(wikiText, navpop) {
if (getValueOf('popupFixDabs') && isDisambig(wikiText, navpop.article) &&
Title.fromURL(location.href).namespaceId() != pg.nsSpecialId &&
navpop.article.talkPage() ) {
setPopupHTML(makeFixDab(wikiText, navpop), 'popupFixDab', navpop.idNumber);
}
}
function popupRedlinkHTML(article) {
return changeLinkTargetLink(
{ newTarget: null, text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this page from this article"),
clickButton: getValueOf('popupRedlinkAutoClick'),
oldTarget: article.toString(),
summary: simplePrintf(getValueOf('popupRedlinkSummary'), [article.toString()])});
}
//</NOLITE>
// ENDFILE: dab.js
// STARTFILE: htmloutput.js
// this has to use a timer loop as we don't know if the DOM element exists when we want to set the text
function setPopupHTML (str, elementId, popupId, onSuccess, append) {
if (typeof popupId === 'undefined') {
//console.error('popupId is not defined in setPopupHTML, html='+str.substring(0,100));
popupId = pg.idNumber;
}
var popupElement=document.getElementById(elementId+popupId);
if (popupElement) {
if (!append) { popupElement.innerHTML=''; }
if (isString(str)) {
popupElement.innerHTML+=str;
} else {
popupElement.appendChild(str);
}
if (onSuccess) { onSuccess(); }
setTimeout(checkPopupPosition, 100);
return true;
} else {
// call this function again in a little while...
setTimeout(function(){
setPopupHTML(str,elementId,popupId,onSuccess);
}, 600);
}
return null;
}
//<NOLITE>
function setPopupTrailer(str,id) {return setPopupHTML(str, 'popupData', id);}
//</NOLITE>
// args.navpopup is mandatory
// optional: args.redir, args.redirTarget
// FIXME: ye gods, this is ugly stuff
function fillEmptySpans(args) {
// if redir is present and true then redirTarget is mandatory
var redir=true;
var rcid;
if (typeof args != 'object' || typeof args.redir == 'undefined' || !args.redir) { redir=false; }
var a=args.navpopup.parentAnchor;
var article, hint=null, oldid=null, params={};
if (redir && typeof args.redirTarget == typeof {}) {
article=args.redirTarget;
//hint=article.hintValue();
} else {
article=(new Title()).fromAnchor(a);
hint=a.originalTitle || article.hintValue();
params=parseParams(a.href);
oldid=(getValueOf('popupHistoricalLinks')) ? params.oldid : null;
rcid=params.rcid;
}
var x={ article:article, hint: hint, oldid: oldid, rcid: rcid, navpop:args.navpopup, params:params };
var structure=pg.structures[getValueOf('popupStructure')];
if (typeof structure != 'object') {
setPopupHTML('popupError', 'Unknown structure (this should never happen): '+
pg.option.popupStructure, args.navpopup.idNumber);
return;
}
var spans=flatten(pg.misc.layout);
var numspans = spans.length;
var redirs=pg.misc.redirSpans;
for (var i=0; i<numspans; ++i) {
var found = redirs && (redirs.indexOf( spans[i] ) !== -1);
//log('redir='+redir+', found='+found+', spans[i]='+spans[i]);
if ( (found && !redir) || (!found && redir) ) {
//log('skipping this set of the loop');
continue;
}
var structurefn=structure[spans[i]];
var setfn = setPopupHTML;
if (getValueOf('popupActiveNavlinks') &&
(spans[i].indexOf('popupTopLinks')===0 || spans[i].indexOf('popupRedirTopLinks')===0)
) {
setfn = setPopupTipsAndHTML;
}
switch (typeof structurefn) {
case 'function':
log('running '+spans[i]+'({article:'+x.article+', hint:'+x.hint+', oldid: '+x.oldid+'})');
setfn(structurefn(x), spans[i], args.navpopup.idNumber);
break;
case 'string':
setfn(structurefn, spans[i], args.navpopup.idNumber);
break;
default:
errlog('unknown thing with label '+spans[i] + ' (span index was ' + i + ')');
break;
}
}
}
// flatten an array
function flatten(list, start) {
var ret=[];
if (typeof start == 'undefined') { start=0; }
for (var i=start; i<list.length; ++i) {
if (typeof list[i] == typeof []) {
return ret.concat(flatten(list[i])).concat(flatten(list, i+1));
}
else { ret.push(list[i]); }
}
return ret;
}
// Generate html for whole popup
function popupHTML (a) {
getValueOf('popupStructure');
var structure=pg.structures[pg.option.popupStructure];
if (typeof structure != 'object') {
//return 'Unknown structure: '+pg.option.popupStructure;
// override user choice
pg.option.popupStructure=pg.optionDefault.popupStructure;
return popupHTML(a);
}
if (typeof structure.popupLayout != 'function') { return 'Bad layout'; }
pg.misc.layout=structure.popupLayout();
if (typeof structure.popupRedirSpans === 'function') { pg.misc.redirSpans=structure.popupRedirSpans(); }
else { pg.misc.redirSpans=[]; }
return makeEmptySpans(pg.misc.layout, a.navpopup);
}
function makeEmptySpans (list, navpop) {
var ret='';
for (var i=0; i<list.length; ++i) {
if (typeof list[i] == typeof '') {
ret += emptySpanHTML(list[i], navpop.idNumber, 'div');
} else if (typeof list[i] == typeof [] && list[i].length > 0 ) {
ret = ret.parenSplit(RegExp('(</[^>]*?>$)')).join(makeEmptySpans(list[i], navpop));
} else if (typeof list[i] == typeof {} && list[i].nodeType ) {
ret += emptySpanHTML(list[i].name, navpop.idNumber, list[i].nodeType);
}
}
return ret;
}
function emptySpanHTML(name, id, tag, classname) {
tag = tag || 'span';
if (!classname) { classname = emptySpanHTML.classAliases[name]; }
classname = classname || name;
if (name == getValueOf('popupDragHandle')) { classname += ' popupDragHandle'; }
return simplePrintf('<%s id="%s" class="%s"></%s>', [tag, name + id, classname, tag]);
}
emptySpanHTML.classAliases={ 'popupSecondPreview': 'popupPreview' };
// generate html for popup image
// <a id="popupImageLinkn"><img id="popupImagen">
// where n=idNumber
function imageHTML(article, idNumber) {
return simplePrintf('<a id="popupImageLink$1">' +
'<img align="right" valign="top" id="popupImg$1" style="display: none;"></img>' +
'</a>', [ idNumber ]);
}
function popTipsSoonFn(id, when, popData) {
if (!when) { when=250; }
var popTips=function(){ setupTooltips(document.getElementById(id), false, true, popData); };
return function() { setTimeout( popTips, when, popData ); };
}
function setPopupTipsAndHTML(html, divname, idnumber, popData) {
setPopupHTML(html, divname, idnumber,
getValueOf('popupSubpopups') ?
popTipsSoonFn(divname + idnumber, null, popData) :
null);
}
// ENDFILE: htmloutput.js
// STARTFILE: mouseout.js
//////////////////////////////////////////////////
// fuzzy checks
function fuzzyCursorOffMenus(x,y, fuzz, parent) {
if (!parent) { return null; }
var uls=parent.getElementsByTagName('ul');
for (var i=0; i<uls.length; ++i) {
if (uls[i].className=='popup_menu') {
if (uls[i].offsetWidth > 0) return false;
} // else {document.title+='.';}
}
return true;
}
function checkPopupPosition () { // stop the popup running off the right of the screen
// FIXME avoid pg.current.link
if (pg.current.link && pg.current.link.navpopup)
pg.current.link.navpopup.limitHorizontalPosition();
}
function mouseOutWikiLink () {
//console ('mouseOutWikiLink');
var a=this;
removeModifierKeyHandler(a);
if (a.navpopup === null || typeof a.navpopup === 'undefined') return;
if ( ! a.navpopup.isVisible() ) {
a.navpopup.banish();
return;
}
restoreTitle(a);
Navpopup.tracker.addHook(posCheckerHook(a.navpopup));
}
function posCheckerHook(navpop) {
return function() {
if (!navpop.isVisible()) { return true; /* remove this hook */ }
if (Navpopup.tracker.dirty) {
return false;
}
var x=Navpopup.tracker.x, y=Navpopup.tracker.y;
var mouseOverNavpop = navpop.isWithin(x,y,navpop.fuzz, navpop.mainDiv) ||
!fuzzyCursorOffMenus(x,y,navpop.fuzz, navpop.mainDiv);
// FIXME it'd be prettier to do this internal to the Navpopup objects
var t=getValueOf('popupHideDelay');
if (t) { t = t * 1000; }
if (!t) {
if(!mouseOverNavpop) {
if(navpop.parentAnchor) {
restoreTitle( navpop.parentAnchor );
}
navpop.banish();
return true; /* remove this hook */
}
return false;
}
// we have a hide delay set
var d=+(new Date());
if ( !navpop.mouseLeavingTime ) {
navpop.mouseLeavingTime = d;
return false;
}
if ( mouseOverNavpop ) {
navpop.mouseLeavingTime=null;
return false;
}
if (d - navpop.mouseLeavingTime > t) {
navpop.mouseLeavingTime=null;
navpop.banish(); return true; /* remove this hook */
}
return false;
};
}
function runStopPopupTimer(navpop) {
// at this point, we should have left the link but remain within the popup
// so we call this function again until we leave the popup.
if (!navpop.stopPopupTimer) {
navpop.stopPopupTimer=setInterval(posCheckerHook(navpop), 500);
navpop.addHook(function(){clearInterval(navpop.stopPopupTimer);},
'hide', 'before');
}
}
// ENDFILE: mouseout.js
// STARTFILE: previewmaker.js
/**
@fileoverview
Defines the {@link Previewmaker} object, which generates short previews from wiki markup.
*/
/**
Creates a new Previewmaker
@constructor
@class The Previewmaker class. Use an instance of this to generate short previews from Wikitext.
@param {String} wikiText The Wikitext source of the page we wish to preview.
@param {String} baseUrl The url we should prepend when creating relative urls.
@param {Navpopup} owner The navpop associated to this preview generator
*/
function Previewmaker(wikiText, baseUrl, owner) {
/** The wikitext which is manipulated to generate the preview. */
this.originalData=wikiText;
this.baseUrl=baseUrl;
this.owner=owner;
this.maxCharacters=getValueOf('popupMaxPreviewCharacters');
this.maxSentences=getValueOf('popupMaxPreviewSentences');
this.setData();
}
Previewmaker.prototype.setData=function() {
var maxSize=Math.max(10000, 2*this.maxCharacters);
this.data=this.originalData.substring(0,maxSize);
};
/** Remove HTML comments
@private
*/
Previewmaker.prototype.killComments = function () {
// this also kills one trailing newline, eg [[diamyo]]
this.data=this.data.replace(RegExp('^<!--[^$]*?-->\\n|\\n<!--[^$]*?-->(?=\\n)|<!--[^$]*?-->', 'g'), '');
};
/**
@private
*/
Previewmaker.prototype.killDivs = function () {
// say goodbye, divs (can be nested, so use * not *?)
this.data=this.data.replace(RegExp('< *div[^>]* *>[\\s\\S]*?< */ *div *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killGalleries = function () {
this.data=this.data.replace(RegExp('< *gallery[^>]* *>[\\s\\S]*?< */ *gallery *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.kill = function(opening, closing, subopening, subclosing, repl) {
var oldk=this.data;
var k=this.killStuff(this.data, opening, closing, subopening, subclosing, repl);
while (k.length < oldk.length) {
oldk=k;
k=this.killStuff(k, opening, closing, subopening, subclosing, repl);
}
this.data=k;
};
/**
@private
*/
Previewmaker.prototype.killStuff = function (txt, opening, closing, subopening, subclosing, repl) {
var op=this.makeRegexp(opening);
var cl=this.makeRegexp(closing, '^');
var sb=subopening ? this.makeRegexp(subopening, '^') : null;
var sc=subclosing ? this.makeRegexp(subclosing, '^') : cl;
if (!op || !cl) {
alert('Navigation Popups error: op or cl is null! something is wrong.');
return;
}
if (!op.test(txt)) { return txt; }
var ret='';
var opResult = op.exec(txt);
ret = txt.substring(0,opResult.index);
txt=txt.substring(opResult.index+opResult[0].length);
var depth = 1;
while (txt.length > 0) {
var removal=0;
if (depth==1 && cl.test(txt)) {
depth--;
removal=cl.exec(txt)[0].length;
} else if (depth > 1 && sc.test(txt)) {
depth--;
removal=sc.exec(txt)[0].length;
}else if (sb && sb.test(txt)) {
depth++;
removal=sb.exec(txt)[0].length;
}
if ( !removal ) { removal = 1; }
txt=txt.substring(removal);
if (depth === 0) { break; }
}
return ret + (repl || '') + txt;
};
/**
@private
*/
Previewmaker.prototype.makeRegexp = function (x, prefix, suffix) {
prefix = prefix || '';
suffix = suffix || '';
var reStr='';
var flags='';
if (isString(x)) {
reStr=prefix + literalizeRegex(x) + suffix;
} else if (isRegExp(x)) {
var s=x.toString().substring(1);
var sp=s.split('/');
flags=sp[sp.length-1];
sp[sp.length-1]='';
s=sp.join('/');
s=s.substring(0,s.length-1);
reStr= prefix + s + suffix;
} else {
log ('makeRegexp failed');
}
log ('makeRegexp: got reStr=' + reStr + ', flags=' + flags);
return RegExp(reStr, flags);
};
/**
@private
*/
Previewmaker.prototype.killBoxTemplates = function () {
// taxobox removal... in fact, there's a saudiprincebox_begin, so let's be more general
// also, have float_begin, ... float_end
this.kill(RegExp('[{][{][^{}\\s|]*?(float|box)[_ ](begin|start)', 'i'), /[}][}]\s*/, '{{');
// infoboxes etc
// from User:Zyxw/popups.js: kill frames too
this.kill(RegExp('[{][{][^{}\\s|]*?(infobox|elementbox|frame)[_ ]', 'i'), /[}][}]\s*/, '{{');
};
/**
@private
*/
Previewmaker.prototype.killTemplates = function () {
// the plus sign is to avoid transcluding [[Template:', ']]
this.kill('{'+'{', '}'+'}', '{', '}', ' ');
};
/**
@private
*/
Previewmaker.prototype.killTables = function () {
// tables are bad, too
// this can be slow, but it's an inprovement over a browser hang
// torture test: [[Comparison_of_Intel_Central_Processing_Units]]
this.kill('{|', /[|]}\s*/, '{|');
this.kill(/<table.*?>/i, /<\/table.*?>/i, /<table.*?>/i);
// remove lines starting with a pipe for the hell of it (?)
this.data=this.data.replace(RegExp('^[|].*$', 'mg'), '');
};
/**
@private
*/
Previewmaker.prototype.killImages = function () {
var forbiddenNamespaceAliases = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=pg.nsImageId && _namespaceId!=pg.nsCategoryId) return;
forbiddenNamespaceAliases.push(_localizedNamespaceLc.split(' ').join('[ _]')); //todo: escape regexp fragments!
});
// images and categories are a nono
this.kill(RegExp('[[][[]\\s*(' + forbiddenNamespaceAliases.join('|') + ')\\s*:', 'i'),
/\]\]\s*/, '[', ']');
};
/**
@private
*/
Previewmaker.prototype.killHTML = function () {
// kill <ref ...>...</ref>
this.kill(/<ref\b[^/>]*?>/i, /<\/ref>/i);
// let's also delete entire lines starting with <. it's worth a try.
this.data=this.data.replace(RegExp('(^|\\n) *<.*', 'g'), '\n');
// and those pesky html tags, but not <nowiki> or <blockquote>
var splitted=this.data.parenSplit(/(<[\w\W]*?(?:>|$|(?=<)))/);
var len=splitted.length;
for (var i=1; i<len; i=i+2) {
switch (splitted[i]) {
case '<nowiki>':
case '</nowiki>':
case '<blockquote>':
case '</blockquote>':
break;
default:
splitted[i]='';
}
}
this.data=splitted.join('');
};
/**
@private
*/
Previewmaker.prototype.killChunks = function() { // heuristics alert
// chunks of italic text? you crazy, man?
var italicChunkRegex=new RegExp
("((^|\\n)\\s*:*\\s*''[^']([^']|'''|'[^']){20}(.|\\n[^\\n])*''[.!?\\s]*\\n)+", 'g');
// keep stuff separated, though, so stick in \n (fixes [[Union Jack]]?
this.data=this.data.replace(italicChunkRegex, '\n');
};
/**
@private
*/
Previewmaker.prototype.mopup = function () {
// we simply *can't* be doing with horizontal rules right now
this.data=this.data.replace(RegExp('^-{4,}','mg'),'');
// no indented lines
this.data=this.data.replace(RegExp('(^|\\n) *:[^\\n]*','g'), '');
// replace __TOC__, __NOTOC__ and whatever else there is
// this'll probably do
this.data=this.data.replace(RegExp('^__[A-Z_]*__ *$', 'gmi'),'');
};
/**
@private
*/
Previewmaker.prototype.firstBit = function () {
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d=this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data=this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data=this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data=this.data.replace(RegExp('^[\\s\\n]*'), '');
var stuff=(RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) { d = stuff[0]; }
if (!getValueOf('popupPreviewFirstParOnly')) { d = this.data; }
/// now put \n\n after sections so that bullets and numbered lists work
d=d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// Split sentences. Superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["'+"'"+']*\\s)','g'));
// leading space is bad, mmkay?
d[0]=d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds=RegExp('([^.][a-z][.] *[a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|op|cit|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
this.fullLength=d.join('').length;
var n=this.maxSentences;
var dd=this.firstSentences(d,n);
do {
dd=this.firstSentences(d,n); --n;
} while ( dd.length > this.maxCharacters && n !== 0 );
this.data = dd;
};
/**
@private
*/
Previewmaker.prototype.fixSentenceEnds = function(strs, reg) {
// take an array of strings, strs
// join strs[i] to strs[i+1] & strs[i+2] if strs[i] matches regex reg
for (var i=0; i<strs.length-2; ++i) {
if (reg.test(strs[i])) {
var a=[];
for (var j=0; j<strs.length; ++j) {
if (j<i) a[j]=strs[j];
if (j==i) a[i]=strs[i]+strs[i+1]+strs[i+2];
if (j>i+2) a[j-2]=strs[j];
}
return this.fixSentenceEnds(a,reg);
}
}
return strs;
};
/**
@private
*/
Previewmaker.prototype.firstSentences = function(strs, howmany) {
var t=strs.slice(0, 2*howmany);
return t.join('');
};
/**
@private
*/
Previewmaker.prototype.killBadWhitespace = function() {
// also cleans up isolated '''', eg [[Suntory Sungoliath]]
this.data=this.data.replace(RegExp('^ *\'+ *$', 'gm'), '');
};
/**
Runs the various methods to generate the preview.
The preview is stored in the <code>html</html> field.
@private
*/
Previewmaker.prototype.makePreview = function() {
if (this.owner.article.namespaceId()!=pg.nsTemplateId &&
this.owner.article.namespaceId()!=pg.nsImageId ) {
this.killComments();
this.killDivs();
this.killGalleries();
this.killBoxTemplates();
if (getValueOf('popupPreviewKillTemplates')) {
this.killTemplates();
} else {
this.killMultilineTemplates();
}
this.killTables();
this.killImages();
this.killHTML();
this.killChunks();
this.mopup();
this.firstBit();
this.killBadWhitespace();
}
else
{
this.killHTML();
}
this.html=wiki2html(this.data, this.baseUrl); // needs livepreview
this.fixHTML();
this.stripLongTemplates();
};
/**
@private
*/
Previewmaker.prototype.esWiki2HtmlPart = function(data) {
var reLinks = /(?:\[\[([^|\]]*)(?:\|([^|\]]*))*]]([a-z]*))/gi; //match a wikilink
reLinks.lastIndex = 0; //reset regex
var match;
var result = "";
var postfixIndex = 0;
while ((match = reLinks.exec(data))) //match all wikilinks
{
//FIXME: the way that link is built here isn't perfect. It is clickable, but popups preview won't recognize it in some cases.
result += pg.escapeQuotesHTML(data.substring(postfixIndex, match.index)) +
'<a href="'+Insta.conf.paths.articles+pg.escapeQuotesHTML(match[1])+'">'+pg.escapeQuotesHTML((match[2]?match[2]:match[1])+match[3])+"</a>";
postfixIndex = reLinks.lastIndex;
}
//append the rest
result += pg.escapeQuotesHTML(data.substring(postfixIndex));
return result;
};
Previewmaker.prototype.editSummaryPreview=function() {
var reAes = /\/\* *(.*?) *\*\//g; //match the first section marker
reAes.lastIndex = 0; //reset regex
var match;
match = reAes.exec(this.data);
if (match)
{
//we have a section link. Split it, process it, combine it.
var prefix = this.data.substring(0,match.index-1);
var section = match[1];
var postfix = this.data.substring(reAes.lastIndex);
var start = "<span class='autocomment'>";
var end = "</span>";
if (prefix.length>0) start = this.esWiki2HtmlPart(prefix) + " " + start + "- ";
if (postfix.length>0) end = ": " + end + this.esWiki2HtmlPart(postfix);
var t=new Title().fromURL(this.baseUrl);
t.anchorFromUtf(section);
var sectionLink = Insta.conf.paths.articles + pg.escapeQuotesHTML(t.toString(true)) + '#' + pg.escapeQuotesHTML(t.anchor);
return start + '<a href="'+sectionLink+'">→</a> '+pg.escapeQuotesHTML(section) + end;
}
//else there's no section link, htmlify the whole thing.
return this.esWiki2HtmlPart(this.data);
};
//<NOLITE>
/** Test function for debugging preview problems one step at a time.
*/
/*eslint-disable */
function previewSteps(txt) {
try {
txt=txt || document.editform.wpTextbox1.value;
} catch (err) {
if (pg.cache.pages.length > 0) {
txt=pg.cache.pages[pg.cache.pages.length-1].data;
} else {
alert('provide text or use an edit page');
}
}
txt=txt.substring(0,10000);
var base=pg.wiki.articlebase + Title.fromURL(document.location.href).urlString();
var p=new Previewmaker(txt, base, pg.current.link.navpopup);
if (this.owner.article.namespaceId() != pg.nsTemplateId) {
p.killComments(); if (!confirm('done killComments(). Continue?\n---\n' + p.data)) { return; }
p.killDivs(); if (!confirm('done killDivs(). Continue?\n---\n' + p.data)) { return; }
p.killGalleries(); if (!confirm('done killGalleries(). Continue?\n---\n' + p.data)) { return; }
p.killBoxTemplates(); if (!confirm('done killBoxTemplates(). Continue?\n---\n' + p.data)) { return; }
if (getValueOf('popupPreviewKillTemplates')) {
p.killTemplates(); if (!confirm('done killTemplates(). Continue?\n---\n' + p.data)) { return; }
} else {
p.killMultilineTemplates(); if (!confirm('done killMultilineTemplates(). Continue?\n---\n' + p.data)) { return; }
}
p.killTables(); if (!confirm('done killTables(). Continue?\n---\n' + p.data)) { return; }
p.killImages(); if (!confirm('done killImages(). Continue?\n---\n' + p.data)) { return; }
p.killHTML(); if (!confirm('done killHTML(). Continue?\n---\n' + p.data)) { return; }
p.killChunks(); if (!confirm('done killChunks(). Continue?\n---\n' + p.data)) { return; }
p.mopup(); if (!confirm('done mopup(). Continue?\n---\n' + p.data)) { return; }
p.firstBit(); if (!confirm('done firstBit(). Continue?\n---\n' + p.data)) { return; }
p.killBadWhitespace(); if (!confirm('done killBadWhitespace(). Continue?\n---\n' + p.data)) { return; }
}
p.html=wiki2html(p.data, base); // needs livepreview
p.fixHTML(); if (!confirm('done fixHTML(). Continue?\n---\n' + p.html)) { return; }
p.stripLongTemplates(); if (!confirm('done stripLongTemplates(). Continue?\n---\n' + p.html)) { return; }
alert('finished preview - end result follows.\n---\n' + p.html);
}
/*eslint-enable */
//</NOLITE>
/**
Works around livepreview bugs.
@private
*/
Previewmaker.prototype.fixHTML = function() {
if(!this.html) return;
var ret = this.html;
// fix question marks in wiki links
// maybe this'll break some stuff :-(
ret=ret.replace(RegExp('(<a href="' + pg.wiki.articlePath + '/[^"]*)[?](.*?")', 'g'), '$1%3F$2');
ret=ret.replace(RegExp('(<a href=\'' + pg.wiki.articlePath + '/[^\']*)[?](.*?\')', 'g'), '$1%3F$2');
// FIXME fix up % too
this.html=ret;
};
/**
Generates the preview and displays it in the current popup.
Does nothing if the generated preview is invalid or consists of whitespace only.
Also activates wikilinks in the preview for subpopups if the popupSubpopups option is true.
*/
Previewmaker.prototype.showPreview = function () {
this.makePreview();
if (typeof this.html != typeof '') return;
if (RegExp('^\\s*$').test(this.html)) return;
setPopupHTML('<hr />', 'popupPrePreviewSep', this.owner.idNumber);
setPopupTipsAndHTML(this.html, 'popupPreview', this.owner.idNumber, { owner: this.owner });
var more = (this.fullLength > this.data.length) ? this.moreLink() : '';
setPopupHTML(more, 'popupPreviewMore', this.owner.idNumber);
};
/**
@private
*/
Previewmaker.prototype.moreLink=function() {
var a=document.createElement('a');
a.className='popupMoreLink';
a.innerHTML=popupString('more...');
var savedThis=this;
a.onclick=function() {
savedThis.maxCharacters+=2000;
savedThis.maxSentences+=20;
savedThis.setData();
savedThis.showPreview();
};
return a;
};
/**
@private
*/
Previewmaker.prototype.stripLongTemplates = function() {
// operates on the HTML!
this.html=this.html.replace(RegExp('^.{0,1000}[{][{][^}]*?(<(p|br)( /)?>\\s*){2,}([^{}]*?[}][}])?', 'gi'), '');
this.html=this.html.split('\n').join(' '); // workaround for <pre> templates
this.html=this.html.replace(RegExp('[{][{][^}]*<pre>[^}]*[}][}]','gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killMultilineTemplates = function() {
this.kill('{{{', '}}}');
this.kill(RegExp('\\s*[{][{][^{}]*\\n'), '}}', '{{');
};
// ENDFILE: previewmaker.js
// STARTFILE: querypreview.js
function loadAPIPreview(queryType, article, navpop) {
var art=new Title(article).urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
var htmlGenerator=function(/*a, d*/){alert('invalid html generator');};
var usernameart = '';
switch (queryType) {
case 'history':
url += 'titles=' + art + '&prop=revisions&rvlimit=' +
getValueOf('popupHistoryPreviewLimit');
htmlGenerator=APIhistoryPreviewHTML;
break;
case 'category':
url += 'list=categorymembers&cmtitle=' + art;
htmlGenerator=APIcategoryPreviewHTML;
break;
case 'userinfo':
var username = new Title( article ).userName();
usernameart = encodeURIComponent( username );
if (pg.re.ipUser.test(username)) {
url += 'list=blocks&bkprop=range|restrictions&bkip=' + usernameart;
} else {
url += 'list=users|usercontribs&usprop=blockinfo|groups|editcount|registration|gender&ususers=' + usernameart + "&meta=globaluserinfo&guiprop=groups|unattached&guiuser="+ usernameart + "&uclimit=1&ucprop=timestamp&ucuser=" + usernameart;
}
htmlGenerator=APIuserInfoPreviewHTML;
break;
case 'contribs':
usernameart = encodeURIComponent( new Title( article ).userName() );
url += 'list=usercontribs&ucuser=' + usernameart +
'&uclimit=' + getValueOf('popupContribsPreviewLimit');
htmlGenerator=APIcontribsPreviewHTML;
break;
case 'imagepagepreview':
var trail='';
if (getValueOf('popupImageLinks')) { trail = '&list=imageusage&iutitle=' + art; }
url += 'titles=' + art + '&prop=revisions|imageinfo&rvprop=content' + trail;
htmlGenerator=APIimagepagePreviewHTML;
break;
case 'backlinks':
url += 'list=backlinks&bltitle=' + art;
htmlGenerator=APIbacklinksPreviewHTML;
break;
case 'revision':
if (article.oldid) {
url += 'revids=' + article.oldid;
} else {
url += 'titles=' + article.removeAnchor().urlString();
}
url += '&prop=revisions|pageprops|info|images|categories&rvprop=ids|timestamp|flags|comment|user|content&cllimit=max&imlimit=max';
htmlGenerator=APIrevisionPreviewHTML;
break;
}
pendingNavpopTask(navpop);
var callback=function(d){
log( "callback of API functions was hit" );
showAPIPreview(queryType, htmlGenerator(article,d,navpop), navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_'+queryType+'_QUERY_DATA'); }
}
function linkList(list) {
list.sort(function(x,y) { return (x==y ? 0 : (x<y ? -1 : 1)); });
var buf=[];
for (var i=0; i<list.length; ++i) {
buf.push(wikiLink({article: new Title(list[i]),
text: list[i].split(' ').join(' '),
action: 'view'}));
}
return buf.join(', ');
}
function getTimeOffset() {
var tz = mw.user.options.get('timecorrection');
if(tz) {
if( tz.indexOf('|') > -1 ) {
// New format
return parseInt(tz.split('|')[1],10);
} else if ( tz.indexOf(':') > -1 ) {
// Old format
return( parseInt(tz,10)*60 + parseInt(tz.split(':')[1],10) );
}
}
return 0;
}
/*
* Creates a HTML table that's shown in the history and user-contribs popups.
* @param {Object[]} h - a list of revisions, returned from the API
* @param {boolean} reallyContribs - true only if we're displaying user contributions
*/
function editPreviewTable(article, h, reallyContribs, timeOffset) {
var html=['<table>'];
var day=null;
var curart=article;
var page=null;
var makeFirstColumnLinks;
if(reallyContribs) {
// We're showing user contributions, so make (diff | hist) links
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&diff=prev' +
'&oldid=' + currentRevision.revid + '">' + popupString('diff') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&action=history">' +
popupString('hist') + '</a>';
result += ')';
return result;
};
} else {
// It's a regular history page, so make (cur | last) links
var firstRevid = h[0].revid;
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=' + firstRevid + '&oldid=' + currentRevision.revid + '">' + popupString('cur') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=prev&oldid=' + currentRevision.revid + '">' + popupString('last') + '</a>';
result += ')';
return result;
};
}
for (var i=0; i<h.length; ++i) {
if (reallyContribs) {
page = h[i].title;
curart = new Title(page);
}
var minor = h[i].minor ? '<b>m </b>' : '';
var editDate = adjustDate(getDateFromTimestamp(h[i].timestamp), timeOffset);
var thisDay = dayFormat(editDate);
var thisTime = timeFormat(editDate);
if (thisDay == day) {
thisDay = '';
} else {
day = thisDay;
}
if (thisDay) {
html.push( '<tr><td colspan=3><span class="popup_history_date">' +
thisDay+'</span></td></tr>' );
}
html.push('<tr class="popup_history_row_' + ( (i%2) ? 'odd' : 'even') + '">');
html.push('<td>' + makeFirstColumnLinks(h[i]) + '</td>');
html.push('<td>' +
'<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&oldid=' + h[i].revid + '">' + thisTime + '</a></td>');
var col3url='', col3txt='';
if (!reallyContribs) {
var user=h[i].user;
if( !h[i].userhidden ) {
if( pg.re.ipUser.test(user) ) {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Contributions&target=' + new Title(user).urlString();
} else {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(user).urlString();
}
col3txt=pg.escapeQuotesHTML(user);
} else {
col3url=getValueOf('popupRevDelUrl');
col3txt=pg.escapeQuotesHTML( popupString('revdel'));
}
} else {
col3url=pg.wiki.titlebase + curart.urlString();
col3txt=pg.escapeQuotesHTML(page);
}
html.push('<td>' + (reallyContribs ? minor : '') +
'<a href="' + col3url + '">' + col3txt + '</a></td>');
var comment='';
var c=h[i].comment || h[i].content;
if (c) {
comment=new Previewmaker(c, new Title(curart).toUrl()).editSummaryPreview();
} else if ( h[i].commenthidden ) {
comment=popupString('revdel');
}
html.push('<td>' + (!reallyContribs ? minor : '') + comment + '</td>');
html.push('</tr>');
html=[html.join('')];
}
html.push('</table>');
return html.join('');
}
function getDateFromTimestamp(t) {
var s=t.split(/[^0-9]/);
switch(s.length) {
case 0: return null;
case 1: return new Date(s[0]);
case 2: return new Date(s[0], s[1]-1);
case 3: return new Date(s[0], s[1]-1, s[2]);
case 4: return new Date(s[0], s[1]-1, s[2], s[3]);
case 5: return new Date(s[0], s[1]-1, s[2], s[3], s[4]);
case 6: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5]);
default: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5], s[6]);
}
}
function adjustDate(d, offset) {
// offset is in minutes
var o=offset * 60 * 1000;
return new Date( +d + o);
}
function dayFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCFullYear(), editDate.getUTCMonth()+1, editDate.getUTCDate()]).join('-'); }
return map(zeroFill, [editDate.getFullYear(), editDate.getMonth()+1, editDate.getDate()]).join('-');
}
function timeFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCHours(), editDate.getUTCMinutes(), editDate.getUTCSeconds()]).join(':'); }
return map(zeroFill, [editDate.getHours(), editDate.getMinutes(), editDate.getSeconds()]).join(':');
}
function showAPIPreview(queryType, html, id, navpop, download) {
// DJ: done
var target='popupPreview';
completedNavpopTask(navpop);
switch (queryType) {
case 'imagelinks':
case 'category':
target='popupPostPreview'; break;
case 'userinfo':
target='popupUserData'; break;
case 'revision':
insertPreview(download);
return;
}
setPopupTipsAndHTML(html, target, id);
}
function APIrevisionPreviewHTML(article, download) {
try{
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
if( page.missing ) {
// TODO we need to fix this proper later on
download.owner = null;
return;
}
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
download.data = content;
download.lastModified = new Date(page.revisions[0].timestamp);
}
} catch(someError) {
return 'Revision preview failed :(';
}
}
function APIbacklinksPreviewHTML(article, download/*, navpop*/ ) {
try {
var jsObj=getJsObj(download.data);
var list=jsObj.query.backlinks;
var html=[];
if (!list) { return popupString('No backlinks found'); }
for ( var i=0; i < list.length; i++ ) {
var t=new Title(list[i].title);
html.push('<a href="' + pg.wiki.titlebase + t.urlString() + '">' + t + '</a>');
}
html=html.join(', ');
if (jsObj['continue'] && jsObj['continue'].blcontinue) {
html += popupString(' and more');
}
return html;
} catch (someError) {
return 'backlinksPreviewHTML went wonky';
}
}
pg.fn.APIsharedImagePagePreviewHTML = function APIsharedImagePagePreviewHTML(obj) {
log( "APIsharedImagePagePreviewHTML" );
var popupid = obj.requestid;
if( obj.query && obj.query.pages )
{
var page=anyChild(obj.query.pages );
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
/* Not entirely safe, but the best we can do */
var p=new Previewmaker(content, pg.current.link.navpopup.article, pg.current.link.navpopup);
p.makePreview();
setPopupHTML( p.html, "popupSecondPreview", popupid );
}
}
};
function APIimagepagePreviewHTML(article, download, navpop) {
try {
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
var content= (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
var ret='';
var alt='';
try{alt=navpop.parentAnchor.childNodes[0].alt;} catch(e){}
if (alt) {
ret = ret + '<hr /><b>' + popupString('Alt text:') + '</b> ' + pg.escapeQuotesHTML(alt);
}
if (typeof content === 'string') {
var p=prepPreviewmaker(content, article, navpop);
p.makePreview();
if (p.html) { ret += '<hr />' + p.html; }
if (getValueOf('popupSummaryData')) {
var info=getPageInfo(content, download);
log(info);
setPopupTrailer(info, navpop.idNumber);
}
}
if (page && page.imagerepository == "shared" ) {
var art=new Title(article);
var encart = encodeURIComponent( "File:" + art.stripNamespace() );
var shared_url = pg.wiki.apicommonsbase + '?format=json&formatversion=2' +
'&callback=pg.fn.APIsharedImagePagePreviewHTML' +
'&requestid=' + navpop.idNumber +
'&action=query&prop=revisions&rvprop=content&titles=' + encart;
ret = ret +'<hr />' + popupString( 'Image from Commons') +
': <a href="' + pg.wiki.commonsbase + '?title=' + encart + '">' +
popupString( 'Description page') + '</a>';
mw.loader.load( shared_url );
}
showAPIPreview('imagelinks', APIimagelinksPreviewHTML(article,download), navpop.idNumber, download);
return ret;
} catch (someError) {
return 'API imagepage preview failed :(';
}
}
function APIimagelinksPreviewHTML(article, download) {
try {
var jsobj=getJsObj(download.data);
var list=jsobj.query.imageusage;
if (list) {
var ret=[];
for (var i=0; i < list.length; i++) {
ret.push(list[i].title);
}
if (ret.length === 0) { return popupString('No image links found'); }
return '<h2>' + popupString('File links') + '</h2>' + linkList(ret);
} else {
return popupString('No image links found');
}
} catch(someError) {
return 'Image links preview generation failed :(';
}
}
function APIcategoryPreviewHTML(article, download) {
try{
var jsobj=getJsObj(download.data);
var list=jsobj.query.categorymembers;
var ret=[];
for (var p=0; p < list.length; p++) {
ret.push(list[p].title);
}
if (ret.length === 0) { return popupString('Empty category'); }
ret = '<h2>' + tprintf('Category members (%s shown)', [ret.length]) + '</h2>' +linkList(ret);
if (jsobj['continue'] && jsobj['continue'].cmcontinue) {
ret += popupString(' and more');
}
return ret;
} catch(someError) {
return 'Category preview failed :(';
}
}
function APIuserInfoPreviewHTML(article, download) {
var ret=[];
var queryobj = {};
try{
queryobj=getJsObj(download.data).query;
} catch(someError) { return 'Userinfo preview failed :('; }
var user=anyChild(queryobj.users);
if (user) {
var globaluserinfo=queryobj.globaluserinfo;
if (user.invalid === '') {
ret.push( popupString( 'Invalid user') );
} else if (user.missing === '') {
ret.push( popupString( 'Not a registered username') );
}
if( user.blockedby ) {
if( user.blockpartial ) {
ret.push('<b>' + popupString('Has blocks') + '</b>');
} else {
ret.push('<b>' + popupString('CẤM') + '</b>');
}
}
if( globaluserinfo && ( 'locked' in globaluserinfo || 'hidden' in globaluserinfo ) ) {
var lockedSulAccountIsAttachedToThis = true;
for( var i=0; globaluserinfo.unattached && i < globaluserinfo.unattached.length; i++) {
if ( globaluserinfo.unattached[i].wiki === mw.config.get('wgDBname') ) {
lockedSulAccountIsAttachedToThis=false;
break;
}
}
if (lockedSulAccountIsAttachedToThis) {
if ( 'locked' in globaluserinfo ) ret.push('<b><i>' + popupString('LOCKED') + '</i></b>');
if ( 'hidden' in globaluserinfo ) ret.push('<b><i>' + popupString('HIDDEN') + '</i></b>');
}
}
if( getValueOf('popupShowGender') && user.gender ) {
switch( user.gender ) {
case "male": ret.push( popupString( "\u2642" ) ); break;
case "female": ret.push( popupString( "\u2640" ) ); break;
}
}
if( user.groups ) {
for( var j=0; j < user.groups.length; j++) {
var currentGroup = user.groups[j];
if( ["*", "user", "autoconfirmed", "extendedconfirmed"].indexOf( currentGroup ) === -1 ) {
ret.push( pg.escapeQuotesHTML(user.groups[j]) );
}
}
}
if( globaluserinfo && globaluserinfo.groups ) {
for( var k=0; k < globaluserinfo.groups.length; k++) {
ret.push( '<i>'+pg.escapeQuotesHTML(globaluserinfo.groups[k])+'</i>' );
}
}
if( user.registration )
ret.push( pg.escapeQuotesHTML((user.editcount?user.editcount:'0') + popupString(' sửa đổi từ: ') + (user.registration?dayFormat(getDateFromTimestamp(user.registration)):'')) );
}
if (queryobj.usercontribs && queryobj.usercontribs.length) {
ret.push( popupString('sửa đổi lần cuối vào ') + dayFormat(getDateFromTimestamp(queryobj.usercontribs[0].timestamp)) );
}
if (queryobj.blocks) {
ret.push( popupString( 'IP user') ); //we only request list=blocks for IPs
for (var l=0; l<queryobj.blocks.length; l++) {
console.log(queryobj);
var rbstr = queryobj.blocks[l].rangestart === queryobj.blocks[l].rangeend ? 'BLOCK' : 'RANGEBLOCK';
rbstr = (!Array.isArray(queryobj.blocks[l].restrictions) ? 'Has ' + rbstr.toLowerCase() + 's' : rbstr + 'ED');
ret.push('<b>' + popupString(rbstr) + '</b>' );
}
}
ret = '<hr />' + ret.join( ', ' );
return ret;
}
function APIcontribsPreviewHTML(article, download, navpop) {
return APIhistoryPreviewHTML(article, download, navpop, true);
}
function APIhistoryPreviewHTML(article, download, navpop, reallyContribs) {
try {
var jsobj=getJsObj(download.data);
var edits = [];
if( reallyContribs ) {
edits=jsobj.query.usercontribs;
} else {
edits=anyChild(jsobj.query.pages).revisions;
}
var ret=editPreviewTable(article, edits, reallyContribs, getTimeOffset());
return ret;
} catch (someError) {
return 'History preview failed :-(';
}
}
//</NOLITE>
// ENDFILE: querypreview.js
// STARTFILE: debug.js
////////////////////////////////////////////////////////////////////
// Debugging functions
////////////////////////////////////////////////////////////////////
function setupDebugging() {
//<NOLITE>
if (window.popupDebug) { // popupDebug is set from .version
window.log=function(x) { //if(gMsg!='')gMsg += '\n'; gMsg+=time() + ' ' + x; };
window.console.log(x);
};
window.errlog=function(x) {
window.console.error(x);
};
log('Initializing logger');
} else {
//</NOLITE>
window.log = function() {};
window.errlog = function() {};
//<NOLITE>
}
//</NOLITE>
}
// ENDFILE: debug.js
// STARTFILE: images.js
// load image of type Title.
function loadImage(image, navpop) {
if (typeof image.stripNamespace != 'function') { alert('loadImages bad'); }
// API call to retrieve image info.
if ( !getValueOf('popupImages') ) return;
if ( !isValidImageName(image) ) return false;
var art=image.urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query';
url += '&prop=imageinfo&iiprop=url|mime&iiurlwidth=' + getValueOf('popupImageSizeLarge');
url += '&titles=' + art;
pendingNavpopTask(navpop);
var callback=function(d){
popupsInsertImage(navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'after', 'DOWNLOAD_IMAGE_QUERY_DATA'); }
}
function popupsInsertImage(id, navpop, download) {
log( "popupsInsertImage");
var imageinfo;
try {
var jsObj=getJsObj(download.data);
var imagepage=anyChild(jsObj.query.pages);
if (typeof imagepage.imageinfo === 'undefined') return;
imageinfo = imagepage.imageinfo[0];
} catch (someError) {
log( "popupsInsertImage failed :(" );
return;
}
var popupImage = document.getElementById("popupImg"+id);
if (!popupImage) {
log( "could not find insertion point for image");
return;
}
popupImage.width=getValueOf('popupImageSize');
popupImage.style.display='inline';
// Set the source for the image.
if( imageinfo.thumburl )
popupImage.src=imageinfo.thumburl;
else if( imageinfo.mime.indexOf("image") === 0 ){
popupImage.src=imageinfo.url;
log( "a thumb could not be found, using original image" );
} else log( "fullsize imagethumb, but not sure if it's an image");
var a=document.getElementById("popupImageLink"+id);
if (a === null) { return null; }
// Determine the action of the surrouding imagelink.
switch (getValueOf('popupThumbAction')) {
case 'imagepage':
if (pg.current.article.namespaceId()!=pg.nsImageId) {
a.href=imageinfo.descriptionurl;
// FIXME: unreliable pg.idNumber
popTipsSoonFn('popupImage' + id)();
break;
}
/* falls through */
case 'sizetoggle':
a.onclick=toggleSize;
a.title=popupString('Toggle image size');
return;
case 'linkfull':
a.href = imageinfo.url;
a.title=popupString('Open full-size image');
return;
}
}
// Toggles the image between inline small and navpop fullwidth.
// It's the same image, no actual sizechange occurs, only display width.
function toggleSize() {
var imgContainer=this;
if (!imgContainer) {
alert('imgContainer is null :/');
return;
}
var img=imgContainer.firstChild;
if (!img) {
alert('img is null :/');
return;
}
if (!img.style.width || img.style.width==='') {
img.style.width='100%';
} else {
img.style.width='';
}
}
// Returns one title of an image from wikiText.
function getValidImageFromWikiText(wikiText) {
// nb in pg.re.image we're interested in the second bracketed expression
// this may change if the regex changes :-(
//var match=pg.re.image.exec(wikiText);
var matched=null;
var match;
// strip html comments, used by evil bots :-(
var t = removeMatchesUnless(wikiText, RegExp('(<!--[\\s\\S]*?-->)'), 1,
RegExp('^<!--[^[]*popup', 'i'));
while ( ( match = pg.re.image.exec(t) ) ) {
// now find a sane image name - exclude templates by seeking {
var m = match[2] || match[6];
if ( isValidImageName(m) ) {
matched=m;
break;
}
}
pg.re.image.lastIndex=0;
if (!matched) { return null; }
return mw.config.get('wgFormattedNamespaces')[pg.nsImageId]+':'+upcaseFirst(matched);
}
function removeMatchesUnless(str, re1, parencount, re2) {
var split=str.parenSplit(re1);
var c=parencount + 1;
for (var i=0; i<split.length; ++i) {
if ( i%c === 0 || re2.test(split[i]) ) { continue; }
split[i]='';
}
return split.join('');
}
//</NOLITE>
// ENDFILE: images.js
// STARTFILE: namespaces.js
// Set up namespaces and other non-strings.js localization
// (currently that means redirs too)
function setNamespaces() {
pg.nsSpecialId = -1;
pg.nsMainspaceId = 0;
pg.nsImageId = 6;
pg.nsUserId = 2;
pg.nsUsertalkId = 3;
pg.nsCategoryId = 14;
pg.nsTemplateId = 10;
}
function setRedirs() {
var r='redirect';
var R='REDIRECT';
var redirLists={
//<NOLITE>
'ar': [ R, 'تحويل' ],
'be': [ r, 'перанакіраваньне' ],
'bg': [ r, 'пренасочване', 'виж' ],
'bs': [ r, 'Preusmjeri', 'preusmjeri', 'PREUSMJERI' ],
'cs': [ R, 'PŘESMĚRUJ' ],
'cy': [ r, 'ail-cyfeirio' ],
'de': [ R, 'WEITERLEITUNG' ],
'el': [ R, 'ΑΝΑΚΑΤΕΥΘΥΝΣΗ'],
'eo': [ R, 'ALIDIREKTU', 'ALIDIREKTI' ],
'es': [ R, 'REDIRECCIÓN' ],
'et': [ r, 'suuna' ],
'ga': [ r, 'athsheoladh' ],
'gl': [ r, 'REDIRECCIÓN', 'REDIRECIONAMENTO'],
'he': [ R, 'הפניה' ],
'hu': [ R, 'ÁTIRÁNYÍTÁS' ],
'is': [ r, 'tilvísun', 'TILVÍSUN' ],
'it': [ R, 'RINVIA', 'Rinvia'],
'ja': [ R, '転送' ],
'mk': [ r, 'пренасочување', 'види' ],
'nds': [ r, 'wiederleiden' ],
'nl': [ R, 'DOORVERWIJZING' ],
'nn': [ r, 'omdiriger' ],
'pl': [ R, 'PATRZ', 'PRZEKIERUJ', 'TAM' ],
'pt': [ R, 'redir' ],
'ru': [ R, 'ПЕРЕНАПРАВЛЕНИЕ', 'ПЕРЕНАПР' ],
'sk': [ r, 'presmeruj' ],
'sr': [ r, 'Преусмери', 'преусмери', 'ПРЕУСМЕРИ', 'Preusmeri', 'preusmeri', 'PREUSMERI' ],
'tt': [ R, 'yünältü', 'перенаправление', 'перенапр' ],
'uk': [ R, 'ПЕРЕНАПРАВЛЕННЯ', 'ПЕРЕНАПР' ],
'vi': [ r, 'đổi' ],
'zh': [ R, '重定向'] // no comma
//</NOLITE>
};
var redirList=redirLists[ pg.wiki.lang ] || [r, R];
// Mediawiki is very tolerant about what comes after the #redirect at the start
pg.re.redirect=RegExp('^\\s*[#](' + redirList.join('|') + ').*?\\[{2}([^\\|\\]]*)(|[^\\]]*)?\\]{2}\\s*(.*)', 'i');
}
function setInterwiki() {
if (pg.wiki.wikimedia) {
// From https://meta.wikimedia.org/wiki/List_of_Wikipedias
pg.wiki.interwiki='aa|ab|ace|af|ak|als|am|an|ang|ar|arc|arz|as|ast|av|ay|az|ba|bar|bat-smg|bcl|be|be-x-old|bg|bh|bi|bjn|bm|bn|bo|bpy|br|bs|bug|bxr|ca|cbk-zam|cdo|ce|ceb|ch|cho|chr|chy|ckb|co|cr|crh|cs|csb|cu|cv|cy|da|de|diq|dsb|dv|dz|ee|el|eml|en|eo|es|et|eu|ext|fa|ff|fi|fiu-vro|fj|fo|fr|frp|frr|fur|fy|ga|gag|gan|gd|gl|glk|gn|got|gu|gv|ha|hak|haw|he|hi|hif|ho|hr|hsb|ht|hu|hy|hz|ia|id|ie|ig|ii|ik|ilo|io|is|it|iu|ja|jbo|jv|ka|kaa|kab|kbd|kg|ki|kj|kk|kl|km|kn|ko|koi|kr|krc|ks|ksh|ku|kv|kw|ky|la|lad|lb|lbe|lg|li|lij|lmo|ln|lo|lt|ltg|lv|map-bms|mdf|mg|mh|mhr|mi|mk|ml|mn|mo|mr|mrj|ms|mt|mus|mwl|my|myv|mzn|na|nah|nap|nds|nds-nl|ne|new|ng|nl|nn|no|nov|nrm|nv|ny|oc|om|or|os|pa|pag|pam|pap|pcd|pdc|pfl|pi|pih|pl|pms|pnb|pnt|ps|pt|qu|rm|rmy|rn|ro|roa-rup|roa-tara|ru|rue|rw|sa|sah|sc|scn|sco|sd|se|sg|sh|si|simple|sk|sl|sm|sn|so|sq|sr|srn|ss|st|stq|su|sv|sw|szl|ta|te|tet|tg|th|ti|tk|tl|tn|to|tpi|tr|ts|tt|tum|tw|ty|udm|ug|uk|ur|uz|ve|vec|vi|vls|vo|wa|war|wo|wuu|xal|xh|yi|yo|za|zea|zh|zh-classical|zh-min-nan|zh-yue|zu';
pg.re.interwiki=RegExp('^'+pg.wiki.interwiki+':');
} else {
pg.wiki.interwiki=null;
pg.re.interwiki=RegExp('^$');
}
}
// return a regexp pattern matching all variants to write the given namespace
function nsRe(namespaceId) {
var imageNamespaceVariants = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=namespaceId) return;
_localizedNamespaceLc = upcaseFirst(_localizedNamespaceLc);
imageNamespaceVariants.push(mw.util.escapeRegExp(_localizedNamespaceLc).split(' ').join('[ _]'));
imageNamespaceVariants.push(mw.util.escapeRegExp(encodeURI(_localizedNamespaceLc)));
});
return '(?:' + imageNamespaceVariants.join('|') + ')';
}
function nsReImage() {
return nsRe(pg.nsImageId);
}
// ENDFILE: namespaces.js
// STARTFILE: selpop.js
//<NOLITE>
function getEditboxSelection() {
// see http://www.webgurusforum.com/8/12/0
var editbox;
try {
editbox=document.editform.wpTextbox1;
} catch (dang) { return; }
// IE, Opera
if (document.selection) { return document.selection.createRange().text; }
// Mozilla
var selStart = editbox.selectionStart;
var selEnd = editbox.selectionEnd;
return (editbox.value).substring(selStart, selEnd);
}
function doSelectionPopup() {
// popup if the selection looks like [[foo|anything afterwards at all
// or [[foo|bar]]text without ']]'
// or [[foo|bar]]
var sel=getEditboxSelection();
var open=sel.indexOf('[[');
var pipe=sel.indexOf('|');
var close=sel.indexOf(']]');
if (open == -1 || ( pipe == -1 && close == -1) ) { return; }
if (pipe != -1 && open > pipe || close != -1 && open > close) { return; }
if (getValueOf('popupOnEditSelection')=='boxpreview') {
return doSeparateSelectionPopup(sel);
}
var article=new Title(sel.substring(open+2, (pipe < 0) ? close : pipe)).urlString();
if (close > 0 && sel.substring(close+2).indexOf('[[') >= 0) {
return;
}
var a=document.createElement('a');
a.href=pg.wiki.titlebase + article;
mouseOverWikiLink2(a);
if (a.navpopup) {
a.navpopup.addHook(function(){runStopPopupTimer(a.navpopup);}, 'unhide', 'after');
}
}
function doSeparateSelectionPopup(str) {
var div=document.getElementById('selectionPreview');
if (!div) {
div = document.createElement('div');
div.id='selectionPreview';
try {
var box=document.editform.wpTextbox1;
box.parentNode.insertBefore(div, box);
} catch (error) {
return;
}
}
div.innerHTML=wiki2html(str);
div.ranSetupTooltipsAlready = false;
popTipsSoonFn('selectionPreview')();
}
//</NOLITE>
// ENDFILE: selpop.js
// STARTFILE: navpopup.js
/**
@fileoverview Defines two classes: {@link Navpopup} and {@link Mousetracker}.
<code>Navpopup</code> describes popups: when they appear, where, what
they look like and so on.
<code>Mousetracker</code> "captures" the mouse using
<code>document.onmousemove</code>.
*/
/**
Creates a new Mousetracker.
@constructor
@class The Mousetracker class. This monitors mouse movements and manages associated hooks.
*/
function Mousetracker() {
/**
Interval to regularly run the hooks anyway, in milliseconds.
@type Integer
*/
this.loopDelay=400;
/**
Timer for the loop.
@type Timer
*/
this.timer=null;
/**
Flag - are we switched on?
@type Boolean
*/
this.active=false;
/**
Flag - are we probably inaccurate, i.e. not reflecting the actual mouse position?
*/
this.dirty=true;
/**
Array of hook functions.
@private
@type Array
*/
this.hooks=[];
}
/**
Adds a hook, to be called when we get events.
@param {Function} f A function which is called as
<code>f(x,y)</code>. It should return <code>true</code> when it
wants to be removed, and <code>false</code> otherwise.
*/
Mousetracker.prototype.addHook = function (f) {
this.hooks.push(f);
};
/**
Runs hooks, passing them the x
and y coords of the mouse. Hook functions that return true are
passed to {@link Mousetracker#removeHooks} for removal.
@private
*/
Mousetracker.prototype.runHooks = function () {
if (!this.hooks || !this.hooks.length) { return; }
//log('Mousetracker.runHooks; we got some hooks to run');
var remove=false;
var removeObj={};
// this method gets called a LOT -
// pre-cache some variables
var x=this.x, y=this.y, len = this.hooks.length;
for (var i=0; i<len; ++i) {
//~ run the hook function, and remove it if it returns true
if (this.hooks[i](x, y)===true) {
remove=true;
removeObj[i]=true;
}
}
if (remove) { this.removeHooks(removeObj); }
};
/**
Removes hooks.
@private
@param {Object} removeObj An object whose keys are the index
numbers of functions for removal, with values that evaluate to true
*/
Mousetracker.prototype.removeHooks = function(removeObj) {
var newHooks=[];
var len = this.hooks.length;
for (var i=0; i<len; ++i) {
if (! removeObj[i]) { newHooks.push(this.hooks[i]); }
}
this.hooks=newHooks;
};
/**
Event handler for mouse wiggles.
We simply grab the event, set x and y and run the hooks.
This makes the cpu all hot and bothered :-(
@private
@param {Event} e Mousemove event
*/
Mousetracker.prototype.track=function (e) {
//~ Apparently this is needed in IE.
e = e || window.event;
var x, y;
if (e) {
if (e.pageX) { x=e.pageX; y=e.pageY; }
else if (typeof e.clientX!='undefined') {
var left, top, docElt = document.documentElement;
if (docElt) { left=docElt.scrollLeft; }
left = left || document.body.scrollLeft || document.scrollLeft || 0;
if (docElt) { top=docElt.scrollTop; }
top = top || document.body.scrollTop || document.scrollTop || 0;
x=e.clientX + left;
y=e.clientY + top;
} else { return; }
this.setPosition(x,y);
}
};
/**
Sets the x and y coordinates stored and takes appropriate action,
running hooks as appropriate.
@param {Integer} x, y Screen coordinates to set
*/
Mousetracker.prototype.setPosition=function(x,y) {
this.x = x;
this.y = y;
if (this.dirty || this.hooks.length === 0) { this.dirty=false; return; }
if (typeof this.lastHook_x != 'number') { this.lastHook_x = -100; this.lastHook_y=-100; }
var diff = (this.lastHook_x - x)*(this.lastHook_y - y);
diff = (diff >= 0) ? diff : -diff;
if ( diff > 1 ) {
this.lastHook_x=x;
this.lastHook_y=y;
if (this.dirty) { this.dirty = false; }
else { this.runHooks(); }
}
};
/**
Sets things in motion, unless they are already that is, registering an event handler on <code>document.onmousemove</code>.
A half-hearted attempt is made to preserve the old event handler if there is one.
*/
Mousetracker.prototype.enable = function () {
if (this.active) { return; }
this.active=true;
//~ Save the current handler for mousemove events. This isn't too
//~ robust, of course.
this.savedHandler=document.onmousemove;
//~ Gotta save @tt{this} again for the closure, and use apply for
//~ the member function.
var savedThis=this;
document.onmousemove=function (e) {savedThis.track.apply(savedThis, [e]);};
if (this.loopDelay) { this.timer = setInterval(function() { //log('loop delay in mousetracker is working');
savedThis.runHooks();}, this.loopDelay); }
};
/**
Disables the tracker, removing the event handler.
*/
Mousetracker.prototype.disable = function () {
if (!this.active) { return; }
if (typeof this.savedHandler === 'function') {
document.onmousemove=this.savedHandler;
} else { delete document.onmousemove; }
if (this.timer) { clearInterval(this.timer); }
this.active=false;
};
/**
Creates a new Navpopup.
Gets a UID for the popup and
@param init Contructor object. If <code>init.draggable</code> is true or absent, the popup becomes draggable.
@constructor
@class The Navpopup class. This generates popup hints, and does some management of them.
*/
function Navpopup(/*init*/) {
//alert('new Navpopup(init)');
/** UID for each Navpopup instance.
Read-only.
@type integer
*/
this.uid=Navpopup.uid++;
/**
Read-only flag for current visibility of the popup.
@type boolean
@private
*/
this.visible=false;
/** Flag to be set when we want to cancel a previous request to
show the popup in a little while.
@private
@type boolean
*/
this.noshow=false;
/** Categorised list of hooks.
@see #runHooks
@see #addHook
@private
@type Object
*/
this.hooks={
'create': [],
'unhide': [],
'hide': []
};
/** list of unique IDs of hook functions, to avoid duplicates
@private
*/
this.hookIds={};
/** List of downloads associated with the popup.
@private
@type Array
*/
this.downloads=[];
/** Number of uncompleted downloads.
@type integer
*/
this.pending=null;
/** Tolerance in pixels when detecting whether the mouse has left the popup.
@type integer
*/
this.fuzz=5;
/** Flag to toggle running {@link #limitHorizontalPosition} to regulate the popup's position.
@type boolean
*/
this.constrained=true;
/** The popup width in pixels.
@private
@type integer
*/
this.width=0;
/** The popup width in pixels.
@private
@type integer
*/
this.height=0;
/** The main content DIV element.
@type HTMLDivElement
*/
this.mainDiv=null;
this.createMainDiv();
// if (!init || typeof init.popups_draggable=='undefined' || init.popups_draggable) {
// this.makeDraggable(true);
// }
}
/**
A UID for each Navpopup. This constructor property is just a counter.
@type integer
@private
*/
Navpopup.uid=0;
/**
Retrieves the {@link #visible} attribute, indicating whether the popup is currently visible.
@type boolean
*/
Navpopup.prototype.isVisible=function() {
return this.visible;
};
/**
Repositions popup using CSS style.
@private
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@param {boolean} noLimitHor Don't call {@link #limitHorizontalPosition}
*/
Navpopup.prototype.reposition= function (x,y, noLimitHor) {
log ('reposition('+x+','+y+','+noLimitHor+')');
if (typeof x != 'undefined' && x !== null) { this.left=x; }
if (typeof y != 'undefined' && y !== null) { this.top=y; }
if (typeof this.left != 'undefined' && typeof this.top != 'undefined') {
this.mainDiv.style.left=this.left + 'px';
this.mainDiv.style.top=this.top + 'px';
}
if (!noLimitHor) { this.limitHorizontalPosition(); }
//console.log('navpop'+this.uid+' - (left,top)=(' + this.left + ',' + this.top + '), css=('
//+ this.mainDiv.style.left + ',' + this.mainDiv.style.top + ')');
};
/**
Prevents popups from being in silly locations. Hopefully.
Should not be run if {@link #constrained} is true.
@private
*/
Navpopup.prototype.limitHorizontalPosition=function() {
if (!this.constrained || this.tooWide) { return; }
this.updateDimensions();
var x=this.left;
var w=this.width;
var cWidth=document.body.clientWidth;
// log('limitHorizontalPosition: x='+x+
// ', this.left=' + this.left +
// ', this.width=' + this.width +
// ', cWidth=' + cWidth);
if ( (x+w) >= cWidth ||
( x > 0 &&
this.maxWidth &&
this.width < this.maxWidth &&
this.height > this.width &&
x > cWidth - this.maxWidth ) ) {
// This is a very nasty hack. There has to be a better way!
// We find the "natural" width of the div by positioning it at the far left
// then reset it so that it should be flush right (well, nearly)
this.mainDiv.style.left='-10000px';
this.mainDiv.style.width = this.maxWidth + 'px';
var naturalWidth=parseInt(this.mainDiv.offsetWidth, 10);
var newLeft=cWidth - naturalWidth - 1;
if (newLeft < 0) { newLeft = 0; this.tooWide=true; } // still unstable for really wide popups?
log ('limitHorizontalPosition: moving to ('+newLeft + ','+ this.top+');' + ' naturalWidth=' + naturalWidth + ', clientWidth=' + cWidth);
this.reposition(newLeft, null, true);
}
};
/**
Counter indicating the z-order of the "highest" popup.
We start the z-index at 1000 so that popups are above everything
else on the screen.
@private
@type integer
*/
Navpopup.highest=1000;
/**
Brings popup to the top of the z-order.
We increment the {@link #highest} property of the contructor here.
@private
*/
Navpopup.prototype.raise = function () {
this.mainDiv.style.zIndex=Navpopup.highest + 1;
++Navpopup.highest;
};
/**
Shows the popup provided {@link #noshow} is not true.
Updates the position, brings the popup to the top of the z-order and unhides it.
*/
Navpopup.prototype.show = function () {
//document.title+='s';
if (this.noshow) { return; }
//document.title+='t';
this.reposition();
this.raise();
this.unhide();
};
/**
Checks to see if the mouse pointer has
stabilised (checking every <code>time</code>/2 milliseconds) and runs the
{@link #show} method if it has.
@param {integer} time The minimum time (ms) before the popup may be shown.
*/
Navpopup.prototype.showSoonIfStable = function (time) {
log ('showSoonIfStable, time='+time);
if (this.visible) { return; }
this.noshow = false;
//~ initialize these variables so that we never run @tt{show} after
//~ just half the time
this.stable_x = -10000; this.stable_y = -10000;
var stableShow = function() {
log('stableShow called');
var new_x = Navpopup.tracker.x, new_y = Navpopup.tracker.y;
var dx = savedThis.stable_x - new_x, dy = savedThis.stable_y - new_y;
var fuzz2 = 0; // savedThis.fuzz * savedThis.fuzz;
//document.title += '[' + [savedThis.stable_x,new_x, savedThis.stable_y,new_y, dx, dy, fuzz2].join(',') + '] ';
if ( dx * dx <= fuzz2 && dy * dy <= fuzz2 ) {
log ('mouse is stable');
clearInterval(savedThis.showSoonStableTimer);
savedThis.reposition.apply(savedThis, [new_x + 2, new_y + 2]);
savedThis.show.apply(savedThis, []);
savedThis.limitHorizontalPosition.apply(savedThis, []);
return;
}
savedThis.stable_x = new_x; savedThis.stable_y = new_y;
};
var savedThis = this;
this.showSoonStableTimer = setInterval(stableShow, time/2);
};
/**
Sets the {@link #noshow} flag and hides the popup. This should be called
when the mouse leaves the link before
(or after) it's actually been displayed.
*/
Navpopup.prototype.banish = function () {
log ('banish called');
// hide and prevent showing with showSoon in the future
this.noshow=true;
if (this.showSoonStableTimer) {
log('clearing showSoonStableTimer');
clearInterval(this.showSoonStableTimer);
}
this.hide();
};
/**
Runs hooks added with {@link #addHook}.
@private
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
*/
Navpopup.prototype.runHooks = function (key, when) {
if (!this.hooks[key]) { return; }
var keyHooks=this.hooks[key];
var len=keyHooks.length;
for (var i=0; i< len; ++i) {
if (keyHooks[i] && keyHooks[i].when == when) {
if (keyHooks[i].hook.apply(this, [])) {
// remove the hook
if (keyHooks[i].hookId) {
delete this.hookIds[keyHooks[i].hookId];
}
keyHooks[i]=null;
}
}
}
};
/**
Adds a hook to the popup. Hook functions are run with <code>this</code> set to refer to the Navpopup instance, and no arguments.
@param {Function} hook The hook function. Functions that return true are deleted.
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
@param {String} uid A truthy string identifying the hook function; if it matches another hook in this position, it won't be added again.
*/
Navpopup.prototype.addHook = function ( hook, key, when, uid ) {
when = when || 'after';
if (!this.hooks[key]) { return; }
// if uid is specified, don't add duplicates
var hookId=null;
if (uid) {
hookId=[key,when,uid].join('|');
if (this.hookIds[hookId]) {
return;
}
this.hookIds[hookId]=true;
}
this.hooks[key].push( {hook: hook, when: when, hookId: hookId} );
};
/**
Creates the main DIV element, which contains all the actual popup content.
Runs hooks with key 'create'.
@private
*/
Navpopup.prototype.createMainDiv = function () {
if (this.mainDiv) { return; }
this.runHooks('create', 'before');
var mainDiv=document.createElement('div');
var savedThis=this;
mainDiv.onclick=function(e) {savedThis.onclickHandler(e);};
mainDiv.className=(this.className) ? this.className : 'navpopup_maindiv';
mainDiv.id=mainDiv.className + this.uid;
mainDiv.style.position='absolute';
mainDiv.style.minWidth = '350px';
mainDiv.style.display='none';
mainDiv.className='navpopup';
// easy access to javascript object through DOM functions
mainDiv.navpopup=this;
this.mainDiv=mainDiv;
document.body.appendChild(mainDiv);
this.runHooks('create', 'after');
};
/**
Calls the {@link #raise} method.
@private
*/
Navpopup.prototype.onclickHandler=function(/*e*/) {
this.raise();
};
/**
Makes the popup draggable, using a {@link Drag} object.
@private
*/
Navpopup.prototype.makeDraggable=function(handleName) {
if (!this.mainDiv) { this.createMainDiv(); }
var drag=new Drag();
if (!handleName) {
drag.startCondition=function(e) {
try { if (!e.shiftKey) { return false; } } catch (err) { return false; }
return true;
};
}
var dragHandle;
if (handleName) dragHandle = document.getElementById(handleName);
if (!dragHandle) dragHandle = this.mainDiv;
var np=this;
drag.endHook=function(x,y) {
Navpopup.tracker.dirty=true;
np.reposition(x,y);
};
drag.init(dragHandle,this.mainDiv);
};
/** Hides the popup using CSS. Runs hooks with key 'hide'.
Sets {@link #visible} appropriately. {@link #banish} should be called externally instead of this method.
@private
*/
Navpopup.prototype.hide = function () {
this.runHooks('hide', 'before');
this.abortDownloads();
if (typeof this.visible != 'undefined' && this.visible) {
this.mainDiv.style.display='none';
this.visible=false;
}
this.runHooks('hide', 'after');
};
/** Shows the popup using CSS. Runs hooks with key 'unhide'.
Sets {@link #visible} appropriately. {@link #show} should be called externally instead of this method.
@private
*/
Navpopup.prototype.unhide = function () {
this.runHooks('unhide', 'before');
if (typeof this.visible != 'undefined' && !this.visible) {
this.mainDiv.style.display='inline';
this.visible=true;
}
this.runHooks('unhide', 'after');
};
/**
Sets the <code>innerHTML</code> attribute of the main div containing the popup content.
@param {String} html The HTML to set.
*/
Navpopup.prototype.setInnerHTML = function (html) {
this.mainDiv.innerHTML = html;
};
/**
Updates the {@link #width} and {@link #height} attributes with the CSS properties.
@private
*/
Navpopup.prototype.updateDimensions = function () {
this.width=parseInt(this.mainDiv.offsetWidth, 10);
this.height=parseInt(this.mainDiv.offsetHeight, 10);
};
/**
Checks if the point (x,y) is within {@link #fuzz} of the
{@link #mainDiv}.
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@type boolean
*/
Navpopup.prototype.isWithin = function(x,y) {
//~ If we're not even visible, no point should be considered as
//~ being within the popup.
if (!this.visible) { return false; }
this.updateDimensions();
var fuzz=this.fuzz || 0;
//~ Use a simple box metric here.
return (x+fuzz >= this.left && x-fuzz <= this.left + this.width &&
y+fuzz >= this.top && y-fuzz <= this.top + this.height);
};
/**
Adds a download to {@link #downloads}.
@param {Downloader} download
*/
Navpopup.prototype.addDownload=function(download) {
if (!download) { return; }
this.downloads.push(download);
};
/**
Aborts the downloads listed in {@link #downloads}.
@see Downloader#abort
*/
Navpopup.prototype.abortDownloads=function() {
for(var i=0; i<this.downloads.length; ++i) {
var d=this.downloads[i];
if (d && d.abort) { d.abort(); }
}
this.downloads=[];
};
/**
A {@link Mousetracker} instance which is a property of the constructor (pseudo-global).
*/
Navpopup.tracker=new Mousetracker();
// ENDFILE: navpopup.js
// STARTFILE: diff.js
//<NOLITE>
/*
* Javascript Diff Algorithm
* By John Resig (http://ejohn.org/) and [[:en:User:Lupin]]
*
* More Info:
* http://ejohn.org/projects/javascript-diff-algorithm/
*/
function delFmt(x) {
if (!x.length) { return ''; }
return "<del class='popupDiff'>" + x.join('') +"</del>";
}
function insFmt(x) {
if (!x.length) { return ''; }
return "<ins class='popupDiff'>" + x.join('') +"</ins>";
}
function countCrossings(a, b, i, eject) {
// count the crossings on the edge starting at b[i]
if (!b[i].row && b[i].row !== 0) { return -1; }
var count=0;
for (var j=0; j<a.length; ++j) {
if (!a[j].row && a[j].row !== 0) { continue; }
if ( (j-b[i].row)*(i-a[j].row) > 0) {
if(eject) { return true; }
count++;
}
}
return count;
}
function shortenDiffString(str, context) {
var re=RegExp('(<del[\\s\\S]*?</del>|<ins[\\s\\S]*?</ins>)');
var splitted=str.parenSplit(re);
var ret=[''];
for (var i=0; i<splitted.length; i+=2) {
if (splitted[i].length < 2*context) {
ret[ret.length-1] += splitted[i];
if (i+1<splitted.length) { ret[ret.length-1] += splitted[i+1]; }
continue;
}
else {
if (i > 0) { ret[ret.length-1] += splitted[i].substring(0,context); }
if (i+1 < splitted.length) {
ret.push(splitted[i].substring(splitted[i].length-context) +
splitted[i+1]);
}
}
}
while (ret.length > 0 && !ret[0]) { ret = ret.slice(1); }
return ret;
}
function diffString( o, n, simpleSplit ) {
var splitRe=RegExp('([[]{2}|[\\]]{2}|[{]{2,3}|[}]{2,3}|[|]|=|<|>|[*:]+|\\s|\\b)');
// We need to split the strings o and n first, and entify() the parts
// individually, so that the HTML entities are never cut apart. (AxelBoldt)
var out, i, oSplitted, nSplitted;
if (simpleSplit) {
oSplitted=o.split(/\b/);
nSplitted=n.split(/\b/);
} else {
oSplitted=o.parenSplit(splitRe);
nSplitted=n.parenSplit(splitRe);
}
for (i=0; i<oSplitted.length; ++i) {oSplitted[i]=oSplitted[i].entify();}
for (i=0; i<nSplitted.length; ++i) {nSplitted[i]=nSplitted[i].entify();}
out = diff (oSplitted, nSplitted);
var str = "";
var acc=[]; // accumulator for prettier output
// crossing pairings -- eg 'A B' vs 'B A' -- cause problems, so let's iron them out
// this doesn't always do things optimally but it should be fast enough
var maxOutputPair=0;
for (i=0; i<out.n.length; ++i) {
if ( out.n[i].paired ) {
if( maxOutputPair > out.n[i].row ) {
// tangle - delete pairing
out.o[ out.n[i].row ]=out.o[ out.n[i].row ].text;
out.n[i]=out.n[i].text;
}
if (maxOutputPair < out.n[i].row) { maxOutputPair = out.n[i].row; }
}
}
// output the stuff preceding the first paired old line
for (i=0; i<out.o.length && !out.o[i].paired; ++i) { acc.push( out.o[i] ); }
str += delFmt(acc); acc=[];
// main loop
for ( i = 0; i < out.n.length; ++i ) {
// output unpaired new "lines"
while ( i < out.n.length && !out.n[i].paired ) { acc.push( out.n[i++] ); }
str += insFmt(acc); acc=[];
if ( i < out.n.length ) { // this new "line" is paired with the (out.n[i].row)th old "line"
str += out.n[i].text;
// output unpaired old rows starting after this new line's partner
var m = out.n[i].row + 1;
while ( m < out.o.length && !out.o[m].paired ) { acc.push ( out.o[m++] ); }
str += delFmt(acc); acc=[];
}
}
return str;
}
// see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Object
// FIXME: use obj.hasOwnProperty instead of this kludge!
var jsReservedProperties=RegExp('^(constructor|prototype|__((define|lookup)[GS]etter)__' +
'|eval|hasOwnProperty|propertyIsEnumerable' +
'|to(Source|String|LocaleString)|(un)?watch|valueOf)$');
function diffBugAlert(word) {
if (!diffBugAlert.list[word]) {
diffBugAlert.list[word]=1;
alert('Bad word: '+word+'\n\nPlease report this bug.');
}
}
diffBugAlert.list={};
function makeDiffHashtable(src) {
var ret={};
for ( var i = 0; i < src.length; i++ ) {
if ( jsReservedProperties.test(src[i]) ) { src[i] += '<!-- -->'; }
if ( !ret[ src[i] ] ) { ret[ src[i] ] = []; }
try { ret[ src[i] ].push( i ); } catch (err) { diffBugAlert(src[i]); }
}
return ret;
}
function diff( o, n ) {
// pass 1: make hashtable ns with new rows as keys
var ns = makeDiffHashtable(n);
// pass 2: make hashtable os with old rows as keys
var os = makeDiffHashtable(o);
// pass 3: pair unique new rows and matching unique old rows
var i;
for ( i in ns ) {
if ( ns[i].length == 1 && os[i] && os[i].length == 1 ) {
n[ ns[i][0] ] = { text: n[ ns[i][0] ], row: os[i][0], paired: true };
o[ os[i][0] ] = { text: o[ os[i][0] ], row: ns[i][0], paired: true };
}
}
// pass 4: pair matching rows immediately following paired rows (not necessarily unique)
for ( i = 0; i < n.length - 1; i++ ) {
if ( n[i].paired && ! n[i+1].paired && n[i].row + 1 < o.length && ! o[ n[i].row + 1 ].paired &&
n[i+1] == o[ n[i].row + 1 ] ) {
n[i+1] = { text: n[i+1], row: n[i].row + 1, paired: true };
o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1, paired: true };
}
}
// pass 5: pair matching rows immediately preceding paired rows (not necessarily unique)
for ( i = n.length - 1; i > 0; i-- ) {
if ( n[i].paired && ! n[i-1].paired && n[i].row > 0 && ! o[ n[i].row - 1 ].paired &&
n[i-1] == o[ n[i].row - 1 ] ) {
n[i-1] = { text: n[i-1], row: n[i].row - 1, paired: true };
o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1, paired: true };
}
}
return { o: o, n: n };
}
//</NOLITE>
// ENDFILE: diff.js
// STARTFILE: init.js
function setSiteInfo() {
if (window.popupLocalDebug) {
pg.wiki.hostname = 'en.wikipedia.org';
} else {
pg.wiki.hostname = location.hostname; // use in preference to location.hostname for flexibility (?)
}
pg.wiki.wikimedia=RegExp('(wiki([pm]edia|source|books|news|quote|versity)|wiktionary|mediawiki)[.]org').test(pg.wiki.hostname);
pg.wiki.wikia=RegExp('[.]wikia[.]com$', 'i').test(pg.wiki.hostname);
pg.wiki.isLocal=RegExp('^localhost').test(pg.wiki.hostname);
pg.wiki.commons=( pg.wiki.wikimedia && pg.wiki.hostname != 'commons.wikimedia.org') ? 'commons.wikimedia.org' : null;
pg.wiki.lang = mw.config.get('wgContentLanguage');
var port = location.port ? ':' + location.port : '';
pg.wiki.sitebase = pg.wiki.hostname + port;
}
function setUserInfo() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
list: 'users',
ususers: mw.config.get('wgUserName'),
usprop: 'rights'
};
pg.user.canReview = false;
if (getValueOf('popupReview')) {
api.get(params).done(function(data){
var rights = data.query.users[0].rights;
pg.user.canReview = rights.indexOf('review') !== -1; // TODO: Should it be a getValueOf('ReviewRight') ?
});
}
}
function setTitleBase() {
var protocol = ( window.popupLocalDebug ? 'http:' : location.protocol );
pg.wiki.articlePath = mw.config.get('wgArticlePath').replace(/\/\$1/, ""); // as in http://some.thing.com/wiki/Article
pg.wiki.botInterfacePath = mw.config.get('wgScript');
pg.wiki.APIPath = mw.config.get('wgScriptPath') +"/api.php";
// default mediawiki setting is paths like http://some.thing.com/articlePath/index.php?title=foo
var titletail = pg.wiki.botInterfacePath + '?title=';
//var titletail2 = joinPath([pg.wiki.botInterfacePath, 'wiki.phtml?title=']);
// other sites may need to add code here to set titletail depending on how their urls work
pg.wiki.titlebase = protocol + '//' + pg.wiki.sitebase + titletail;
//pg.wiki.titlebase2 = protocol + '//' + joinPath([pg.wiki.sitebase, titletail2]);
pg.wiki.wikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.botInterfacePath;
pg.wiki.apiwikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.APIPath;
pg.wiki.articlebase = protocol + '//' + pg.wiki.sitebase + pg.wiki.articlePath;
pg.wiki.commonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.botInterfacePath;
pg.wiki.apicommonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.APIPath;
pg.re.basenames = RegExp( '^(' +
map( literalizeRegex, [ pg.wiki.titlebase, //pg.wiki.titlebase2,
pg.wiki.articlebase ]).join('|') + ')' );
}
//////////////////////////////////////////////////
// Global regexps
function setMainRegex() {
var reStart='[^:]*://';
var preTitles = literalizeRegex( mw.config.get('wgScriptPath') ) + '/(?:index[.]php|wiki[.]phtml)[?]title=';
preTitles += '|' + literalizeRegex( pg.wiki.articlePath + '/' );
var reEnd='(' + preTitles + ')([^&?#]*)[^#]*(?:#(.+))?';
pg.re.main = RegExp(reStart + literalizeRegex(pg.wiki.sitebase) + reEnd);
}
function setRegexps() {
// TODO: We shoud use an api call to get the aliases for special pages, now it does not work for non-English wikipedias:
// E.g., https://ru.wikipedia.org/w/api.php?action=query&meta=siteinfo&siprop=specialpagealiases&formatversion=2
setMainRegex();
var sp=nsRe(pg.nsSpecialId);
pg.re.urlNoPopup=RegExp('((title=|/)' + sp + '(?:%3A|:)|section=[0-9]|^#$)') ;
pg.re.contribs =RegExp('(title=|/)' + sp + '(?:%3A|:)Contributions' + '(&target=|/|/' + nsRe(pg.nsUserId)+':)(.*)') ;
pg.re.email =RegExp('(title=|/)' + sp + '(?:%3A|:)EmailUser' + '(&target=|/|/(?:' + nsRe(pg.nsUserId)+':)?)(.*)') ;
pg.re.backlinks =RegExp('(title=|/)' + sp + '(?:%3A|:)WhatLinksHere' + '(&target=|/)([^&]*)');
pg.re.specialdiff=RegExp('/' + sp + '(?:%3A|:)Diff/([^?#]*)');
//<NOLITE>
var im=nsReImage();
// note: tries to get images in infobox templates too, e.g. movie pages, album pages etc
// (^|\[\[)image: *([^|\]]*[^|\] ]) *
// (^|\[\[)image: *([^|\]]*[^|\] ])([^0-9\]]*([0-9]+) *px)?
// $4 = 120 as in 120px
pg.re.image = RegExp('(^|\\[\\[)' + im + ': *([^|\\]]*[^|\\] ])' +
'([^0-9\\]]*([0-9]+) *px)?|(?:\\n *[|]?|[|]) *' +
'(' + getValueOf('popupImageVarsRegexp') + ')' +
' *= *(?:\\[\\[ *)?(?:' + im + ':)?' +
'([^|]*?)(?:\\]\\])? *[|]? *\\n', 'img') ;
pg.re.imageBracketCount = 6;
pg.re.category = RegExp('\\[\\[' +nsRe(pg.nsCategoryId) +
': *([^|\\]]*[^|\\] ]) *', 'i');
pg.re.categoryBracketCount = 1;
pg.re.ipUser=RegExp('^' +
// IPv6
'(?::(?::|(?::[0-9A-Fa-f]{1,4}){1,7})|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,6}::|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){7})' +
// IPv4
'|(((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}' +
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9]))$');
pg.re.stub= RegExp(getValueOf('popupStubRegexp'), 'im');
pg.re.disambig=RegExp(getValueOf('popupDabRegexp'), 'im');
//</NOLITE>
// FIXME replace with general parameter parsing function, this is daft
pg.re.oldid=RegExp('[?&]oldid=([^&]*)');
pg.re.diff=RegExp('[?&]diff=([^&]*)');
}
//////////////////////////////////////////////////
// miscellany
function setupCache() {
// page caching
pg.cache.pages = [];
}
function setMisc() {
pg.current.link=null;
pg.current.links=[];
pg.current.linksHash={};
setupCache();
pg.timer.checkPopupPosition=null;
pg.counter.loop=0;
// ids change with each popup: popupImage0, popupImage1 etc
pg.idNumber=0;
// for myDecodeURI
pg.misc.decodeExtras = [
{from: '%2C', to: ',' },
{from: '_', to: ' ' },
{from: '%24', to: '$'},
{from: '%26', to: '&' } // no ,
];
pg.misc.userAgent = 'Navigation popups/1.0 (' + mw.config.get( 'wgServerName' ) +')';
}
// We need a callback since this might end up asynchronous because of
// the mw.loader.using() call.
function setupPopups( callback ) {
if ( setupPopups.completed ) {
if ( typeof callback === 'function' ) {
callback();
}
return;
}
// These dependencies are also enforced from the gadget,
// but not everyone loads this as a gadget, so double check
mw.loader.using( ['mediawiki.util', 'mediawiki.user', 'user.options'] ).then( function() {
// NB translatable strings should be set up first (strings.js)
// basics
setupDebugging();
setSiteInfo();
setTitleBase();
setOptions(); // see options.js
setUserInfo();
// namespaces etc
setNamespaces();
setInterwiki();
// regexps
setRegexps();
setRedirs();
// other stuff
setMisc();
setupLivePreview();
// main deal here
setupTooltips();
log('In setupPopups(), just called setupTooltips()');
Navpopup.tracker.enable();
setupPopups.completed = true;
if ( typeof callback === 'function' ) {
callback();
}
});
}
// ENDFILE: init.js
// STARTFILE: navlinks.js
//<NOLITE>
//////////////////////////////////////////////////
// navlinks... let the fun begin
//
function defaultNavlinkSpec() {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
if (getValueOf('popupLastEditLink')) {
str += '*<<lastEdit|shortcut=/>>|<<lastContrib>>|<<sinceMe>>if(oldid){|<<oldEdit>>|<<diffCur>>}';
}
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>*<<userlog|shortcut=L|log>>';
str+='if(ipuser){*<<arin>>}if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>|<<blocklog|log>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' + editstr + '}';
var historystr='<<history|shortcut=h>>|<<editors|shortcut=E|>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str+='<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>*<<move|shortcut=m>>';
// admin links
str += 'if(admin){<br><<unprotect|unprotectShort>>|<<protect|shortcut=p>>|<<protectlog|log>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>|<<deletelog|log>>}';
return str;
}
function navLinksHTML (article, hint, params) { //oldid, rcid) {
var str = '<span class="popupNavLinks">' + defaultNavlinkSpec() + '</span>';
// BAM
return navlinkStringToHTML(str, article, params);
}
function expandConditionalNavlinkString(s,article,z,recursionCount) {
var oldid=z.oldid, rcid=z.rcid, diff=z.diff;
// nested conditionals (up to 10 deep) are ok, hopefully! (work from the inside out)
if (typeof recursionCount!=typeof 0) { recursionCount=0; }
var conditionalSplitRegex=RegExp(
//(1 if \\( (2 2) \\) {(3 3)} (4 else {(5 5)} 4)1)
'(;?\\s*if\\s*\\(\\s*([\\w]*)\\s*\\)\\s*\\{([^{}]*)\\}(\\s*else\\s*\\{([^{}]*?)\\}|))', 'i');
var splitted=s.parenSplit(conditionalSplitRegex);
// $1: whole conditional
// $2: test condition
// $3: true expansion
// $4: else clause (possibly empty)
// $5: false expansion (possibly null)
var numParens=5;
var ret = splitted[0];
for (var i=1; i<splitted.length; i=i+numParens+1) {
var testString=splitted[i+2-1];
var trueString=splitted[i+3-1];
var falseString=splitted[i+5-1];
if (typeof falseString=='undefined' || !falseString) { falseString=''; }
var testResult=null;
switch (testString) {
case 'user':
testResult=(article.userName())?true:false;
break;
case 'talk':
testResult=(article.talkPage())?false:true; // talkPage converts _articles_ to talkPages
break;
case 'admin':
testResult=getValueOf('popupAdminLinks')?true:false;
break;
case 'oldid':
testResult=(typeof oldid != 'undefined' && oldid)?true:false;
break;
case 'rcid':
testResult=(typeof rcid != 'undefined' && rcid)?true:false;
break;
case 'ipuser':
testResult=(article.isIpUser())?true:false;
break;
case 'mainspace_en':
testResult=isInMainNamespace(article) &&
pg.wiki.hostname=='en.wikipedia.org';
break;
case 'wikimedia':
testResult=(pg.wiki.wikimedia) ? true : false;
break;
case 'diff':
testResult=(typeof diff != 'undefined' && diff)?true:false;
break;
}
switch(testResult) {
case null: ret+=splitted[i]; break;
case true: ret+=trueString; break;
case false: ret+=falseString; break;
}
// append non-conditional string
ret += splitted[i+numParens];
}
if (conditionalSplitRegex.test(ret) && recursionCount < 10) {
return expandConditionalNavlinkString(ret,article,z,recursionCount+1);
}
return ret;
}
function navlinkStringToArray(s, article, params) {
s=expandConditionalNavlinkString(s,article,params);
var splitted=s.parenSplit(RegExp('<<(.*?)>>'));
var ret=[];
for (var i=0; i<splitted.length; ++i) {
if (i%2) { // i odd, so s is a tag
var t=new navlinkTag();
var ss=splitted[i].split('|');
t.id=ss[0];
for (var j=1; j<ss.length; ++j) {
var sss=ss[j].split('=');
if (sss.length>1) {
t[sss[0]]=sss[1];
}
else { // no assignment (no "="), so treat this as a title (overwriting the last one)
t.text=popupString(sss[0]);
}
}
t.article=article;
var oldid=params.oldid, rcid=params.rcid, diff=params.diff;
if (typeof oldid !== 'undefined' && oldid !== null) { t.oldid=oldid; }
if (typeof rcid !== 'undefined' && rcid !== null) { t.rcid=rcid; }
if (typeof diff !== 'undefined' && diff !== null) { t.diff=diff; }
if (!t.text && t.id !== 'mainlink') { t.text=popupString(t.id); }
ret.push(t);
}
else { // plain HTML
ret.push(splitted[i]);
}
}
return ret;
}
function navlinkSubstituteHTML(s) {
return s.split('*').join(getValueOf('popupNavLinkSeparator'))
.split('<menurow>').join('<li class="popup_menu_row">')
.split('</menurow>').join('</li>')
.split('<menu>').join('<ul class="popup_menu">')
.split('</menu>').join('</ul>');
}
function navlinkDepth(magic,s) {
return s.split('<' + magic + '>').length - s.split('</' + magic + '>').length;
}
// navlinkString: * becomes the separator
// <<foo|bar=baz|fubar>> becomes a foo-link with attribute bar='baz'
// and visible text 'fubar'
// if(test){...} and if(test){...}else{...} work too (nested ok)
function navlinkStringToHTML(s,article,params) {
//limitAlert(navlinkStringToHTML, 5, 'navlinkStringToHTML\n' + article + '\n' + (typeof article));
var p=navlinkStringToArray(s,article,params);
var html='';
var menudepth = 0; // nested menus not currently allowed, but doesn't do any harm to code for it
var menurowdepth = 0;
for (var i=0; i<p.length; ++i) {
if (typeof p[i] == typeof '') {
html+=navlinkSubstituteHTML(p[i]);
menudepth += navlinkDepth('menu', p[i]);
menurowdepth += navlinkDepth('menurow', p[i]);
// if (menudepth === 0) {
// tagType='span';
// } else if (menurowdepth === 0) {
// tagType='li';
// } else {
// tagType = null;
// }
} else if (typeof p[i].type != 'undefined' && p[i].type=='navlinkTag') {
if (menudepth > 0 && menurowdepth === 0) {
html += '<li class="popup_menu_item">' + p[i].html() + '</li>';
} else {
html+=p[i].html();
}
}
}
return html;
}
function navlinkTag() {
this.type='navlinkTag';
}
navlinkTag.prototype.html=function () {
this.getNewWin();
this.getPrintFunction();
var html='';
var opening, closing;
var tagType='span';
if (!tagType) {
opening = ''; closing = '';
} else {
opening = '<' + tagType + ' class="popup_' + this.id + '">';
closing = '</' + tagType + '>';
}
if (typeof this.print!='function') {
errlog ('Oh dear - invalid print function for a navlinkTag, id='+this.id);
} else {
html=this.print(this);
if (typeof html != typeof '') {html='';}
else if (typeof this.shortcut!='undefined') html=addPopupShortcut(html, this.shortcut);
}
return opening + html + closing;
};
navlinkTag.prototype.getNewWin=function() {
getValueOf('popupLinksNewWindow');
if (typeof pg.option.popupLinksNewWindow[this.id] === 'undefined') { this.newWin=null; }
this.newWin=pg.option.popupLinksNewWindow[this.id];
};
navlinkTag.prototype.getPrintFunction=function() { //think about this some more
// this.id and this.article should already be defined
if (typeof this.id!=typeof '' || typeof this.article!=typeof {} ) { return; }
this.noPopup=1;
switch (this.id) {
case 'contribs': case 'history': case 'whatLinksHere':
case 'userPage': case 'monobook': case 'userTalk':
case 'talk': case 'article': case 'lastEdit':
this.noPopup=null;
}
switch (this.id) {
case 'email': case 'contribs': case 'block': case 'unblock':
case 'userlog': case 'userSpace': case 'deletedContribs':
this.article=this.article.userName();
}
switch (this.id) {
case 'userTalk': case 'newUserTalk': case 'editUserTalk':
case 'userPage': case 'monobook': case 'editMonobook': case 'blocklog':
this.article=this.article.userName(true);
/* fall through */
case 'pagelog': case 'deletelog': case 'protectlog':
delete this.oldid;
}
if (this.id=='editMonobook' || this.id=='monobook') { this.article.append('/monobook.js'); }
if (this.id != 'mainlink') {
// FIXME anchor handling should be done differently with Title object
this.article=this.article.removeAnchor();
// if (typeof this.text=='undefined') this.text=popupString(this.id);
}
switch (this.id) {
case 'undelete': this.print=specialLink; this.specialpage='Undelete'; this.sep='/'; break;
case 'whatLinksHere': this.print=specialLink; this.specialpage='Whatlinkshere'; break;
case 'relatedChanges': this.print=specialLink; this.specialpage='Recentchangeslinked'; break;
case 'move': this.print=specialLink; this.specialpage='Movepage'; break;
case 'contribs': this.print=specialLink; this.specialpage='Contributions'; break;
case 'deletedContribs':this.print=specialLink; this.specialpage='Deletedcontributions'; break;
case 'email': this.print=specialLink; this.specialpage='EmailUser'; this.sep='/'; break;
case 'block': this.print=specialLink; this.specialpage='Blockip'; this.sep='&ip='; break;
case 'unblock': this.print=specialLink; this.specialpage='Ipblocklist'; this.sep='&action=unblock&ip='; break;
case 'userlog': this.print=specialLink; this.specialpage='Log'; this.sep='&user='; break;
case 'blocklog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=block&page='; break;
case 'pagelog': this.print=specialLink; this.specialpage='Log'; this.sep='&page='; break;
case 'protectlog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=protect&page='; break;
case 'deletelog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=delete&page='; break;
case 'userSpace': this.print=specialLink; this.specialpage='PrefixIndex'; this.sep='&namespace=2&prefix='; break;
case 'search': this.print=specialLink; this.specialpage='Search'; this.sep='&fulltext=Search&search='; break;
case 'thank': this.print=specialLink; this.specialpage='Thanks'; this.sep='/'; this.article.value = this.diff; break;
case 'unwatch': case 'watch':
this.print=magicWatchLink; this.action=this.id+'&autowatchlist=1&autoimpl=' + popupString('autoedit_version') + '&actoken='+autoClickToken(); break;
case 'history': case 'historyfeed':
case 'unprotect': case 'protect':
this.print=wikiLink; this.action=this.id; break;
case 'delete':
this.print=wikiLink; this.action='delete';
if (this.article.namespaceId()==pg.nsImageId) {
var img=this.article.stripNamespace();
this.action+='&image='+img;
}
break;
case 'markpatrolled':
case 'edit': // editOld should keep the oldid, but edit should not.
delete this.oldid;
/* fall through */
case 'view': case 'purge': case 'render':
this.print=wikiLink;
this.action=this.id; break;
case 'raw':
this.print=wikiLink; this.action='raw'; break;
case 'new':
this.print=wikiLink; this.action='edit§ion=new'; break;
case 'mainlink':
if (typeof this.text=='undefined') { this.text=this.article.toString().entify(); }
if (getValueOf('popupSimplifyMainLink') && isInStrippableNamespace(this.article)) {
// only show the /subpage part of the title text
var s=this.text.split('/'); this.text=s[s.length-1];
if (this.text==='' && s.length > 1) { this.text=s[s.length-2]; }
}
this.print=titledWikiLink;
if (typeof this.title==='undefined' && pg.current.link && typeof pg.current.link.href !== 'undefined') {
this.title=safeDecodeURI((pg.current.link.originalTitle)?pg.current.link.originalTitle:this.article);
if (typeof this.oldid !== 'undefined' && this.oldid) {
this.title=tprintf('Revision %s of %s', [this.oldid, this.title]);
}
}
this.action='view'; break;
case 'userPage':
case 'article':
case 'monobook':
case 'editMonobook':
case 'editArticle':
delete this.oldid;
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.article=this.article.articleFromTalkOrArticle();
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.print=wikiLink;
if (this.id.indexOf('edit')===0) {
this.action='edit';
} else { this.action='view';}
break;
case 'userTalk':
case 'talk':
this.article=this.article.talkPage();
delete this.oldid;
this.print=wikiLink;
this.action='view'; break;
case 'arin':
this.print=arinLink; break;
case 'count':
this.print=editCounterLink; break;
case 'google':
this.print=googleLink; break;
case 'editors':
this.print=editorListLink; break;
case 'globalsearch':
this.print=globalSearchLink; break;
case 'lastEdit':
this.print=titledDiffLink;
this.title=popupString('Show the last edit');
this.from='prev'; this.to='cur'; break;
case 'oldEdit':
this.print=titledDiffLink;
this.title=popupString('Show the edit made to get revision') + ' ' + this.oldid;
this.from='prev'; this.to=this.oldid; break;
case 'editOld':
this.print=wikiLink; this.action='edit'; break;
case 'undo':
this.print=wikiLink; this.action='edit&undo='; break;
case 'revert':
this.print=wikiLink; this.action='revert'; break;
case 'nullEdit':
this.print=wikiLink; this.action='nullEdit'; break;
case 'diffCur':
this.print=titledDiffLink;
this.title=tprintf('Show changes since revision %s', [this.oldid]);
this.from=this.oldid; this.to='cur'; break;
case 'editUserTalk':
case 'editTalk':
delete this.oldid;
this.article=this.article.talkPage();
this.action='edit'; this.print=wikiLink; break;
case 'newUserTalk':
case 'newTalk':
this.article=this.article.talkPage();
this.action='edit§ion=new'; this.print=wikiLink; break;
case 'lastContrib':
case 'sinceMe':
this.print=magicHistoryLink;
break;
case 'togglePreviews':
this.text=popupString(pg.option.simplePopups ? 'enable previews' : 'disable previews');
/* fall through */
case 'disablePopups': case 'purgePopups':
this.print=popupMenuLink;
break;
default:
this.print=function () {return 'Unknown navlink type: '+this.id+'';};
}
};
//
// end navlinks
//////////////////////////////////////////////////
//</NOLITE>
// ENDFILE: navlinks.js
// STARTFILE: shortcutkeys.js
//<NOLITE>
function popupHandleKeypress(evt) {
var keyCode = window.event ? window.event.keyCode : ( evt.keyCode ? evt.keyCode : evt.which);
if (!keyCode || !pg.current.link || !pg.current.link.navpopup) { return; }
if (keyCode==27) { // escape
killPopup();
return false; // swallow keypress
}
var letter=String.fromCharCode(keyCode);
var links=pg.current.link.navpopup.mainDiv.getElementsByTagName('A');
var startLink=0;
var i,j;
if (popupHandleKeypress.lastPopupLinkSelected) {
for (i=0; i<links.length; ++i) {
if (links[i]==popupHandleKeypress.lastPopupLinkSelected) { startLink=i; }
}
}
for (j=0; j<links.length; ++j) {
i=(startLink + j + 1) % links.length;
if (links[i].getAttribute('popupkey')==letter) {
if (evt && evt.preventDefault) evt.preventDefault();
links[i].focus();
popupHandleKeypress.lastPopupLinkSelected=links[i];
return false; // swallow keypress
}
}
// pass keypress on
if (document.oldPopupOnkeypress) { return document.oldPopupOnkeypress(evt); }
return true;
}
function addPopupShortcuts() {
if (document.onkeypress!=popupHandleKeypress) {
document.oldPopupOnkeypress=document.onkeypress;
}
document.onkeypress=popupHandleKeypress;
}
function rmPopupShortcuts() {
popupHandleKeypress.lastPopupLinkSelected=null;
try {
if (document.oldPopupOnkeypress && document.oldPopupOnkeypress==popupHandleKeypress) {
// panic
document.onkeypress=null; //function () {};
return;
}
document.onkeypress=document.oldPopupOnkeypress;
} catch (nasties) { /* IE goes here */ }
}
function addLinkProperty(html, property) {
// take "<a href=...>...</a> and add a property
// not sophisticated at all, easily broken
var i=html.indexOf('>');
if (i<0) { return html; }
return html.substring(0,i) + ' ' + property + html.substring(i);
}
function addPopupShortcut(html, key) {
if (!getValueOf('popupShortcutKeys')) { return html; }
var ret= addLinkProperty(html, 'popupkey="'+key+'"');
if (key==' ') { key=popupString('spacebar'); }
return ret.replace(RegExp('^(.*?)(title=")(.*?)(".*)$', 'i'),'$1$2$3 ['+key+']$4');
}
//</NOLITE>
// ENDFILE: shortcutkeys.js
// STARTFILE: diffpreview.js
//<NOLITE>
//lets jump through hoops to find the rev ids we need to retrieve
function loadDiff(article, oldid, diff, navpop) {
navpop.diffData={ oldRev: {}, newRev: {} };
mw.loader.using( 'mediawiki.api' ).then( function() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'compare',
prop: 'ids|title'
};
if(article.title){
params.fromtitle = article.title;
}
switch (diff) {
case 'cur':
switch ( oldid ) {
case null:
case '':
case 'prev':
// this can only work if we have the title
// cur -> prev
params.torelative = 'prev';
break;
default:
params.fromrev = oldid;
params.torelative = 'cur';
break;
}
break;
case 'prev':
if( oldid ) {
params.fromrev = oldid;
} else {
params.fromtitle;
}
params.torelative = 'prev';
break;
case 'next':
params.fromrev = oldid || 0;
params.torelative = 'next';
break;
default:
params.fromrev = oldid || 0;
params.torev = diff || 0;
break;
}
api.get( params ).then( function( data ) {
navpop.diffData.oldRev.revid = data.compare.fromrevid;
navpop.diffData.newRev.revid = data.compare.torevid;
addReviewLink(navpop, 'popupMiscTools');
var go = function() {
pendingNavpopTask(navpop);
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
url += 'revids=' + navpop.diffData.oldRev.revid + '|' + navpop.diffData.newRev.revid;
url += '&prop=revisions&rvprop=ids|timestamp|content';
getPageWithCaching(url, doneDiff, navpop);
return true; // remove hook once run
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_DIFFS'); }
} );
} );
}
// Put a "mark patrolled" link to an element target
// TODO: Allow patrol a revision, as well as a diff
function addReviewLink (navpop, target) {
if (! pg.user.canReview) return;
// If 'newRev' is older than 'oldRev' than it could be confusing, so we do not show the review link.
if (navpop.diffData.newRev.revid <= navpop.diffData.oldRev.revid) return;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
prop: 'info|flagged',
revids: navpop.diffData.oldRev.revid,
formatversion : 2
};
api.get (params).then(function(data){
var stable_revid = data.query.pages[0].flagged && data.query.pages[0].flagged.stable_revid || 0;
// The diff can be reviewed if the old version is the last reviewed version
// TODO: Other possible conditions that we may want to implement instead of this one:
// * old version is patrolled and the new version is not patrolled
// * old version is patrolled and the new version is more recent than the last reviewed version
if (stable_revid == navpop.diffData.oldRev.revid) {
var a = document.createElement('a');
a.innerHTML = popupString('mark patrolled');
a.title=popupString('markpatrolledHint');
a.onclick = function() {
var params = {
action: 'review',
revid: navpop.diffData.newRev.revid,
comment: tprintf('defaultpopupReviewedSummary', [navpop.diffData.oldRev.revid, navpop.diffData.newRev.revid])
};
api.postWithToken('csrf',params).done(function(){
a.style.display = "none";
// TODO: Update current page and other already constructed popups
} ).fail(function(){
alert(popupString('Could not marked this edit as patrolled'));
});
};
setPopupHTML(a, target, navpop.idNumber,null,true);
}
});
}
function doneDiff(download) {
if (!download.owner || !download.owner.diffData) { return; }
var navpop=download.owner;
completedNavpopTask(navpop);
var pages, revisions=[];
try{
// Process the downloads
pages = getJsObj(download.data).query.pages;
for(var i=0; i < pages.length; i++ ) {
revisions = revisions.concat(pages[i].revisions);
}
for(i=0; i< revisions.length; i++){
if(revisions[i].revid == navpop.diffData.oldRev.revid) {
navpop.diffData.oldRev.revision = revisions[i];
} else if (revisions[i].revid == navpop.diffData.newRev.revid) {
navpop.diffData.newRev.revision = revisions[i];
}
}
} catch(someError) {
errlog( 'Could not get diff' );
}
insertDiff(navpop);
}
function rmBoringLines(a,b,context) {
if (typeof context == 'undefined') { context=2; }
// this is fairly slow... i think it's quicker than doing a word-based diff from the off, though
var aa=[], aaa=[];
var bb=[], bbb=[];
var i, j;
// first, gather all disconnected nodes in a and all crossing nodes in a and b
for (i=0; i<a.length; ++i ) {
if(!a[i].paired) { aa[i]=1; }
else if (countCrossings(b,a,i, true)) {
aa[i]=1;
bb[ a[i].row ] = 1;
}
}
// pick up remaining disconnected nodes in b
for (i=0; i<b.length; ++i ) {
if (bb[i]==1) { continue; }
if(!b[i].paired) { bb[i]=1; }
}
// another pass to gather context: we want the neighbours of included nodes which are not yet included
// we have to add in partners of these nodes, but we don't want to add context for *those* nodes in the next pass
for (i=0; i<b.length; ++i) {
if ( bb[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(b.length, i+context); ++j) {
if ( !bb[j] ) { bb[j] = 1; aa[ b[j].row ] = 0.5; }
}
}
}
for (i=0; i<a.length; ++i) {
if ( aa[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(a.length, i+context); ++j) {
if ( !aa[j] ) { aa[j] = 1; bb[ a[j].row ] = 0.5; }
}
}
}
for (i=0; i<bb.length; ++i) {
if (bb[i] > 0) { // it's a row we need
if (b[i].paired) { bbb.push(b[i].text); } // joined; partner should be in aa
else {
bbb.push(b[i]);
}
}
}
for (i=0; i<aa.length; ++i) {
if (aa[i] > 0) { // it's a row we need
if (a[i].paired) { aaa.push(a[i].text); } // joined; partner should be in aa
else {
aaa.push(a[i]);
}
}
}
return { a: aaa, b: bbb};
}
function stripOuterCommonLines(a,b,context) {
var i=0;
while (i<a.length && i < b.length && a[i]==b[i]) { ++i; }
var j=a.length-1; var k=b.length-1;
while ( j>=0 && k>=0 && a[j]==b[k] ) { --j; --k; }
return { a: a.slice(Math.max(0,i - 1 - context), Math.min(a.length+1, j + context+1)),
b: b.slice(Math.max(0,i - 1 - context), Math.min(b.length+1, k + context+1)) };
}
function insertDiff(navpop) {
// for speed reasons, we first do a line-based diff, discard stuff that seems boring, then do a word-based diff
// FIXME: sometimes this gives misleading diffs as distant chunks are squashed together
var oldlines = navpop.diffData.oldRev.revision.content.split('\n');
var newlines = navpop.diffData.newRev.revision.content.split('\n');
var inner=stripOuterCommonLines(oldlines,newlines,getValueOf('popupDiffContextLines'));
oldlines=inner.a; newlines=inner.b;
var truncated=false;
getValueOf('popupDiffMaxLines');
if (oldlines.length > pg.option.popupDiffMaxLines || newlines.length > pg.option.popupDiffMaxLines) {
// truncate
truncated=true;
inner=stripOuterCommonLines(oldlines.slice(0,pg.option.popupDiffMaxLines),
newlines.slice(0,pg.option.popupDiffMaxLines),
pg.option.popupDiffContextLines);
oldlines=inner.a; newlines=inner.b;
}
var lineDiff=diff(oldlines, newlines);
var lines2=rmBoringLines(lineDiff.o, lineDiff.n);
var oldlines2=lines2.a; var newlines2=lines2.b;
var simpleSplit = !String.prototype.parenSplit.isNative;
var html='<hr />';
if (getValueOf('popupDiffDates')) {
html += diffDatesTable(navpop);
html += '<hr />';
}
html += shortenDiffString(
diffString(oldlines2.join('\n'), newlines2.join('\n'), simpleSplit),
getValueOf('popupDiffContextCharacters') ).join('<hr />');
setPopupTipsAndHTML(html.split('\n').join('<br>') +
(truncated ? '<hr /><b>'+popupString('Diff truncated for performance reasons')+'</b>' : '') ,
'popupPreview', navpop.idNumber);
}
function diffDatesTable( navpop ) {
var html='<table class="popup_diff_dates">';
html += diffDatesTableRow( navpop.diffData.newRev.revision, tprintf('New revision'));
html += diffDatesTableRow( navpop.diffData.oldRev.revision, tprintf('Old revision'));
html += '</table>';
return html;
}
function diffDatesTableRow( revision, label ) {
var txt='';
var lastModifiedDate = new Date(revision.timestamp);
var datePrint=getValueOf('popupDiffDatePrinter');
if (typeof lastModifiedDate[datePrint] == 'function') {
var d2 = adjustDate(lastModifiedDate, getTimeOffset());
txt = dayFormat(d2, true) + ' ' + timeFormat(d2, true);
} else {
txt = tprintf('Invalid %s %s', ['popupDiffDatePrinter', datePrint]);
}
var revlink = generalLink({url: mw.config.get('wgScript') + '?oldid='+revision.revid,
text: label, title: label});
return simplePrintf('<tr><td>%s</td><td>%s</td></tr>', [ revlink, txt ]);
}
//</NOLITE>
// ENDFILE: diffpreview.js
// STARTFILE: links.js
//<NOLITE>
/////////////////////
// LINK GENERATION //
/////////////////////
// titledDiffLink --> titledWikiLink --> generalLink
// wikiLink --> titledWikiLink --> generalLink
// editCounterLink --> generalLink
// TODO Make these functions return Element objects, not just raw HTML strings.
function titledDiffLink(l) { // article, text, title, from, to) {
return titledWikiLink({article: l.article, action: l.to + '&oldid=' + l.from,
newWin: l.newWin,
noPopup: l.noPopup,
text: l.text, title: l.title,
/* hack: no oldid here */
actionName: 'diff'});
}
function wikiLink(l) {
//{article:article, action:action, text:text, oldid, newid}) {
if (! (typeof l.article == typeof {} &&
typeof l.action == typeof '' &&
typeof l.text==typeof '')) return null;
if (typeof l.oldid == 'undefined') { l.oldid=null; }
var savedOldid = l.oldid;
if (!/^(edit|view|revert|render)$|^raw/.test(l.action)) { l.oldid=null; }
var hint=popupString(l.action + 'Hint'); // revertHint etc etc etc
var oldidData=[l.oldid, safeDecodeURI(l.article)];
var revisionString = tprintf('revision %s of %s', oldidData);
log('revisionString='+revisionString);
switch (l.action) {
case 'edit§ion=new': hint = popupString('newSectionHint'); break;
case 'edit&undo=':
if (l.diff && l.diff != 'prev' && savedOldid ) {
l.action += l.diff + '&undoafter=' + savedOldid;
} else if (savedOldid) {
l.action += savedOldid;
}
hint = popupString('undoHint');
break;
case 'raw&ctype=text/css': hint=popupString('rawHint'); break;
case 'revert':
var p=parseParams(pg.current.link.href);
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=' + revertSummary(l.oldid, p.diff);
if (p.diff=='prev') {
l.action += '&direction=prev';
revisionString = tprintf('the revision prior to revision %s of %s', oldidData);
}
if (getValueOf('popupRevertSummaryPrompt')) { l.action += '&autosummaryprompt=true'; }
if (getValueOf('popupMinorReverts')) { l.action += '&autominor=true'; }
log('revisionString is now '+revisionString);
break;
case 'nullEdit':
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=null';
break;
case 'historyfeed':
l.action='history&feed=rss';
break;
case 'markpatrolled':
l.action='markpatrolled&rcid='+l.rcid;
}
if (hint) {
if (l.oldid) {
hint = simplePrintf(hint, [revisionString]);
}
else {
hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
}
}
else {
hint = safeDecodeURI(l.article + '&action=' + l.action) + (l.oldid) ? '&oldid='+l.oldid : '';
}
return titledWikiLink({article: l.article, action: l.action, text: l.text, newWin:l.newWin,
title: hint, oldid: l.oldid, noPopup: l.noPopup, onclick: l.onclick});
}
function revertSummary(oldid, diff) {
var ret='';
if (diff == 'prev') {
ret=getValueOf('popupQueriedRevertToPreviousSummary');
} else { ret = getValueOf('popupQueriedRevertSummary'); }
return ret + '&autorv=' + oldid;
}
function titledWikiLink(l) {
// possible properties of argument:
// article, action, text, title, oldid, actionName, className, noPopup
// oldid = null is fine here
// article and action are mandatory args
if (typeof l.article == 'undefined' || typeof l.action=='undefined') {
errlog('got undefined article or action in titledWikiLink');
return null;
}
var base = pg.wiki.titlebase + l.article.urlString();
var url=base;
if (typeof l.actionName=='undefined' || !l.actionName) { l.actionName='action'; }
// no need to add &action=view, and this confuses anchors
if (l.action != 'view') { url = base + '&' + l.actionName + '=' + l.action; }
if (typeof l.oldid!='undefined' && l.oldid) { url+='&oldid='+l.oldid; }
var cssClass=pg.misc.defaultNavlinkClassname;
if (typeof l.className!='undefined' && l.className) { cssClass=l.className; }
return generalNavLink({url: url, newWin: l.newWin,
title: (typeof l.title != 'undefined') ? l.title : null,
text: (typeof l.text!='undefined')?l.text:null,
className: cssClass, noPopup:l.noPopup, onclick:l.onclick});
}
pg.fn.getLastContrib = function getLastContrib(wikipage, newWin) {
getHistoryInfo(wikipage, function(x) {
processLastContribInfo(x, {page: wikipage, newWin: newWin});
});
};
function processLastContribInfo(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: an odd thing happened. Please retry.'); return; }
if(!info.firstNewEditor) {
alert(tprintf('Only found one editor: %s made %s edits', [info.edits[0].editor,info.edits.length]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+info.firstNewEditor.oldid;
displayUrl(newUrl, stuff.newWin);
}
pg.fn.getDiffSinceMyEdit = function getDiffSinceMyEdit(wikipage, newWin) {
getHistoryInfo(wikipage, function(x){
processDiffSinceMyEdit(x, {page: wikipage, newWin: newWin});
});
};
function processDiffSinceMyEdit(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: something fishy happened. Please try again.'); return; }
var friendlyName=stuff.page.split('_').join(' ');
if(!info.myLastEdit) {
alert(tprintf('Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
[info.userName, getValueOf('popupHistoryLimit'), friendlyName]));
return;
}
if(info.myLastEdit.index === 0) {
alert(tprintf("%s seems to be the last editor to the page %s", [info.userName, friendlyName]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+ info.myLastEdit.oldid;
displayUrl(newUrl, stuff.newWin);
}
function displayUrl(url, newWin){
if(newWin) { window.open(url); }
else { document.location=url; }
}
pg.fn.purgePopups = function purgePopups() {
processAllPopups(true);
setupCache(); // deletes all cached items (not browser cached, though...)
pg.option={};
abortAllDownloads();
};
function processAllPopups(nullify, banish) {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
if (!pg.current.links[i].navpopup) { continue; }
if (nullify || banish) pg.current.links[i].navpopup.banish();
pg.current.links[i].simpleNoMore=false;
if (nullify) pg.current.links[i].navpopup=null;
}
}
pg.fn.disablePopups = function disablePopups(){
processAllPopups(false, true);
setupTooltips(null, true);
};
pg.fn.togglePreviews = function togglePreviews() {
processAllPopups(true, true);
pg.option.simplePopups=!pg.option.simplePopups;
abortAllDownloads();
};
function magicWatchLink(l) {
//Yuck!! Would require a thorough redesign to add this as a click event though ...
l.onclick = simplePrintf( 'pg.fn.modifyWatchlist(\'%s\',\'%s\');return false;', [l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), this.id] );
return wikiLink(l);
}
pg.fn.modifyWatchlist = function modifyWatchlist(title, action) {
var reqData = {
'action': 'watch',
'formatversion': 2,
'titles': title,
'uselang': mw.config.get('wgUserLanguage')
};
if ( action === 'unwatch' ) reqData.unwatch = true;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
// Load the Addedwatchtext or Removedwatchtext message and show it
var mwTitle = mw.Title.newFromText( title );
var messageName;
if ( mwTitle && mwTitle.getNamespaceId() > 0 && mwTitle.getNamespaceId() % 2 === 1 ) {
messageName = action === 'watch' ? 'addedwatchtext-talk' : 'removedwatchtext-talk';
} else {
messageName = action === 'watch' ? 'addedwatchtext' : 'removedwatchtext';
}
$.when(
api.postWithToken( 'watch', reqData ),
mw.loader.using( [ 'mediawiki.api', 'mediawiki.jqueryMsg' ] ).then( function () {
return api.loadMessagesIfMissing( [ messageName ] );
} )
).done( function () {
mw.notify( mw.message( messageName, title ).parseDom() );
} );
};
function magicHistoryLink(l) {
// FIXME use onclick change href trick to sort this out instead of window.open
var jsUrl='', title='', onClick='';
switch(l.id) {
case 'lastContrib':
onClick=simplePrintf('pg.fn.getLastContrib(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('lastContribHint');
break;
case 'sinceMe':
onClick=simplePrintf('pg.fn.getDiffSinceMyEdit(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('sinceMeHint');
break;
}
jsUrl = 'javascript:' + onClick; // jshint ignore:line
onClick += ';return false;';
return generalNavLink({url: jsUrl, newWin: false, // can't have new windows with JS links, I think
title: title, text: l.text, noPopup: l.noPopup, onclick: onClick });
}
function popupMenuLink(l) {
var jsUrl=simplePrintf('javascript:pg.fn.%s()', [l.id]); // jshint ignore:line
var title=popupString(simplePrintf('%sHint', [l.id]));
var onClick=simplePrintf('pg.fn.%s();return false;', [l.id]);
return generalNavLink({url: jsUrl, newWin:false, title:title, text:l.text, noPopup:l.noPopup, onclick: onClick});
}
function specialLink(l) {
// properties: article, specialpage, text, sep
if (typeof l.specialpage=='undefined'||!l.specialpage) return null;
var base = pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId]+':'+l.specialpage;
if (typeof l.sep == 'undefined' || l.sep === null) l.sep='&target=';
var article=l.article.urlString({keepSpaces: l.specialpage=='Search'});
var hint=popupString(l.specialpage+'Hint');
switch (l.specialpage) {
case 'Log':
switch (l.sep) {
case '&user=': hint=popupString('userLogHint'); break;
case '&type=block&page=': hint=popupString('blockLogHint'); break;
case '&page=': hint=popupString('pageLogHint'); break;
case '&type=protect&page=': hint=popupString('protectLogHint'); break;
case '&type=delete&page=': hint=popupString('deleteLogHint'); break;
default: log('Unknown log type, sep=' + l.sep); hint='Missing hint (FIXME)';
}
break;
case 'PrefixIndex': article += '/'; break;
}
if (hint) hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
else hint = safeDecodeURI(l.specialpage+':'+l.article) ;
var url = base + l.sep + article;
return generalNavLink({url: url, title: hint, text: l.text, newWin:l.newWin, noPopup:l.noPopup});
}
function generalLink(l) {
// l.url, l.text, l.title, l.newWin, l.className, l.noPopup, l.onclick
if (typeof l.url=='undefined') return null;
// only quotation marks in the url can screw us up now... I think
var url=l.url.split('"').join('%22');
var ret='<a href="' + url + '"';
if (typeof l.title!='undefined' && l.title) { ret += ' title="' + pg.escapeQuotesHTML(l.title) + '"'; }
if (typeof l.onclick!='undefined' && l.onclick) { ret += ' onclick="' + pg.escapeQuotesHTML(l.onclick) + '"'; }
if (l.noPopup) { ret += ' noPopup=1'; }
var newWin;
if (typeof l.newWin=='undefined' || l.newWin === null) { newWin=getValueOf('popupNewWindows'); }
else { newWin=l.newWin; }
if (newWin) { ret += ' target="_blank"'; }
if (typeof l.className!='undefined'&&l.className) { ret+=' class="'+l.className+'"'; }
ret += '>';
if (typeof l.text==typeof '') { ret+= l.text; }
ret +='</a>';
return ret;
}
function appendParamsToLink(linkstr, params) {
var sp=linkstr.parenSplit(RegExp('(href="[^"]+?)"', 'i'));
if (sp.length<2) return null;
var ret=sp.shift() + sp.shift();
ret += '&' + params + '"';
ret += sp.join('');
return ret;
}
function changeLinkTargetLink(x) { // newTarget, text, hint, summary, clickButton, minor, title (optional), alsoChangeLabel {
if (x.newTarget) {
log ('changeLinkTargetLink: newTarget=' + x.newTarget);
}
if (x.oldTarget !== decodeURIComponent( x.oldTarget ) ) {
log ('This might be an input problem: ' + x.oldTarget );
}
// FIXME: first character of page title as well as namespace should be case insensitive
// eg [[category:X1]] and [[Category:X1]] are equivalent
// this'll break if charAt(0) is nasty
var cA = mw.util.escapeRegExp(x.oldTarget);
var chs = cA.charAt(0).toUpperCase();
chs='['+chs + chs.toLowerCase()+']';
var currentArticleRegexBit=chs+cA.substring(1);
currentArticleRegexBit=currentArticleRegexBit
.split(RegExp('(?:[_ ]+|%20)', 'g')).join('(?:[_ ]+|%20)')
.split('\\(').join('(?:%28|\\()')
.split('\\)').join('(?:%29|\\))'); // why does this need to match encoded strings ? links in the document ?
// leading and trailing space should be ignored, and anchor bits optional:
currentArticleRegexBit = '\\s*(' + currentArticleRegexBit + '(?:#[^\\[\\|]*)?)\\s*';
// e.g. Computer (archaic) -> \s*([Cc]omputer[_ ](?:%2528|\()archaic(?:%2528|\)))\s*
// autoedit=s~\[\[([Cc]ad)\]\]~[[Computer-aided%20design|$1]]~g;s~\[\[([Cc]AD)[|]~[[Computer-aided%20design|~g
var title=x.title || mw.config.get('wgPageName').split('_').join(' ');
var lk=titledWikiLink({article: new Title(title), newWin:x.newWin,
action: 'edit',
text: x.text,
title: x.hint,
className: 'popup_change_title_link'
});
var cmd='';
if (x.newTarget) {
// escape '&' and other nasties
var t = x.newTarget;
var s = mw.util.escapeRegExp(x.newTarget);
if (x.alsoChangeLabel) {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + ']]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
} else {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + '|$1]]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
}
} else {
cmd += 's~\\[\\['+currentArticleRegexBit+'\\]\\]~$1~g;';
cmd += 's~\\[\\['+currentArticleRegexBit+'[|](.*?)\\]\\]~$2~g';
}
// Build query
cmd = 'autoedit=' + encodeURIComponent ( cmd );
cmd += '&autoclick='+ encodeURIComponent( x.clickButton ) + '&actoken=' + encodeURIComponent( autoClickToken() );
cmd += ( x.minor === null ) ? '' : '&autominor='+ encodeURIComponent( x.minor );
cmd += ( x.watch === null ) ? '' : '&autowatch='+ encodeURIComponent( x.watch );
cmd += '&autosummary='+encodeURIComponent(x.summary);
cmd += '&autoimpl='+encodeURIComponent( popupString('autoedit_version') );
return appendParamsToLink(lk, cmd);
}
function redirLink(redirMatch, article) {
// NB redirMatch is in wikiText
var ret='';
if (getValueOf('popupAppendRedirNavLinks') && getValueOf('popupNavLinks')) {
ret += '<hr />';
if (getValueOf('popupFixRedirs') && typeof autoEdit != 'undefined' && autoEdit) {
ret += popupString('Redirects to: (Fix ');
log('redirLink: newTarget=' + redirMatch);
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target'),
hint: popupString('Fix this redirect, changing just the link target'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages')
}), 'R');
ret += popupString(' or ');
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target & label'),
hint: popupString('Fix this redirect, changing the link target and label'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages'),
alsoChangeLabel: true
}), 'R');
ret += popupString(')');
}
else ret += popupString('Redirects') + popupString(' to ');
return ret;
}
else return '<br> ' + popupString('Redirects') + popupString(' to ') +
titledWikiLink({article: new Title().fromWikiText(redirMatch), action: 'view', /* FIXME: newWin */
text: safeDecodeURI(redirMatch), title: popupString('Bypass redirect')});
}
function arinLink(l) {
if (!saneLinkCheck(l)) { return null; }
if ( ! l.article.isIpUser() || ! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
return generalNavLink({url:'http://ws.arin.net/cgi-bin/whois.pl?queryinput=' + encodeURIComponent(uN), newWin:l.newWin,
title: tprintf('Look up %s in ARIN whois database', [uN]),
text: l.text, noPopup:1});
}
function toolDbName(cookieStyle) {
var ret = mw.config.get('wgDBname');
if (!cookieStyle) { ret+= '_p'; }
return ret;
}
function saneLinkCheck(l) {
if (typeof l.article != typeof {} || typeof l.text != typeof '') { return false; }
return true;
}
function editCounterLink(l) {
if(!saneLinkCheck(l)) return null;
if (! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
var tool=getValueOf('popupEditCounterTool');
var url;
var defaultToolUrl='//tools.wmflabs.org/supercount/index.php?user=$1&project=$2.$3';
switch(tool) {
case 'custom':
url=simplePrintf(getValueOf('popupEditCounterUrl'), [ encodeURIComponent(uN), toolDbName() ]);
break;
case 'soxred': // no longer available
case 'kate': // no longer available
case 'interiot':// no longer available
/* fall through */
case 'supercount':
default:
var theWiki=pg.wiki.hostname.split('.');
url=simplePrintf(defaultToolUrl, [ encodeURIComponent(uN), theWiki[0], theWiki[1] ]);
}
return generalNavLink({url:url, title: tprintf('editCounterLinkHint', [uN]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function globalSearchLink(l) {
if(!saneLinkCheck(l)) return null;
var base='http://vs.aka-online.de/cgi-bin/globalwpsearch.pl?timeout=120&search=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + article, newWin:l.newWin,
title: tprintf('globalSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function googleLink(l) {
if(!saneLinkCheck(l)) return null;
var base='https://www.google.com/search?q=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + '%22' + article + '%22', newWin:l.newWin,
title: tprintf('googleSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function editorListLink(l) {
if(!saneLinkCheck(l)) return null;
var article= l.article.articleFromTalkPage() || l.article;
var url='https://xtools.wmflabs.org/articleinfo/' +
encodeURI( pg.wiki.hostname ) + '/' +
article.urlString() +
'?uselang=' + mw.config.get('wgUserLanguage');
return generalNavLink({url:url,
title: tprintf('editorListHint', [article]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function generalNavLink(l) {
l.className = (l.className === null) ? 'popupNavLink' : l.className;
return generalLink(l);
}
//////////////////////////////////////////////////
// magic history links
//
function getHistoryInfo(wikipage, whatNext) {
log('getHistoryInfo');
getHistory(wikipage, whatNext ? function(d){whatNext(processHistory(d));} : processHistory);
}
// FIXME eliminate pg.idNumber ... how? :-(
function getHistory(wikipage, onComplete) {
log('getHistory');
var url = pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&prop=revisions&titles=' +
new Title(wikipage).urlString() + '&rvlimit=' + getValueOf('popupHistoryLimit');
log('getHistory: url='+url);
return startDownload(url, pg.idNumber+'history', onComplete);
}
function processHistory(download) {
var jsobj = getJsObj(download.data);
try {
var revisions = anyChild(jsobj.query.pages).revisions;
var edits=[];
for (var i=0; i<revisions.length; ++i) {
edits.push({ oldid: revisions[i].revid, editor: revisions[i].user });
}
log('processed ' + edits.length + ' edits');
return finishProcessHistory( edits, mw.config.get('wgUserName') );
} catch (someError) {
log('Something went wrong with JSON business');
return finishProcessHistory([]);
}
}
function finishProcessHistory(edits, userName) {
var histInfo={};
histInfo.edits=edits;
histInfo.userName=userName;
for (var i=0; i<edits.length; ++i) {
if (typeof histInfo.myLastEdit === 'undefined' && userName && edits[i].editor==userName) {
histInfo.myLastEdit={index: i, oldid: edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
if (typeof histInfo.firstNewEditor === 'undefined' && edits[i].editor != edits[0].editor) {
histInfo.firstNewEditor={index:i, oldid:edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
}
//pg.misc.historyInfo=histInfo;
return histInfo;
}
//</NOLITE>
// ENDFILE: links.js
// STARTFILE: options.js
//////////////////////////////////////////////////
// options
// check for existing value, else use default
function defaultize(x) {
if (pg.option[x]===null || typeof pg.option[x]=='undefined') {
if (typeof window[x] != 'undefined' ) pg.option[x]=window[x];
else pg.option[x]=pg.optionDefault[x];
}
}
function newOption(x, def) {
pg.optionDefault[x]=def;
}
function setDefault(x, def) {
return newOption(x, def);
}
function getValueOf(varName) {
defaultize(varName);
return pg.option[varName];
}
/*eslint-disable */
function useDefaultOptions() { // for testing
for (var p in pg.optionDefault) {
pg.option[p]=pg.optionDefault[p];
if (typeof window[p]!='undefined') { delete window[p]; }
}
}
/*eslint-enable */
function setOptions() {
// user-settable parameters and defaults
var userIsSysop = false;
if ( mw.config.get('wgUserGroups') ) {
for ( var g = 0; g < mw.config.get('wgUserGroups').length; ++g ) {
if ( mw.config.get('wgUserGroups')[g] == "sysop" )
userIsSysop = true;
}
}
// Basic options
newOption('popupDelay', 0.5);
newOption('popupHideDelay', 0.5);
newOption('simplePopups', false);
newOption('popupStructure', 'shortmenus'); // see later - default for popupStructure is 'original' if simplePopups is true
newOption('popupActionsMenu', true);
newOption('popupSetupMenu', true);
newOption('popupAdminLinks', userIsSysop);
newOption('popupShortcutKeys', false);
newOption('popupHistoricalLinks', true);
newOption('popupOnlyArticleLinks', true);
newOption('removeTitles', true);
newOption('popupMaxWidth', 350);
newOption('popupSimplifyMainLink', true);
newOption('popupAppendRedirNavLinks', true);
newOption('popupTocLinks', false);
newOption('popupSubpopups', true);
newOption('popupDragHandle', false /* 'popupTopLinks'*/);
newOption('popupLazyPreviews', true);
newOption('popupLazyDownloads', true);
newOption('popupAllDabsStubs', false);
newOption('popupDebugging', false);
newOption('popupActiveNavlinks', true);
newOption('popupModifier', false); // ctrl, shift, alt or meta
newOption('popupModifierAction', 'enable'); // or 'disable'
newOption('popupDraggable', true);
newOption('popupReview', false);
//<NOLITE>
// images
newOption('popupImages', true);
newOption('imagePopupsForImages', true);
newOption('popupNeverGetThumbs', false);
//newOption('popupImagesToggleSize', true);
newOption('popupThumbAction', 'imagepage'); //'sizetoggle');
newOption('popupImageSize', 60);
newOption('popupImageSizeLarge', 200);
// redirs, dabs, reversion
newOption('popupFixRedirs', false);
newOption('popupRedirAutoClick', 'wpDiff');
newOption('popupFixDabs', false);
newOption('popupDabsAutoClick', 'wpDiff');
newOption('popupRevertSummaryPrompt', false);
newOption('popupMinorReverts', false);
newOption('popupRedlinkRemoval', false);
newOption('popupRedlinkAutoClick', 'wpDiff');
newOption('popupWatchDisambiggedPages', null);
newOption('popupWatchRedirredPages', null);
newOption('popupDabWiktionary', 'last');
// navlinks
newOption('popupNavLinks', true);
newOption('popupNavLinkSeparator', ' ⋅ ');
newOption('popupLastEditLink', true);
newOption('popupEditCounterTool', 'supercount');
newOption('popupEditCounterUrl', '');
//</NOLITE>
// previews etc
newOption('popupPreviews', true);
newOption('popupSummaryData', true);
newOption('popupMaxPreviewSentences', 9);
newOption('popupMaxPreviewCharacters', 900);
newOption('popupLastModified', true);
newOption('popupPreviewKillTemplates', true);
newOption('popupPreviewRawTemplates', true);
newOption('popupPreviewFirstParOnly', false);
newOption('popupPreviewCutHeadings', false);
newOption('popupPreviewButton', false);
newOption('popupPreviewButtonEvent', 'click');
//<NOLITE>
// diffs
newOption('popupPreviewDiffs', true);
newOption('popupDiffMaxLines', 100);
newOption('popupDiffContextLines', 2);
newOption('popupDiffContextCharacters', 40);
newOption('popupDiffDates', true);
newOption('popupDiffDatePrinter', 'toLocaleString');
// edit summaries. God, these are ugly.
newOption('popupReviewedSummary', popupString('defaultpopupReviewedSummary') );
newOption('popupFixDabsSummary', popupString('defaultpopupFixDabsSummary') );
newOption('popupExtendedRevertSummary', popupString('defaultpopupExtendedRevertSummary') );
newOption('popupRevertSummary', popupString('defaultpopupRevertSummary') );
newOption('popupRevertToPreviousSummary', popupString('defaultpopupRevertToPreviousSummary') );
newOption('popupQueriedRevertSummary', popupString('defaultpopupQueriedRevertSummary') );
newOption('popupQueriedRevertToPreviousSummary', popupString('defaultpopupQueriedRevertToPreviousSummary') );
newOption('popupFixRedirsSummary', popupString('defaultpopupFixRedirsSummary') );
newOption('popupRedlinkSummary', popupString('defaultpopupRedlinkSummary') );
newOption('popupRmDabLinkSummary', popupString('defaultpopupRmDabLinkSummary') );
//</NOLITE>
// misc
newOption('popupHistoryLimit', 50);
//<NOLITE>
newOption('popupFilters', [popupFilterStubDetect, popupFilterDisambigDetect,
popupFilterPageSize, popupFilterCountLinks,
popupFilterCountImages, popupFilterCountCategories,
popupFilterLastModified]);
newOption('extraPopupFilters', []);
newOption('popupOnEditSelection', 'cursor');
newOption('popupPreviewHistory', true);
newOption('popupImageLinks', true);
newOption('popupCategoryMembers', true);
newOption('popupUserInfo', true);
newOption('popupHistoryPreviewLimit', 25);
newOption('popupContribsPreviewLimit',25);
newOption('popupRevDelUrl', '//en.wikipedia.org/wiki/Wikipedia:Revision_deletion');
newOption('popupShowGender', true);
//</NOLITE>
// new windows
newOption('popupNewWindows', false);
newOption('popupLinksNewWindow', {'lastContrib': true, 'sinceMe': true});
// regexps
newOption('popupDabRegexp', '(\\{\\{\\s*disambig(?!uation needed)|disambig(uation|)\\s*\\}\\}|disamb\\s*\\}\\}|dab\\s*\\}\\})|\\{\\{\\s*(((geo|hn|road?|school|number)dis)|[234][lc][acw]|(road|ship)index)(\\s*[|][^}]*)?\\s*[}][}]|is a .*disambiguation.*page');
newOption('popupAnchorRegexp', 'anchors?'); //how to identify an anchors template
newOption('popupStubRegexp', '(sect)?stub[}][}]|This .*-related article is a .*stub');
newOption('popupImageVarsRegexp', 'image|image_(?:file|skyline|name|flag|seal)|cover|badge|logo');
}
// ENDFILE: options.js
// STARTFILE: strings.js
//<NOLITE>
//////////////////////////////////////////////////
// Translatable strings
//////////////////////////////////////////////////
//
// See instructions at
// https://en.wikipedia.org/wiki/Wikipedia:Tools/Navigation_popups/Translation
pg.string = {
/////////////////////////////////////
// summary data, searching etc.
/////////////////////////////////////
'article': 'article',
'category': 'category',
'categories': 'categories',
'image': 'image',
'images': 'images',
'stub': 'stub',
'section stub': 'section stub',
'Empty page': 'Empty page',
'kB': 'kB',
'bytes': 'bytes',
'day': 'day',
'days': 'days',
'hour': 'hour',
'hours': 'hours',
'minute': 'minute',
'minutes': 'minutes',
'second': 'second',
'seconds': 'seconds',
'week': 'week',
'weeks': 'weeks',
'search': 'search',
'SearchHint': 'Find English Wikipedia articles containing %s',
'web': 'web',
'global': 'global',
'globalSearchHint': 'Search across Wikipedias in different languages for %s',
'googleSearchHint': 'Google for %s',
/////////////////////////////////////
// article-related actions and info
// (some actions also apply to user pages)
/////////////////////////////////////
'tác vụ': 'tác vụ', ///// view articles and view talk
'popupsMenu': 'popups',
'togglePreviewsHint': 'Toggle preview generation in popups on this page',
'enable previews': 'enable previews',
'disable previews': 'disable previews',
'toggle previews': 'toggle previews',
'show preview': 'show preview',
'reset': 'reset',
'more...': 'more...',
'disable': 'disable popups',
'disablePopupsHint': 'Disable popups on this page. Reload page to re-enable.',
'historyfeedHint': 'RSS feed of recent changes to this page',
'purgePopupsHint': 'Reset popups, clearing all cached popup data.',
'PopupsHint': 'Reset popups, clearing all cached popup data.',
'spacebar': 'space',
'view': 'view',
'view article': 'view article',
'viewHint': 'Go to %s',
'talk': 'talk',
'talk page': 'talk page',
'this revision': 'this revision',
'revision %s of %s': 'revision %s of %s',
'Revision %s of %s': 'Revision %s of %s',
'the revision prior to revision %s of %s': 'the revision prior to revision %s of %s',
'Toggle image size': 'Click to toggle image size',
'del': 'del', ///// delete, protect, move
'delete': 'delete',
'deleteHint': 'Delete %s',
'undeleteShort': 'un',
'UndeleteHint': 'Show the deletion history for %s',
'protect': 'protect',
'protectHint': 'Restrict editing rights to %s',
'unprotectShort': 'un',
'unprotectHint': 'Allow %s to be edited by anyone again',
'send thanks': 'send thanks',
'ThanksHint': 'Send a thank you notification to this user',
'move': 'move',
'move page': 'move page',
'MovepageHint': 'Change the title of %s',
'sửa': 'sửa', ///// edit articles and talk
'edit article': 'edit article',
'editHint': 'Change the content of %s',
'edit talk': 'edit talk',
'new': 'new',
'new topic': 'new topic',
'newSectionHint': 'Start a new section on %s',
'null edit': 'null edit',
'nullEditHint': 'Submit an edit to %s, making no changes ',
'hist': 'hist', ///// history, diffs, editors, related
'lịch sử': 'lịch sử',
'historyHint': 'List the changes made to %s',
'last': 'prev', // For labelling the previous revision in history pages; the key is "last" for backwards compatibility
'lastEdit': 'lastEdit',
'mark patrolled': 'đánh dấu tuần tra',
'markpatrolledHint': 'Mark this edit as patrolled',
'Could not marked this edit as patrolled': 'Could not marked this edit as patrolled',
'show last edit': 'lần sửa đổi gần nhất',
'Show the last edit': 'Show the effects of the most recent change',
'lastContrib': 'lastContrib',
'last set of edits': 'latest edits',
'lastContribHint': 'Show the net effect of changes made by the last editor',
'cur': 'cur',
'diffCur': 'diffCur',
'Show changes since revision %s': 'Show changes since revision %s',
'%s old': '%s old', // as in 4 weeks old
'oldEdit': 'oldEdit',
'purge': 'purge',
'purgeHint': 'Demand a fresh copy of %s',
'raw': 'source',
'rawHint': 'Download the source of %s',
'render': 'simple',
'renderHint': 'Show a plain HTML version of %s',
'Show the edit made to get revision': 'Show the edit made to get revision',
'sinceMe': 'sinceMe',
'changes since mine': 'diff my edit',
'sinceMeHint': 'Show changes since my last edit',
'Couldn\'t find an edit by %s\nin the last %s edits to\n%s': 'Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
'eds': 'eds',
'editors': 'editors',
'editorListHint': 'List the users who have edited %s',
'related': 'related',
'relatedChanges': 'relatedChanges',
'related changes': 'related changes',
'RecentchangeslinkedHint': 'Show changes in articles related to %s',
'editOld': 'editOld', ///// edit old version, or revert
'rv': 'rv',
'revert': 'revert',
'revertHint': 'Revert to %s',
'defaultpopupReviewedSummary': 'Accepted by reviewing the [[Special:diff/%s/%s|difference]] between this version and previously accepted version using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRedlinkSummary': 'Removing link to empty page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixDabsSummary': 'Disambiguate [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixRedirsSummary': 'Redirect bypass from [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupExtendedRevertSummary': 'Revert to revision dated %s by %s, oldid %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertToPreviousSummary': 'Revert to the revision prior to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertSummary': 'Revert to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertToPreviousSummary': 'Revert to the revision prior to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertSummary': 'Revert to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRmDabLinkSummary': 'Remove link to dab page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'Redirects': 'Redirects', // as in Redirects to ...
' to ': ' to ', // as in Redirects to ...
'Bypass redirect': 'Bypass redirect',
'Fix this redirect': 'Fix this redirect',
'disambig': 'disambig', ///// add or remove dab etc.
'disambigHint': 'Disambiguate this link to [[%s]]',
'Click to disambiguate this link to:': 'Click to disambiguate this link to:',
'remove this link': 'remove this link',
'remove all links to this page from this article': 'remove all links to this page from this article',
'remove all links to this disambig page from this article': 'remove all links to this disambig page from this article',
'mainlink': 'mainlink', ///// links, watch, unwatch
'wikiLink': 'wikiLink',
'wikiLinks': 'wikiLinks',
'links here': 'links here',
'whatLinksHere': 'whatLinksHere',
'what links here': 'what links here',
'WhatlinkshereHint': 'List the pages that are hyperlinked to %s',
'unwatchShort': 'un',
'watchThingy': 'watch', // called watchThingy because {}.watch is a function
'watchHint': 'Add %s to my watchlist',
'unwatchHint': 'Remove %s from my watchlist',
'Only found one editor: %s made %s edits': 'Only found one editor: %s made %s edits',
'%s seems to be the last editor to the page %s': '%s seems to be the last editor to the page %s',
'rss': 'rss',
/////////////////////////////////////
// diff previews
/////////////////////////////////////
'Diff truncated for performance reasons': 'Diff truncated for performance reasons',
'Old revision': 'Old revision',
'New revision': 'New revision',
'Something went wrong :-(': 'Something went wrong :-(',
'Empty revision, maybe non-existent': 'Empty revision, maybe non-existent',
'Unknown date': 'Unknown date',
/////////////////////////////////////
// other special previews
/////////////////////////////////////
'Empty category': 'Empty category',
'Category members (%s shown)': 'Category members (%s shown)',
'No image links found': 'No image links found',
'File links': 'File links',
'No image found': 'No image found',
'Image from Commons': 'Image from Commons',
'Description page': 'Description page',
'Alt text:': 'Alt text:',
'revdel':'Hidden revision',
/////////////////////////////////////
// user-related actions and info
/////////////////////////////////////
'user': 'user', ///// user page, talk, email, space
'user page': 'user page',
'user talk': 'user talk',
'edit user talk': 'edit user talk',
'leave comment': 'leave comment',
'email': 'email',
'email user': 'email user',
'EmailuserHint': 'Send an email to %s',
'space': 'space', // short form for userSpace link
'PrefixIndexHint': 'Show pages in the userspace of %s',
'count': 'count', ///// contributions, log
'edit counter': 'edit counter',
'editCounterLinkHint': 'Count the contributions made by %s',
'contribs': 'contribs',
'contributions': 'contributions',
'deletedContribs': 'deleted contributions',
'DeletedcontributionsHint': 'List deleted edits made by %s',
'ContributionsHint': 'List the contributions made by %s',
'log': 'log',
'user log': 'user log',
'userLogHint': 'Show %s\'s user log',
'arin': 'ARIN lookup', ///// ARIN lookup, block user or IP
'Look up %s in ARIN whois database': 'Look up %s in the ARIN whois database',
'unblockShort': 'un',
'block': 'block',
'block user': 'block user',
'IpblocklistHint': 'Unblock %s',
'BlockipHint': 'Prevent %s from editing',
'block log': 'block log',
'blockLogHint': 'Show the block log for %s',
'protectLogHint': 'Show the protection log for %s',
'pageLogHint': 'Show the page log for %s',
'deleteLogHint': 'Show the deletion log for %s',
'Invalid %s %s': 'The option %s is invalid: %s',
'No backlinks found': 'No backlinks found',
' and more': ' and more',
'undo': 'undo',
'undoHint': 'undo this edit',
'Download preview data': 'Download preview data',
'Invalid or IP user': 'Invalid or IP user',
'Not a registered username': 'Not a registered username',
'CẤM': 'CẤM',
'Has blocks': 'Has blocks',
' sửa đổi từ: ': ' sửa đổi từ: ',
'sửa đổi lần cuối vào ': 'sửa đổi lần cuối vào ',
/////////////////////////////////////
// Autoediting
/////////////////////////////////////
'Enter a non-empty edit summary or press cancel to abort': 'Enter a non-empty edit summary or press cancel to abort',
'Failed to get revision information, please edit manually.\n\n': 'Failed to get revision information, please edit manually.\n\n',
'The %s button has been automatically clicked. Please wait for the next page to load.': 'The %s button has been automatically clicked. Please wait for the next page to load.',
'Could not find button %s. Please check the settings in your javascript file.': 'Could not find button %s. Please check the settings in your javascript file.',
/////////////////////////////////////
// Popups setup
/////////////////////////////////////
'Open full-size image': 'Open full-size image',
'zxy': 'zxy',
'autoedit_version': 'np20140416'
};
function popupString(str) {
if (typeof popupStrings != 'undefined' && popupStrings && popupStrings[str]) { return popupStrings[str]; }
if (pg.string[str]) { return pg.string[str]; }
return str;
}
function tprintf(str,subs) {
if (typeof subs != typeof []) { subs = [subs]; }
return simplePrintf(popupString(str), subs);
}
//</NOLITE>
// ENDFILE: strings.js
// STARTFILE: run.js
////////////////////////////////////////////////////////////////////
// Run things
////////////////////////////////////////////////////////////////////
// For some reason popups requires a fully loaded page jQuery.ready(...) causes problems for some.
// The old addOnloadHook did something similar to the below
if (document.readyState=="complete")
autoEdit(); //will setup popups
else
$( window ).on( 'load', autoEdit );
// Support for MediaWiki's live preview, VisualEditor's saves and Echo's flyout.
( function () {
var once = true;
function dynamicContentHandler( $content ) {
// Try to detect the hook fired on initial page load and disregard
// it, we already hook to onload (possibly to different parts of
// page - it's configurable) and running twice might be bad. Ugly…
if ( $content.attr( 'id' ) == 'mw-content-text' ) {
if ( once ) {
once = false;
return;
}
}
function registerHooksForVisibleNavpops () {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
var navpop = pg.current.links[i].navpopup;
if (!navpop || !navpop.isVisible()) { continue; }
Navpopup.tracker.addHook(posCheckerHook(navpop));
}
}
function doIt () {
registerHooksForVisibleNavpops();
$content.each( function () {
this.ranSetupTooltipsAlready = false;
setupTooltips( this );
} );
}
setupPopups( doIt );
}
// This hook is also fired after page load.
mw.hook( 'wikipage.content' ).add( dynamicContentHandler );
mw.hook( 'ext.echo.overlay.beforeShowingOverlay' ).add( function($overlay){
dynamicContentHandler( $overlay.find(".mw-echo-state") );
});
} )();
// Forked from User:Connel MacKenzie/mess-with-popups.js
Previewmaker.prototype.firstBit = function() {
try{
if (mw.config.get("wgNamespaceNumber") !== 0){
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d = this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data = this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data = this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data = this.data.replace(RegExp('^[\\s\\n]*'), '');
stuff = (RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) {
d = stuff[0];
}
if (!getValueOf('popupPreviewFirstParOnly')) {
d = this.data;
}
/// now put \n\n after sections so that bullets and numbered lists work
d = d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["' + "'" + ']*\\s)', 'g'));
// leading space is bad, mmkay?
d[0] = d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds = RegExp('([^.][a-z][.][a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
var maxChars=getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd;
do {
dd = this.firstSentences(d, n);
--n;
}
while (dd.length > maxChars && n > 0);
this.data = dd;
} else {
//TODO: Limit this override to urls that start with http://*.wiktionary.org/
//TODO: extend the above section to also preview Wikipedia links
var charsWikt = 0;
var linesWikt = this.data.split('\n');
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[in]transitive}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[un]countable}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{cattag\|(.*?)}}/g, "");
linesWikt[jWikt] = linesWikt[jWikt].replace(/[\{\}]/g, "'");
if (linesWikt[jWikt].search(/^[#]/) == -1) {
if (linesWikt[jWikt].search(/^==[A-Z]/) == -1) {
linesWikt[jWikt] = '';
}
}
charsWikt += linesWikt[jWikt].length;
}
if (charsWikt < 2) {
linesWikt[0] = "No properly formatted content";
}
var maxChars = getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd = '';
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
if (linesWikt[jWikt] !== '') {
dd += linesWikt[jWikt] + '\n';
}
if (dd.length < maxChars) {
this.data = dd;
}
}
}
}
catch (e) {
console.log("error from mess with popups: ", e);
}
};
});
hcl6pbrsz9x6mz7w8z8unth5ejrnzb8
2513983
2513982
2026-08-14T08:14:47Z
Hiyuune
50834
2513983
javascript
text/javascript
// {{documentation}}
// _________________________________________________________________________________________
// | |
// | === WARNING: GLOBAL GADGET FILE === |
// | Changes to this page affect many users. |
// | Please discuss changes on the [[Wiktionary:Grease Pit]] before editing. |
// |_________________________________________________________________________________________|
mw.loader.load('//en.wikipedia.org/w/index.php?action=raw&title=MediaWiki:Gadget-navpop.css&ctype=text/css', "text/css");
// Forked from [[:w:MediaWiki:Gadget-popups.js]]
$(function () {
//////////////////////////////////////////////////
// Globals
//
// Trying to shove as many of these as possible into the pg (popup globals) object
var pg = {
re: {}, // regexps
ns: {}, // namespaces
string: {}, // translatable strings
wiki: {}, // local site info
user: {}, // current user info
misc: {}, // YUCK PHOOEY
option: {}, // options, see newOption etc
optionDefault: {}, // default option values
flag: {}, // misc flags
cache: {}, // page and image cache
structures: {}, // navlink structures
timer: {}, // all sorts of timers (too damn many)
counter: {}, // .. and all sorts of counters
current: {}, // state info
fn: {}, // functions
endoflist: null
};
/* Bail if the gadget/script is being loaded twice */
if( window.pg ) {
return;
}
/* Export to global context */
window.pg = pg;
/// Local Variables: ///
/// mode:c ///
/// End: ///
// ENDFILE: main.js
// STARTFILE: actions.js
function setupTooltips(container, remove, force, popData) {
log('setupTooltips, container='+container+', remove='+remove);
if (!container) {
//<NOLITE>
// the main initial call
if (getValueOf('popupOnEditSelection') && document && document.editform && document.editform.wpTextbox1) {
document.editform.wpTextbox1.onmouseup=doSelectionPopup;
}
//</NOLITE>
// article/content is a structure-dependent thing
container = defaultPopupsContainer();
}
if (!remove && !force && container.ranSetupTooltipsAlready) { return; }
container.ranSetupTooltipsAlready = !remove;
var anchors;
anchors=container.getElementsByTagName('A');
setupTooltipsLoop(anchors, 0, 250, 100, remove, popData);
}
function defaultPopupsContainer() {
if (getValueOf('popupOnlyArticleLinks')) {
return document.getElementById('mw_content') ||
document.getElementById('content') ||
document.getElementById('article') || document;
}
return document;
}
function setupTooltipsLoop(anchors,begin,howmany,sleep, remove, popData) {
log(simplePrintf('setupTooltipsLoop(%s,%s,%s,%s,%s)', arguments));
var finish=begin+howmany;
var loopend = Math.min(finish, anchors.length);
var j=loopend - begin;
log ('setupTooltips: anchors.length=' + anchors.length + ', begin=' + begin +
', howmany=' + howmany + ', loopend=' + loopend + ', remove=' + remove);
var doTooltip= remove ? removeTooltip : addTooltip;
// try a faster (?) loop construct
if (j > 0) {
do {
var a=anchors[loopend - j];
if (typeof a==='undefined' || !a || !a.href) {
log('got null anchor at index ' + loopend - j);
continue;
}
doTooltip(a, popData);
} while (--j);
}
if (finish < anchors.length) {
setTimeout(function() {
setupTooltipsLoop(anchors,finish,howmany,sleep,remove,popData);},
sleep);
} else {
if ( !remove && ! getValueOf('popupTocLinks')) { rmTocTooltips(); }
pg.flag.finishedLoading=true;
}
}
// eliminate popups from the TOC
// This also kills any onclick stuff that used to be going on in the toc
function rmTocTooltips() {
var toc=document.getElementById('toc');
if (toc) {
var tocLinks=toc.getElementsByTagName('A');
var tocLen = tocLinks.length;
for (var j=0; j<tocLen; ++j) {
removeTooltip(tocLinks[j], true);
}
}
}
function addTooltip(a, popData) {
if ( !isPopupLink(a) ) { return; }
a.onmouseover=mouseOverWikiLink;
a.onmouseout= mouseOutWikiLink;
a.onmousedown = killPopup;
a.hasPopup = true;
a.popData = popData;
}
function removeTooltip(a) {
if ( !a.hasPopup ) { return; }
a.onmouseover = null;
a.onmouseout = null;
if (a.originalTitle) { a.title = a.originalTitle; }
a.hasPopup=false;
}
function removeTitle(a) {
if (!a.originalTitle) {
a.originalTitle=a.title;
}
a.title='';
}
function restoreTitle(a) {
if ( a.title || !a.originalTitle ) { return; }
a.title = a.originalTitle;
}
function registerHooks(np) {
var popupMaxWidth=getValueOf('popupMaxWidth');
if (typeof popupMaxWidth === 'number') {
var setMaxWidth = function () {
np.mainDiv.style.maxWidth = popupMaxWidth + 'px';
np.maxWidth = popupMaxWidth;
};
np.addHook(setMaxWidth, 'unhide', 'before');
}
//<NOLITE>
np.addHook(addPopupShortcuts, 'unhide', 'after');
np.addHook(rmPopupShortcuts, 'hide', 'before');
//</NOLITE>
}
function removeModifierKeyHandler(a) {
//remove listeners for modifier key if any that were added in mouseOverWikiLink
document.removeEventListener('keydown', a.modifierKeyHandler, false);
document.removeEventListener('keyup', a.modifierKeyHandler, false);
}
function mouseOverWikiLink(evt) {
if (!evt && window.event) {evt=window.event;}
// if the modifier is needed, listen for it,
// we will remove the listener when we mouseout of this link or kill popup.
if (getValueOf('popupModifier')) {
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
var key = action=='disable' ? 'keyup' : 'keydown';
var a = this;
a.modifierKeyHandler = function(evt) {
mouseOverWikiLink2(a, evt);
};
document.addEventListener(key, a.modifierKeyHandler, false);
}
return mouseOverWikiLink2(this, evt);
}
/**
* Gets the references list item that the provided footnote link targets. This
* is typically a li element within the ol.references element inside the reflist.
* @param {Element} a - A footnote link.
* @returns {Element|boolean} The targeted element, or false if one can't be found.
*/
function footnoteTarget(a) {
var aTitle=Title.fromAnchor(a);
// We want ".3A" rather than "%3A" or "?" here, so use the anchor property directly
var anch = aTitle.anchor;
if ( ! /^(cite_note-|_note-|endnote)/.test(anch) ) { return false; }
var lTitle=Title.fromURL(location.href);
if ( lTitle.toString(true) !== aTitle.toString(true) ) { return false; }
var el=document.getElementById(anch);
while ( el && typeof el.nodeName === 'string') {
var nt = el.nodeName.toLowerCase();
if ( nt === 'li' ) { return el; }
else if ( nt === 'body' ) { return false; }
else if ( el.parentNode ) { el=el.parentNode; }
else { return false; }
}
return false;
}
function footnotePreview(x, navpop) {
setPopupHTML('<hr />' + x.innerHTML, 'popupPreview', navpop.idNumber);
}
function modifierPressed(evt) {
var mod=getValueOf('popupModifier');
if (!mod) { return false; }
if (!evt && window.event) {evt=window.event;}
return ( evt && mod && evt[mod.toLowerCase() + 'Key'] );
}
// Checks if the correct modifier pressed/unpressed if needed
function isCorrectModifier(a,evt) {
if (!getValueOf('popupModifier')) { return true; }
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
return ( action == 'enable' && modifierPressed(evt) ||
action == 'disable' && !modifierPressed(evt) );
}
function mouseOverWikiLink2(a, evt) {
if (!isCorrectModifier(a,evt)) { return; }
if ( getValueOf('removeTitles') ) { removeTitle(a); }
if ( a==pg.current.link && a.navpopup && a.navpopup.isVisible() ) { return; }
pg.current.link=a;
if (getValueOf('simplePopups') && !pg.option.popupStructure) {
// reset *default value* of popupStructure
setDefault('popupStructure', 'original');
}
var article=(new Title()).fromAnchor(a);
// set global variable (ugh) to hold article (wikipage)
pg.current.article = article;
if (!a.navpopup) {
a.navpopup=newNavpopup(a, article);
pg.current.linksHash[a.href] = a.navpopup;
pg.current.links.push(a);
}
if (a.navpopup.pending === null || a.navpopup.pending !== 0) {
// either fresh popups or those with unfinshed business are redone from scratch
simplePopupContent(a, article);
}
a.navpopup.showSoonIfStable(a.navpopup.delay);
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=setInterval(checkPopupPosition, 600);
if(getValueOf('simplePopups')) {
if (getValueOf('popupPreviewButton') && !a.simpleNoMore) {
var d=document.createElement('div');
d.className='popupPreviewButtonDiv';
var s=document.createElement('span');
d.appendChild(s);
s.className='popupPreviewButton';
s['on' + getValueOf('popupPreviewButtonEvent')] = function() {
a.simpleNoMore=true;
d.style.display = "none";
nonsimplePopupContent(a,article);
};
s.innerHTML=popupString('show preview');
setPopupHTML(d, 'popupPreview', a.navpopup.idNumber);
}
}
if (a.navpopup.pending !== 0 ) {
nonsimplePopupContent(a, article);
}
}
// simplePopupContent: the content that do not require additional download
// (it is shown even when simplePopups is true)
function simplePopupContent(a, article) {
/* FIXME hack */ a.navpopup.hasPopupMenu=false;
a.navpopup.setInnerHTML(popupHTML(a));
fillEmptySpans({navpopup:a.navpopup});
if (getValueOf('popupDraggable'))
{
var dragHandle = getValueOf('popupDragHandle') || null;
if (dragHandle && dragHandle != 'all') {
dragHandle += a.navpopup.idNumber;
}
setTimeout(function(){a.navpopup.makeDraggable(dragHandle);}, 150);
}
//<NOLITE>
if (getValueOf('popupRedlinkRemoval') && a.className=='new') {
setPopupHTML('<br>'+popupRedlinkHTML(article), 'popupRedlink', a.navpopup.idNumber);
}
//</NOLITE>
}
function debugData(navpopup) {
if(getValueOf('popupDebugging') && navpopup.idNumber) {
setPopupHTML('idNumber='+navpopup.idNumber + ', pending=' + navpopup.pending,
'popupError', navpopup.idNumber);
}
}
function newNavpopup(a, article) {
var navpopup = new Navpopup();
navpopup.fuzz=5;
navpopup.delay=getValueOf('popupDelay')*1000;
// increment global counter now
navpopup.idNumber = ++pg.idNumber;
navpopup.parentAnchor = a;
navpopup.parentPopup = (a.popData && a.popData.owner);
navpopup.article = article;
registerHooks(navpopup);
return navpopup;
}
// Should we show nonsimple context?
// If simplePopups is set to true, then we do not show nonsimple context,
// but if a bottom "show preview" was clicked we do show nonsimple context
function shouldShowNonSimple(a) {
return !getValueOf('simplePopups') || a.simpleNoMore;
}
// Should we show nonsimple context govern by the option (e.g. popupUserInfo)?
// If the user explicitly asked for nonsimple context by setting the option to true,
// then we show it even in nonsimple mode.
function shouldShow(a,option) {
if (shouldShowNonSimple(a)) {
return getValueOf(option);
} else {
return (typeof window[option] != 'undefined' ) && window[option];
}
}
function nonsimplePopupContent(a, article) {
var diff=null, history=null;
var params=parseParams(a.href);
var oldid=(typeof params.oldid=='undefined' ? null : params.oldid);
//<NOLITE>
if(shouldShow(a,'popupPreviewDiffs')) {
diff=params.diff;
}
if(shouldShow(a,'popupPreviewHistory')) {
history=(params.action=='history');
}
//</NOLITE>
a.navpopup.pending=0;
var referenceElement = footnoteTarget(a);
if (referenceElement) {
footnotePreview(referenceElement, a.navpopup);
//<NOLITE>
} else if ( diff || diff === 0 ) {
loadDiff(article, oldid, diff, a.navpopup);
} else if ( history ) {
loadAPIPreview('history', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.contribs.test(a.href) ) {
loadAPIPreview('contribs', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.backlinks.test(a.href) ) {
loadAPIPreview('backlinks', article, a.navpopup);
} else if ( // FIXME should be able to get all preview combinations with options
article.namespaceId()==pg.nsImageId &&
( shouldShow(a,'imagePopupsForImages') || ! anchorContainsImage(a) )
) {
loadAPIPreview('imagepagepreview', article, a.navpopup);
loadImage(article, a.navpopup);
//</NOLITE>
} else {
if (article.namespaceId() == pg.nsCategoryId &&
shouldShow(a,'popupCategoryMembers')) {
loadAPIPreview('category', article, a.navpopup);
} else if ((article.namespaceId() == pg.nsUserId || article.namespaceId() == pg.nsUsertalkId) &&
shouldShow(a,'popupUserInfo')) {
loadAPIPreview('userinfo', article, a.navpopup);
}
if (shouldShowNonSimple(a)) startArticlePreview(article, oldid, a.navpopup);
}
}
function pendingNavpopTask(navpop) {
if (navpop && navpop.pending === null) { navpop.pending=0; }
++navpop.pending;
debugData(navpop);
}
function completedNavpopTask(navpop) {
if (navpop && navpop.pending) { --navpop.pending; }
debugData(navpop);
}
function startArticlePreview(article, oldid, navpop) {
navpop.redir=0;
loadPreview(article, oldid, navpop);
}
function loadPreview(article, oldid, navpop) {
if (!navpop.redir) { navpop.originalArticle=article; }
article.oldid = oldid;
loadAPIPreview('revision', article, navpop);
}
function loadPreviewFromRedir(redirMatch, navpop) {
// redirMatch is a regex match
var target = new Title().fromWikiText(redirMatch[2]);
// overwrite (or add) anchor from original target
// mediawiki does overwrite; eg User:Lupin/foo3#Done
if ( navpop.article.anchor ) { target.anchor = navpop.article.anchor; }
navpop.redir++;
navpop.redirTarget=target;
//<NOLITE>
var warnRedir = redirLink(target, navpop.article);
setPopupHTML(warnRedir, 'popupWarnRedir', navpop.idNumber);
//</NOLITE>
navpop.article=target;
fillEmptySpans({redir: true, redirTarget: target, navpopup:navpop});
return loadPreview(target, null, navpop);
}
function insertPreview(download) {
if (!download.owner) { return; }
var redirMatch = pg.re.redirect.exec(download.data);
if (download.owner.redir === 0 && redirMatch) {
loadPreviewFromRedir(redirMatch, download.owner);
return;
}
if (download.owner.visible || !getValueOf('popupLazyPreviews')) {
insertPreviewNow(download);
} else {
var id=(download.owner.redir) ? 'PREVIEW_REDIR_HOOK' : 'PREVIEW_HOOK';
download.owner.addHook( function(){insertPreviewNow(download); return true;},
'unhide', 'after', id );
}
}
function insertPreviewNow(download) {
if (!download.owner) { return; }
var wikiText=download.data;
var navpop=download.owner;
var art=navpop.redirTarget || navpop.originalArticle;
//<NOLITE>
makeFixDabs(wikiText, navpop);
if (getValueOf('popupSummaryData')) {
getPageInfo(wikiText, download);
setPopupTrailer(getPageInfo(wikiText, download), navpop.idNumber);
}
var imagePage='';
if (art.namespaceId()==pg.nsImageId) { imagePage=art.toString(); }
else { imagePage=getValidImageFromWikiText(wikiText); }
if(imagePage) { loadImage(Title.fromWikiText(imagePage), navpop); }
//</NOLITE>
if (getValueOf('popupPreviews')) { insertArticlePreview(download, art, navpop); }
}
function insertArticlePreview(download, art, navpop) {
if (download && typeof download.data == typeof ''){
if (art.namespaceId()==pg.nsTemplateId && getValueOf('popupPreviewRawTemplates')) {
// FIXME compare/consolidate with diff escaping code for wikitext
var h='<hr /><span style="font-family: monospace;">' + download.data.entify().split('\\n').join('<br />\\n') + '</span>';
setPopupHTML(h, 'popupPreview', navpop.idNumber);
}
else {
var p=prepPreviewmaker(download.data, art, navpop);
p.showPreview();
}
}
}
function prepPreviewmaker(data, article, navpop) {
// deal with tricksy anchors
var d=anchorize(data, article.anchorString());
var urlBase=joinPath([pg.wiki.articlebase, article.urlString()]);
var p=new Previewmaker(d, urlBase, navpop);
return p;
}
// Try to imitate the way mediawiki generates HTML anchors from section titles
function anchorize(d, anch) {
if (!anch) { return d; }
var anchRe=RegExp('(?:=+\\s*' + literalizeRegex(anch).replace(/[_ ]/g, '[_ ]') + '\\s*=+|\\{\\{\\s*'+getValueOf('popupAnchorRegexp')+'\\s*(?:\\|[^|}]*)*?\\s*'+literalizeRegex(anch)+'\\s*(?:\\|[^}]*)?}})');
var match=d.match(anchRe);
if(match && match.length > 0 && match[0]) { return d.substring(d.indexOf(match[0])); }
// now try to deal with == foo [[bar|baz]] boom == -> #foo_baz_boom
var lines=d.split('\n');
for (var i=0; i<lines.length; ++i) {
lines[i]=lines[i].replace(RegExp('[[]{2}([^|\\]]*?[|])?(.*?)[\\]]{2}', 'g'), '$2')
.replace(/'''([^'])/g, '$1').replace(RegExp("''([^'])", 'g'), '$1');
if (lines[i].match(anchRe)) {
return d.split('\n').slice(i).join('\n').replace(RegExp('^[^=]*'), '');
}
}
return d;
}
function killPopup() {
removeModifierKeyHandler(this);
if (getValueOf('popupShortcutKeys')) { rmPopupShortcuts(); }
if (!pg) { return; }
if (pg.current.link && pg.current.link.navpopup) { pg.current.link.navpopup.banish(); }
pg.current.link=null;
abortAllDownloads();
if (pg.timer.checkPopupPosition) {
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=null;
}
return true; // preserve default action
}
// ENDFILE: actions.js
// STARTFILE: domdrag.js
/**
@fileoverview
The {@link Drag} object, which enables objects to be dragged around.
<pre>
*************************************************
dom-drag.js
09.25.2001
www.youngpup.net
**************************************************
10.28.2001 - fixed minor bug where events
sometimes fired off the handle, not the root.
*************************************************
Pared down, some hooks added by User:Lupin
Copyright Aaron Boodman.
Saying stupid things daily since March 2001.
</pre>
*/
/**
Creates a new Drag object. This is used to make various DOM elements draggable.
@constructor
*/
function Drag () {
/**
Condition to determine whether or not to drag. This function should take one parameter, an Event.
To disable this, set it to <code>null</code>.
@type Function
*/
this.startCondition = null;
/**
Hook to be run when the drag finishes. This is passed the final coordinates of
the dragged object (two integers, x and y). To disables this, set it to <code>null</code>.
@type Function
*/
this.endHook = null;
}
/**
Gets an event in a cross-browser manner.
@param {Event} e
@private
*/
Drag.prototype.fixE = function(e) {
if (typeof e == 'undefined') { e = window.event; }
if (typeof e.layerX == 'undefined') { e.layerX = e.offsetX; }
if (typeof e.layerY == 'undefined') { e.layerY = e.offsetY; }
return e;
};
/**
Initialises the Drag instance by telling it which object you want to be draggable, and what you want to drag it by.
@param {DOMElement} o The "handle" by which <code>oRoot</code> is dragged.
@param {DOMElement} oRoot The object which moves when <code>o</code> is dragged, or <code>o</code> if omitted.
*/
Drag.prototype.init = function(o, oRoot) {
var dragObj = this;
this.obj = o;
o.onmousedown = function(e) { dragObj.start.apply( dragObj, [e]); };
o.dragging = false;
o.popups_draggable = true;
o.hmode = true;
o.vmode = true;
o.root = oRoot ? oRoot : o ;
if (isNaN(parseInt(o.root.style.left, 10))) { o.root.style.left = "0px"; }
if (isNaN(parseInt(o.root.style.top, 10))) { o.root.style.top = "0px"; }
o.root.onthisStart = function(){};
o.root.onthisEnd = function(){};
o.root.onthis = function(){};
};
/**
Starts the drag.
@private
@param {Event} e
*/
Drag.prototype.start = function(e) {
var o = this.obj; // = this;
e = this.fixE(e);
if (this.startCondition && !this.startCondition(e)) { return; }
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10);
o.root.onthisStart(x, y);
o.lastMouseX = e.clientX;
o.lastMouseY = e.clientY;
var dragObj = this;
o.onmousemoveDefault = document.onmousemove;
o.dragging = true;
document.onmousemove = function(e) { dragObj.drag.apply( dragObj, [e] ); };
document.onmouseup = function(e) { dragObj.end.apply( dragObj, [e] ); };
return false;
};
/**
Does the drag.
@param {Event} e
@private
*/
Drag.prototype.drag = function(e) {
e = this.fixE(e);
var o = this.obj;
var ey = e.clientY;
var ex = e.clientX;
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10 );
var nx, ny;
nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));
this.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
this.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
this.obj.lastMouseX = ex;
this.obj.lastMouseY = ey;
this.obj.root.onthis(nx, ny);
return false;
};
/**
Ends the drag.
@private
*/
Drag.prototype.end = function() {
document.onmousemove=this.obj.onmousemoveDefault;
document.onmouseup = null;
this.obj.dragging = false;
if (this.endHook) {
this.endHook( parseInt(this.obj.root.style[this.obj.hmode ? "left" : "right"], 10),
parseInt(this.obj.root.style[this.obj.vmode ? "top" : "bottom"], 10));
}
};
// ENDFILE: domdrag.js
// STARTFILE: structures.js
//<NOLITE>
pg.structures.original={};
pg.structures.original.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle',
'popupUserData', 'popupData', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.original.popupRedirSpans=function () {
return ['popupRedir', 'popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'];
};
pg.structures.original.popupTitle=function (x) {
log ('defaultstructure.popupTitle');
if (!getValueOf('popupNavLinks')) {
return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
}
return '';
};
pg.structures.original.popupTopLinks=function (x) {
log ('defaultstructure.popupTopLinks');
if (getValueOf('popupNavLinks')) { return navLinksHTML(x.article, x.hint, x.params); }
return '';
};
pg.structures.original.popupImage=function(x) {
log ('original.popupImage, x.article='+x.article+', x.navpop.idNumber='+x.navpop.idNumber);
return imageHTML(x.article, x.navpop.idNumber);
};
pg.structures.original.popupRedirTitle=pg.structures.original.popupTitle;
pg.structures.original.popupRedirTopLinks=pg.structures.original.popupTopLinks;
function copyStructure(oldStructure, newStructure) {
pg.structures[newStructure]={};
for (var prop in pg.structures[oldStructure]) {
pg.structures[newStructure][prop]=pg.structures[oldStructure][prop];
}
}
copyStructure('original', 'nostalgia');
pg.structures.nostalgia.popupTopLinks=function(x) {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>';
str+='if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' +
editstr + '}';
var historystr='<<history|shortcut=h>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str += '<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>';
str += 'if(admin){<br>}else{*}<<move|shortcut=m>>';
// admin links
str += 'if(admin){*<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>}';
return navlinkStringToHTML(str, x.article, x.params);
};
pg.structures.nostalgia.popupRedirTopLinks=pg.structures.nostalgia.popupTopLinks;
/** -- fancy -- **/
copyStructure('original', 'fancy');
pg.structures.fancy.popupTitle=function (x) {
return navlinkStringToHTML('<font size=+0><<mainlink>></font>',x.article,x.params);
};
pg.structures.fancy.popupTopLinks=function(x) {
var hist='<<history|shortcut=h|hist>>|<<lastEdit|shortcut=/|last>>|<<editors|shortcut=E|eds>>';
var watch='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
var move='<<move|shortcut=m|move>>';
return navlinkStringToHTML('if(talk){' +
'<<edit|shortcut=e>>|<<new|shortcut=+|+>>*' + hist + '*' +
'<<article|shortcut=a>>|<<editArticle|edit>>' + '*' + watch + '*' + move +
'}else{<<edit|shortcut=e>>*' + hist +
'*<<talk|shortcut=t|>>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>' +
'*' + watch + '*' + move+'}<br>', x.article, x.params);
};
pg.structures.fancy.popupOtherLinks=function(x) {
var admin='<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*<<undelete|undeleteShort>>|<<delete|shortcut=d|del>>';
var user='<<contribs|shortcut=c>>if(wikimedia){|<<count|shortcut=#|#>>}';
user+='if(ipuser){|<<arin>>}else{*<<email|shortcut=E|'+
popupString('email')+'>>}if(admin){*<<block|shortcut=b>>}';
var normal='<<whatLinksHere|shortcut=l|links here>>*<<relatedChanges|shortcut=r|related>>';
return navlinkStringToHTML('<br>if(user){' + user + '*}if(admin){'+admin+'if(user){<br>}else{*}}' + normal,
x.article, x.params);
};
pg.structures.fancy.popupRedirTitle=pg.structures.fancy.popupTitle;
pg.structures.fancy.popupRedirTopLinks=pg.structures.fancy.popupTopLinks;
pg.structures.fancy.popupRedirOtherLinks=pg.structures.fancy.popupOtherLinks;
/** -- fancy2 -- **/
// hack for User:MacGyverMagic
copyStructure('fancy', 'fancy2');
pg.structures.fancy2.popupTopLinks=function(x) { // hack out the <br> at the end and put one at the beginning
return '<br>'+pg.structures.fancy.popupTopLinks(x).replace(RegExp('<br>$','i'),'');
};
pg.structures.fancy2.popupLayout=function () { // move toplinks to after the title
return ['popupError', 'popupImage', 'popupTitle', 'popupUserData', 'popupData', 'popupTopLinks', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
/** -- menus -- **/
copyStructure('original', 'menus');
pg.structures.menus.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupUserData', 'popupData', 'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.menus.popupTopLinks = function (x, shorter) {
// FIXME maybe this stuff should be cached
var s=[];
var dropdiv='<div class="popup_drop">';
var enddiv='</div>';
var hist='<<history|shortcut=h>>';
if (!shorter) { hist = '<menurow>' + hist +
'|<<historyfeed|rss>>|<<editors|shortcut=E>></menurow>'; }
var lastedit='<<lastEdit|shortcut=/|show last edit>>';
var thank='if(diff){<<thank|send thanks>>}';
var jsHistory='<<lastContrib|last set of edits>><<sinceMe|changes since mine>>';
var linkshere='<<whatLinksHere|shortcut=l|what links here>>';
var related='<<relatedChanges|shortcut=r|related changes>>';
var search='<menurow><<search|shortcut=s>>if(wikimedia){|<<globalsearch|shortcut=g|global>>}' +
'|<<google|shortcut=G|web>></menurow>';
var watch='<menurow><<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>></menurow>';
var protect='<menurow><<unprotect|unprotectShort>>|' +
'<<protect|shortcut=p>>|<<protectlog|log>></menurow>';
var del='<menurow><<undelete|undeleteShort>>|<<delete|shortcut=d>>|' +
'<<deletelog|log>></menurow>';
var move='<<move|shortcut=m|move page>>';
var nullPurge='<menurow><<nullEdit|shortcut=n|null edit>>|<<purge|shortcut=P>></menurow>';
var viewOptions='<menurow><<view|shortcut=v>>|<<render|shortcut=S>>|<<raw>></menurow>';
var editRow='if(oldid){' +
'<menurow><<edit|shortcut=e>>|<<editOld|shortcut=e|this revision>></menurow>' +
'<menurow><<revert|shortcut=v>>|<<undo>></menurow>' + '}else{<<edit|shortcut=e>>}';
var markPatrolled='if(rcid){<<markpatrolled|mark patrolled>>}';
var newTopic='if(talk){<<new|shortcut=+|new topic>>}';
var protectDelete='if(admin){' + protect + del + '}';
if (getValueOf('popupActionsMenu')) {
s.push( '<<mainlink>>*' + dropdiv + menuTitle('tác vụ'));
} else {
s.push( dropdiv + '<<mainlink>>');
}
s.push( '<menu>');
s.push( editRow + markPatrolled + newTopic + hist + lastedit + thank );
if (!shorter) { s.push(jsHistory); }
s.push( move + linkshere + related);
if (!shorter) { s.push(nullPurge + search); }
if (!shorter) { s.push(viewOptions); }
s.push('<hr />' + watch + protectDelete);
s.push('<hr />' +
'if(talk){<<article|shortcut=a|view article>><<editArticle|edit article>>}' +
'else{<<talk|shortcut=t|talk page>><<editTalk|edit talk>>' +
'<<newTalk|shortcut=+|new topic>>}</menu>' + enddiv);
// user menu starts here
var email='<<email|shortcut=E|email user>>';
var contribs= 'if(wikimedia){<menurow>}<<contribs|shortcut=c|contributions>>if(wikimedia){</menurow>}' +
'if(admin){<menurow><<deletedContribs>></menurow>}';
s.push('if(user){*' + dropdiv + menuTitle('user'));
s.push('<menu>');
s.push('<menurow><<userPage|shortcut=u|user page>>|<<userSpace|space>></menurow>');
s.push('<<userTalk|shortcut=t|user talk>><<editUserTalk|edit user talk>>' +
'<<newUserTalk|shortcut=+|leave comment>>');
if(!shorter) { s.push( 'if(ipuser){<<arin>>}else{' + email + '}' ); }
else { s.push( 'if(ipuser){}else{' + email + '}' ); }
s.push('<hr />' + contribs + '<<userlog|shortcut=L|user log>>');
s.push('if(wikimedia){<<count|shortcut=#|edit counter>>}');
s.push('if(admin){<menurow><<unblock|unblockShort>>|<<block|shortcut=b|block user>></menurow>}');
s.push('<<blocklog|shortcut=B|block log>>');
s.push('</menu>' + enddiv + '}');
// popups menu starts here
if (getValueOf('popupSetupMenu') && !x.navpop.hasPopupMenu /* FIXME: hack */) {
x.navpop.hasPopupMenu=true;
s.push('*' + dropdiv + menuTitle('popupsMenu') + '<menu>');
s.push('<<togglePreviews|toggle previews>>');
s.push('<<purgePopups|reset>>');
s.push('<<disablePopups|disable>>');
s.push('</menu>'+enddiv);
}
return navlinkStringToHTML(s.join(''), x.article, x.params);
};
function menuTitle(s) {
return '<a href="#" noPopup=1>' + popupString(s) + '</a>';
}
pg.structures.menus.popupRedirTitle=pg.structures.menus.popupTitle;
pg.structures.menus.popupRedirTopLinks=pg.structures.menus.popupTopLinks;
copyStructure('menus', 'shortmenus');
pg.structures.shortmenus.popupTopLinks=function(x) {
return pg.structures.menus.popupTopLinks(x,true);
};
pg.structures.shortmenus.popupRedirTopLinks=pg.structures.shortmenus.popupTopLinks;
//</NOLITE>
pg.structures.lite={};
pg.structures.lite.popupLayout=function () {
return ['popupTitle', 'popupPreview' ];
};
pg.structures.lite.popupTitle=function (x) {
log (x.article + ': structures.lite.popupTitle');
//return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
return '<div><span class="popup_mainlink"><b>' + x.article.toString() + '</b></span></div>';
};
// ENDFILE: structures.js
// STARTFILE: autoedit.js
//<NOLITE>
function substitute(data,cmdBody) {
// alert('sub\nfrom: '+cmdBody.from+'\nto: '+cmdBody.to+'\nflags: '+cmdBody.flags);
var fromRe=RegExp(cmdBody.from, cmdBody.flags);
return data.replace(fromRe, cmdBody.to);
}
function execCmds(data, cmdList) {
for (var i=0; i<cmdList.length; ++i) {
data=cmdList[i].action(data, cmdList[i]);
}
return data;
}
function parseCmd(str) {
// returns a list of commands
if (!str.length) { return []; }
var p=false;
switch (str.charAt(0)) {
case 's':
p=parseSubstitute(str);
break;
default:
return false;
}
if (p) { return [p].concat(parseCmd(p.remainder)); }
return false;
}
function unEscape(str, sep) {
return str.split('\\\\').join('\\').split('\\'+sep).join(sep).split('\\n').join('\n');
}
function parseSubstitute(str) {
// takes a string like s/a/b/flags;othercmds and parses it
var from,to,flags,tmp;
if (str.length<4) { return false; }
var sep=str.charAt(1);
str=str.substring(2);
tmp=skipOver(str,sep);
if (tmp) { from=tmp.segment; str=tmp.remainder; }
else { return false; }
tmp=skipOver(str,sep);
if (tmp) { to=tmp.segment; str=tmp.remainder; }
else { return false; }
flags='';
if (str.length) {
tmp=skipOver(str,';') || skipToEnd(str, ';');
if (tmp) {flags=tmp.segment; str=tmp.remainder; }
}
return {action: substitute, from: from, to: to, flags: flags, remainder: str};
}
function skipOver(str,sep) {
var endSegment=findNext(str,sep);
if (endSegment<0) { return false; }
var segment=unEscape(str.substring(0,endSegment), sep);
return {segment: segment, remainder: str.substring(endSegment+1)};
}
/*eslint-disable*/
function skipToEnd(str,sep) {
return {segment: str, remainder: ''};
}
/*eslint-enable */
function findNext(str, ch) {
for (var i=0; i<str.length; ++i) {
if (str.charAt(i)=='\\') { i+=2; }
if (str.charAt(i)==ch) { return i; }
}
return -1;
}
function setCheckbox(param, box) {
var val=mw.util.getParamValue(param);
if (val) {
switch (val) {
case '1': case 'yes': case 'true':
box.checked=true;
break;
case '0': case 'no': case 'false':
box.checked=false;
}
}
}
function autoEdit() {
setupPopups( function () {
if (mw.util.getParamValue('autoimpl') !== popupString('autoedit_version') ) { return false; }
if (mw.util.getParamValue('autowatchlist') && mw.util.getParamValue('actoken')===autoClickToken()) {
pg.fn.modifyWatchlist(mw.util.getParamValue('title'), mw.util.getParamValue('action'));
}
if (!document.editform) { return false; }
if (autoEdit.alreadyRan) { return false; }
autoEdit.alreadyRan=true;
var cmdString=mw.util.getParamValue('autoedit');
if (cmdString) {
try {
var editbox=document.editform.wpTextbox1;
var cmdList=parseCmd(cmdString);
var input=editbox.value;
var output=execCmds(input, cmdList);
editbox.value=output;
} catch (dang) { return; }
// wikEd user script compatibility
if (typeof(wikEdUseWikEd) != 'undefined') {
if (wikEdUseWikEd === true) {
WikEdUpdateFrame();
}
}
}
setCheckbox('autominor', document.editform.wpMinoredit);
setCheckbox('autowatch', document.editform.wpWatchthis);
var rvid = mw.util.getParamValue('autorv');
if (rvid) {
var url=pg.wiki.apiwikibase + '?action=query&format=json&formatversion=2&prop=revisions&revids='+rvid;
startDownload(url, null, autoEdit2);
} else { autoEdit2(); }
} );
}
function autoEdit2(d) {
var summary=mw.util.getParamValue('autosummary');
var summaryprompt=mw.util.getParamValue('autosummaryprompt');
var summarynotice='';
if (d && d.data && mw.util.getParamValue('autorv')) {
var s = getRvSummary(summary, d.data);
if (s === false) {
summaryprompt=true;
summarynotice=popupString('Failed to get revision information, please edit manually.\n\n');
summary = simplePrintf(summary, [mw.util.getParamValue('autorv'), '(unknown)', '(unknown)']);
} else { summary = s; }
}
if (summaryprompt) {
var txt= summarynotice +
popupString('Enter a non-empty edit summary or press cancel to abort');
var response=prompt(txt, summary);
if (response) { summary=response; }
else { return; }
}
if (summary) { document.editform.wpSummary.value=summary; }
// Attempt to avoid possible premature clicking of the save button
// (maybe delays in updates to the DOM are to blame?? or a red herring)
setTimeout(autoEdit3, 100);
}
function autoClickToken() {
return mw.user.sessionId();
}
function autoEdit3() {
if( mw.util.getParamValue('actoken') != autoClickToken()) { return; }
var btn=mw.util.getParamValue('autoclick');
if (btn) {
if (document.editform && document.editform[btn]) {
var button=document.editform[btn];
var msg=tprintf('The %s button has been automatically clicked. Please wait for the next page to load.',
[ button.value ]);
bannerMessage(msg);
document.title='('+document.title+')';
button.click();
} else {
alert(tprintf('Could not find button %s. Please check the settings in your javascript file.',
[ btn ]));
}
}
}
function bannerMessage(s) {
var headings=document.getElementsByTagName('h1');
if (headings) {
var div=document.createElement('div');
div.innerHTML='<font size=+1><b>' + s + '</b></font>';
headings[0].parentNode.insertBefore(div, headings[0]);
}
}
function getRvSummary(template, json) {
try {
var o=getJsObj(json);
var edit = anyChild(o.query.pages).revisions[0];
var timestamp = edit.timestamp.split(/[A-Z]/g).join(' ').replace(/^ *| *$/g, '');
return simplePrintf(template, [edit.revid, timestamp, edit.userhidden ? '(hidden)' : edit.user ]);
} catch (badness) {
return false;
}
}
//</NOLITE>
// ENDFILE: autoedit.js
// STARTFILE: downloader.js
/**
@fileoverview
{@link Downloader}, a xmlhttprequest wrapper, and helper functions.
*/
/**
Creates a new Downloader
@constructor
@class The Downloader class. Create a new instance of this class to download stuff.
@param {String} url The url to download. This can be omitted and supplied later.
*/
function Downloader(url) {
if (typeof XMLHttpRequest!='undefined') { this.http = new XMLHttpRequest(); }
/**
The url to download
@type String
*/
this.url = url;
/**
A universally unique ID number
@type integer
*/
this.id=null;
/**
Modification date, to be culled from the incoming headers
@type Date
@private
*/
this.lastModified = null;
/**
What to do when the download completes successfully
@type Function
@private
*/
this.callbackFunction = null;
/**
What to do on failure
@type Function
@private
*/
this.onFailure = null;
/**
Flag set on <code>abort</code>
@type boolean
*/
this.aborted = false;
/**
HTTP method. See https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html for possibilities.
@type String
*/
this.method='GET';
/**
Async flag.
@type boolean
*/
this.async=true;
}
new Downloader();
/** Submits the http request. */
Downloader.prototype.send = function (x) {
if (!this.http) { return null; }
return this.http.send(x);
};
/** Aborts the download, setting the <code>aborted</code> field to true. */
Downloader.prototype.abort = function () {
if (!this.http) { return null; }
this.aborted=true;
return this.http.abort();
};
/** Returns the downloaded data. */
Downloader.prototype.getData = function () {if (!this.http) { return null; } return this.http.responseText;};
/** Prepares the download. */
Downloader.prototype.setTarget = function () {
if (!this.http) { return null; }
this.http.open(this.method, this.url, this.async);
this.http.setRequestHeader( 'Api-User-Agent', pg.misc.userAgent );
};
/** Gets the state of the download. */
Downloader.prototype.getReadyState=function () {if (!this.http) { return null; } return this.http.readyState;};
pg.misc.downloadsInProgress = { };
/** Starts the download.
Note that setTarget {@link Downloader#setTarget} must be run first
*/
Downloader.prototype.start=function () {
if (!this.http) { return; }
pg.misc.downloadsInProgress[this.id] = this;
this.http.send(null);
};
/** Gets the 'Last-Modified' date from the download headers.
Should be run after the download completes.
Returns <code>null</code> on failure.
@return {Date}
*/
Downloader.prototype.getLastModifiedDate=function () {
if(!this.http) { return null; }
var lastmod=null;
try {
lastmod=this.http.getResponseHeader('Last-Modified');
} catch (err) {}
if (lastmod) { return new Date(lastmod); }
return null;
};
/** Sets the callback function.
@param {Function} f callback function, called as <code>f(this)</code> on success
*/
Downloader.prototype.setCallback = function (f) {
if(!this.http) { return; }
this.http.onreadystatechange = f;
};
Downloader.prototype.getStatus = function() { if (!this.http) { return null; } return this.http.status; };
//////////////////////////////////////////////////
// helper functions
/** Creates a new {@link Downloader} and prepares it for action.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function newDownload(url, id, callback, onfailure) {
var d=new Downloader(url);
if (!d.http) { return 'ohdear'; }
d.id=id;
d.setTarget();
if (!onfailure) {
onfailure=2;
}
var f = function () {
if (d.getReadyState() == 4) {
delete pg.misc.downloadsInProgress[this.id];
try {
if ( d.getStatus() == 200 ) {
d.data=d.getData();
d.lastModified=d.getLastModifiedDate();
callback(d);
} else if (typeof onfailure == typeof 1) {
if (onfailure > 0) {
// retry
newDownload(url, id, callback, onfailure - 1);
}
} else if (typeof onfailure === 'function') {
onfailure(d,url,id,callback);
}
} catch (somerr) { /* ignore it */ }
}
};
d.setCallback(f);
return d;
}
/** Simulates a download from cached data.
The supplied data is put into a {@link Downloader} as if it had downloaded it.
@param {String} url The url.
@param {integer} id The ID.
@param {Function} callback The callback, which is invoked immediately as <code>callback(d)</code>,
where <code>d</code> is the new {@link Downloader}.
@param {String} data The (cached) data.
@param {Date} lastModified The (cached) last modified date.
*/
function fakeDownload(url, id, callback, data, lastModified, owner) {
var d=newDownload(url,callback);
d.owner=owner;
d.id=id; d.data=data;
d.lastModified=lastModified;
return callback(d);
}
/**
Starts a download.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function startDownload(url, id, callback) {
var d=newDownload(url, id, callback);
if (typeof d == typeof '' ) { return d; }
d.start();
return d;
}
/**
Aborts all downloads which have been started.
*/
function abortAllDownloads() {
for ( var x in pg.misc.downloadsInProgress ) {
try {
pg.misc.downloadsInProgress[x].aborted=true;
pg.misc.downloadsInProgress[x].abort();
delete pg.misc.downloadsInProgress[x];
} catch (e) {}
}
}
// ENDFILE: downloader.js
// STARTFILE: livepreview.js
// TODO: location is often not correct (eg relative links in previews)
// NOTE: removed md5 and image and math parsing. was broken, lots of bytes.
/**
* InstaView - a Mediawiki to HTML converter in JavaScript
* Version 0.6.1
* Copyright (C) Pedro Fayolle 2005-2006
* https://en.wikipedia.org/wiki/User:Pilaf
* Distributed under the BSD license
*
* Changelog:
*
* 0.6.1
* - Fixed problem caused by \r characters
* - Improved inline formatting parser
*
* 0.6
* - Changed name to InstaView
* - Some major code reorganizations and factored out some common functions
* - Handled conversion of relative links (i.e. [[/foo]])
* - Fixed misrendering of adjacent definition list items
* - Fixed bug in table headings handling
* - Changed date format in signatures to reflect Mediawiki's
* - Fixed handling of [[:Image:...]]
* - Updated MD5 function (hopefully it will work with UTF-8)
* - Fixed bug in handling of links inside images
*
* To do:
* - Better support for math tags
* - Full support for <nowiki>
* - Parser-based (as opposed to RegExp-based) inline wikicode handling (make it one-pass and bullet-proof)
* - Support for templates (through AJAX)
* - Support for coloured links (AJAX)
*/
var Insta = {};
function setupLivePreview() {
// options
Insta.conf =
{
baseUrl: '',
user: {},
wiki: {
lang: pg.wiki.lang,
interwiki: pg.wiki.interwiki,
default_thumb_width: 180
},
paths: {
articles: pg.wiki.articlePath + '/',
// Only used for Insta previews with images. (not in popups)
math: '/math/',
images: '//upload.wikimedia.org/wikipedia/en/', // FIXME getImageUrlStart(pg.wiki.hostname),
images_fallback: '//upload.wikimedia.org/wikipedia/commons/',
},
locale: {
user: mw.config.get('wgFormattedNamespaces')[pg.nsUserId],
image: mw.config.get('wgFormattedNamespaces')[pg.nsImageId],
category: mw.config.get('wgFormattedNamespaces')[pg.nsCategoryId],
// shouldn't be used in popup previews, i think
months: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
}
};
// options with default values or backreferences
Insta.conf.user.name = Insta.conf.user.name || 'Wikipedian';
Insta.conf.user.signature = '[['+Insta.conf.locale.user+':'+Insta.conf.user.name+'|'+Insta.conf.user.name+']]';
//Insta.conf.paths.images = '//upload.wikimedia.org/wikipedia/' + Insta.conf.wiki.lang + '/';
// define constants
Insta.BLOCK_IMAGE = new RegExp('^\\[\\[(?:File|Image|'+Insta.conf.locale.image+
'):.*?\\|.*?(?:frame|thumbnail|thumb|none|right|left|center)', 'i');
}
Insta.dump = function(from, to)
{
if (typeof from == 'string') { from = document.getElementById(from); }
if (typeof to == 'string') { to = document.getElementById(to); }
to.innerHTML = this.convert(from.value);
};
Insta.convert = function(wiki)
{
var ll = (typeof wiki == 'string')? wiki.replace(/\r/g,'').split(/\n/): wiki, // lines of wikicode
o = '', // output
p = 0, // para flag
$r; // result of passing a regexp to $()
// some shorthands
function remain() { return ll.length; }
function sh() { return ll.shift(); } // shift
function ps(s) { o += s; } // push
// similar to C's printf, uses ? as placeholders, ?? to escape question marks
function f()
{
var i=1, a=arguments, f=a[0], o='', c, p;
for (; i<a.length; i++) {
if ((p=f.indexOf('?'))+1) {
// allow character escaping
i -= c = f.charAt(p+1)=='?' ? 1 : 0;
o += f.substring(0,p) + (c ? '?' : a[i]);
f = f.substr(p+1+c);
} else { break; }
}
return o+f;
}
function html_entities(s) {
return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
}
// Wiki text parsing to html is a nightmare.
// The below functions deliberately don't escape the ampersand since this would make it more difficult,
// and we don't absolutely need to for how we need it.
// This means that any unescaped ampersands in wikitext will remain unescaped and can cause invalid HTML.
// Browsers should all be able to handle it though.
// We also escape significant wikimarkup characters to prevent further matching on the processed text
function htmlescape_text(s) {
return s.replace(/</g,"<").replace(/>/g,">").replace(/:/g,":").replace(/\[/g,"[").replace(/]/g,"]");
}
function htmlescape_attr(s) {
return htmlescape_text(s).replace(/'/g,"'").replace(/"/g,""");
}
// return the first non matching character position between two strings
function str_imatch(a, b)
{
for (var i=0, l=Math.min(a.length, b.length); i<l; i++) {
if (a.charAt(i)!=b.charAt(i)) { break; }
}
return i;
}
// compare current line against a string or regexp
// if passed a string it will compare only the first string.length characters
// if passed a regexp the result is stored in $r
function $(c) { return (typeof c == 'string') ? (ll[0].substr(0,c.length)==c) : ($r = ll[0].match(c)); }
function $$(c) { return ll[0]==c; } // compare current line against a string
function _(p) { return ll[0].charAt(p); } // return char at pos p
function endl(s) { ps(s); sh(); }
function parse_list()
{
var prev='';
while (remain() && $(/^([*#:;]+)(.*)$/)) {
var l_match = $r;
sh();
var ipos = str_imatch(prev, l_match[1]);
// close uncontinued lists
for (var prevPos=prev.length-1; prevPos >= ipos; prevPos--) {
var pi = prev.charAt(prevPos);
if (pi=='*') { ps('</ul>'); }
else if (pi=='#') { ps('</ol>'); }
// close a dl only if the new item is not a dl item (:, ; or empty)
else if($.inArray(l_match[1].charAt(prevPos), ['','*','#'])) { ps('</dl>'); }
}
// open new lists
for (var matchPos=ipos; matchPos<l_match[1].length; matchPos++) {
var li = l_match[1].charAt(matchPos);
if (li=='*') { ps('<ul>'); }
else if (li=='#') { ps('<ol>'); }
// open a new dl only if the prev item is not a dl item (:, ; or empty)
else if ($.inArray(prev.charAt(matchPos), ['','*','#'])) { ps('<dl>'); }
}
switch (l_match[1].charAt(l_match[1].length-1)) {
case '*': case '#':
ps('<li>' + parse_inline_nowiki(l_match[2]));
break;
case ';':
ps('<dt>');
var dt_match = l_match[2].match(/(.*?)(:.*?)$/);
// handle ;dt :dd format
if (dt_match) {
ps(parse_inline_nowiki(dt_match[1]));
ll.unshift(dt_match[2]);
} else ps(parse_inline_nowiki(l_match[2]));
break;
case ':':
ps('<dd>' + parse_inline_nowiki(l_match[2]));
}
prev=l_match[1];
}
// close remaining lists
for (var i=prev.length-1; i>=0; i--) {
ps(f('</?>', (prev.charAt(i)=='*')? 'ul': ((prev.charAt(i)=='#')? 'ol': 'dl')));
}
}
function parse_table()
{
endl(f('<table>', $(/^\{\|( .*)$/)? $r[1]: ''));
for (;remain();) if ($('|')) switch (_(1)) {
case '}':
endl('</table>');
return;
case '-':
endl(f('<tr>', $(/\|-*(.*)/)[1]));
break;
default:
parse_table_data();
}
else if ($('!')) { parse_table_data(); }
else { sh(); }
}
function parse_table_data()
{
var td_line, match_i;
// 1: "|+", '|' or '+'
// 2: ??
// 3: attributes ??
// TODO: finish commenting this regexp
var td_match = sh().match(/^(\|\+|\||!)((?:([^[|]*?)\|(?!\|))?(.*))$/);
if (td_match[1] == '|+') ps('<caption');
else ps('<t' + ((td_match[1]=='|')?'d':'h'));
if (typeof td_match[3] != 'undefined') {
//ps(' ' + td_match[3])
match_i = 4;
} else match_i = 2;
ps('>');
if (td_match[1] != '|+') {
// use || or !! as a cell separator depending on context
// NOTE: when split() is passed a regexp make sure to use non-capturing brackets
td_line = td_match[match_i].split((td_match[1] == '|')? '||': /(?:\|\||!!)/);
ps(parse_inline_nowiki(td_line.shift()));
while (td_line.length) ll.unshift(td_match[1] + td_line.pop());
} else ps(td_match[match_i]);
var tc = 0, td = [];
while (remain()) {
td.push(sh());
if ($('|')) {
if (!tc) break; // we're at the outer-most level (no nested tables), skip to td parse
else if (_(1)=='}') tc--;
}
else if (!tc && $('!')) break;
else if ($('{|')) tc++;
}
if (td.length) ps(Insta.convert(td));
}
function parse_pre()
{
ps('<pre>');
do {
endl(parse_inline_nowiki(ll[0].substring(1)) + "\n");
} while (remain() && $(' '));
ps('</pre>');
}
function parse_block_image()
{
ps(parse_image(sh()));
}
function parse_image(str)
{
//<NOLITE>
// get what's in between "[[Image:" and "]]"
var tag = str.substring(str.indexOf(':') + 1, str.length - 2);
/* eslint-disable no-unused-vars */
var width;
var attr = [], filename, caption = '';
var thumb=0, frame=0, center=0;
var align='';
/* eslint-enable no-unused-vars */
if (tag.match(/\|/)) {
// manage nested links
var nesting = 0;
var last_attr;
for (var i = tag.length-1; i > 0; i--) {
if (tag.charAt(i) == '|' && !nesting) {
last_attr = tag.substr(i+1);
tag = tag.substring(0, i);
break;
} else switch (tag.substr(i-1, 2)) {
case ']]':
nesting++;
i--;
break;
case '[[':
nesting--;
i--;
}
}
attr = tag.split(/\s*\|\s*/);
attr.push(last_attr);
filename = attr.shift();
var w_match;
for (;attr.length; attr.shift()) {
w_match = attr[0].match(/^(\d*)(?:[px]*\d*)?px$/);
if (w_match) width = w_match[1];
else switch(attr[0]) {
case 'thumb':
case 'thumbnail':
thumb=true;
frame=true;
break;
case 'frame':
frame=true;
break;
case 'none':
case 'right':
case 'left':
center=false;
align=attr[0];
break;
case 'center':
center=true;
align='none';
break;
default:
if (attr.length == 1) caption = attr[0];
}
}
} else filename = tag;
return '';
//</NOLITE>
}
function parse_inline_nowiki(str)
{
var start, lastend=0;
var substart=0, nestlev=0, open, close, subloop;
var html='';
while (-1 != (start = str.indexOf('<nowiki>', substart))) {
html += parse_inline_wiki(str.substring(lastend, start));
start += 8;
substart = start;
subloop = true;
do {
open = str.indexOf('<nowiki>', substart);
close = str.indexOf('</nowiki>', substart);
if (close<=open || open==-1) {
if (close==-1) {
return html + html_entities(str.substr(start));
}
substart = close+9;
if (nestlev) {
nestlev--;
} else {
lastend = substart;
html += html_entities(str.substring(start, lastend-9));
subloop = false;
}
} else {
substart = open+8;
nestlev++;
}
} while (subloop);
}
return html + parse_inline_wiki(str.substr(lastend));
}
function parse_inline_images(str)
{
//<NOLITE>
var start, substart=0, nestlev=0;
var loop, close, open, wiki, html;
while (-1 != (start=str.indexOf('[[', substart))) {
if(str.substr(start+2).match(RegExp('^(Image|File|' + Insta.conf.locale.image + '):','i'))) {
loop=true;
substart=start;
do {
substart+=2;
close=str.indexOf(']]',substart);
open=str.indexOf('[[',substart);
if (close<=open||open==-1) {
if (close==-1) return str;
substart=close;
if (nestlev) {
nestlev--;
} else {
wiki=str.substring(start,close+2);
html=parse_image(wiki);
str=str.replace(wiki,html);
substart=start+html.length;
loop=false;
}
} else {
substart=open;
nestlev++;
}
} while (loop);
} else break;
}
//</NOLITE>
return str;
}
// the output of this function doesn't respect the FILO structure of HTML
// but since most browsers can handle it I'll save myself the hassle
function parse_inline_formatting(str)
{
var em,st,i,li,o='';
while ((i=str.indexOf("''",li))+1) {
o += str.substring(li,i);
li=i+2;
if (str.charAt(i+2)=="'") {
li++;
st=!st;
o+=st?'<strong>':'</strong>';
} else {
em=!em;
o+=em?'<em>':'</em>';
}
}
return o+str.substr(li);
}
function parse_inline_wiki(str)
{
str = parse_inline_images(str);
str = parse_inline_formatting(str);
// math
str = str.replace(/<(?:)math>(.*?)<\/math>/ig, '');
// Build a Mediawiki-formatted date string
var date = new Date();
var minutes = date.getUTCMinutes();
if (minutes < 10) minutes = '0' + minutes;
date = f("?:?, ? ? ? (UTC)", date.getUTCHours(), minutes, date.getUTCDate(), Insta.conf.locale.months[date.getUTCMonth()], date.getUTCFullYear());
// text formatting
return str.
// signatures
replace(/~{5}(?!~)/g, date).
replace(/~{4}(?!~)/g, Insta.conf.user.name+' '+date).
replace(/~{3}(?!~)/g, Insta.conf.user.name).
// [[:Category:...]], [[:Image:...]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):[^|]*?)\\]\\](\\w*)','gi'), function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2));}).
// remove straight category and interwiki tags
replace(RegExp('\\[\\[(?:'+Insta.conf.locale.category+'|'+Insta.conf.wiki.interwiki+'):.*?\\]\\]','gi'),'').
// [[:Category:...|Links]], [[:Image:...|Links]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):.*?)\\|([^\\]]+?)\\]\\](\\w*)','gi'), function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3));}).
// [[/Relative links]]
replace(/\[\[(\/[^|]*?)\]\]/g, function($0,$1){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($1)); }).
// [[/Replaced|Relative links]]
replace(/\[\[(\/.*?)\|(.+?)\]\]/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($2)); }).
// [[Common links]]
replace(/\[\[([^[|]*?)\]\](\w*)/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2)); }).
// [[Replaced|Links]]
replace(/\[\[([^[]*?)\|([^\]]+?)\]\](\w*)/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3)); }).
// [[Stripped:Namespace|Namespace]]
replace(/\[\[([^\]]*?:)?(.*?)( *\(.*?\))?\|\]\]/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1) + htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($2)); }).
// External links
replace(/\[(https?|news|ftp|mailto|gopher|irc):(\/*)([^\]]*?) (.*?)\]/g, function($0,$1,$2,$3,$4){return f("<a class='external' href='?:?'>?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($4)); }).
replace(/\[http:\/\/(.*?)\]/g, function($0,$1){return f("<a class='external' href='http://?'>[#]</a>", htmlescape_attr($1)); }).
replace(/\[(news|ftp|mailto|gopher|irc):(\/*)(.*?)\]/g, function($0,$1,$2,$3){return f("<a class='external' href='?:?'>?:?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($1), htmlescape_text($2) + htmlescape_text($3)); }).
replace(/(^| )(https?|news|ftp|mailto|gopher|irc):(\/*)([^ $]*[^.,!?;: $])/g, function($0,$1,$2,$3,$4){return f("?<a class='external' href='?:?'>?:?</a>", htmlescape_text($1), htmlescape_attr($2), htmlescape_attr($3) + htmlescape_attr($4), htmlescape_text($2), htmlescape_text($3) + htmlescape_text($4)); }).
replace('__NOTOC__','').
replace('__NOEDITSECTION__','');
}
// begin parsing
for (;remain();) if ($(/^(={1,6})(.*)\1(.*)$/)) {
p=0;
endl(f('<h?>?</h?>?', $r[1].length, parse_inline_nowiki($r[2]), $r[1].length, $r[3]));
} else if ($(/^[*#:;]/)) {
p=0;
parse_list();
} else if ($(' ')) {
p=0;
parse_pre();
} else if ($('{|')) {
p=0;
parse_table();
} else if ($(/^----+$/)) {
p=0;
endl('<hr />');
} else if ($(Insta.BLOCK_IMAGE)) {
p=0;
parse_block_image();
} else {
// handle paragraphs
if ($$('')) {
p = (remain()>1 && ll[1]===(''));
if (p) endl('<p><br>');
} else {
if(!p) {
ps('<p>');
p=1;
}
ps(parse_inline_nowiki(ll[0]) + ' ');
}
sh();
}
return o;
};
function wiki2html(txt,baseurl) {
Insta.conf.baseUrl=baseurl;
return Insta.convert(txt);
}
// ENDFILE: livepreview.js
// STARTFILE: pageinfo.js
//<NOLITE>
function popupFilterPageSize(data) {
return formatBytes(data.length);
}
function popupFilterCountLinks(data) {
var num=countLinks(data);
return String(num) + ' ' + ((num!=1)?popupString('wikiLinks'):popupString('wikiLink'));
}
function popupFilterCountImages(data) {
var num=countImages(data);
return String(num) + ' ' + ((num!=1)?popupString('images'):popupString('image'));
}
function popupFilterCountCategories(data) {
var num=countCategories(data);
return String(num) + ' ' + ((num!=1)?popupString('categories'):popupString('category'));
}
function popupFilterLastModified(data,download) {
var lastmod=download.lastModified;
var now=new Date();
var age=now-lastmod;
if (lastmod && getValueOf('popupLastModified')) {
return (tprintf('%s old', [formatAge(age)])).replace(RegExp(' ','g'), ' ');
}
return '';
}
function formatAge(age) {
// coerce into a number
var a=0+age, aa=a;
var seclen = 1000;
var minlen = 60*seclen;
var hourlen = 60*minlen;
var daylen = 24*hourlen;
var weeklen = 7*daylen;
var numweeks = (a-a%weeklen)/weeklen; a = a-numweeks*weeklen; var sweeks = addunit(numweeks, 'week');
var numdays = (a-a%daylen)/daylen; a = a-numdays*daylen; var sdays = addunit(numdays, 'day');
var numhours = (a-a%hourlen)/hourlen; a = a-numhours*hourlen; var shours = addunit(numhours,'hour');
var nummins = (a-a%minlen)/minlen; a = a-nummins*minlen; var smins = addunit(nummins, 'minute');
var numsecs = (a-a%seclen)/seclen; a = a-numsecs*seclen; var ssecs = addunit(numsecs, 'second');
if (aa > 4*weeklen) { return sweeks; }
if (aa > weeklen) { return sweeks + ' ' + sdays; }
if (aa > daylen) { return sdays + ' ' + shours; }
if (aa > 6*hourlen) { return shours; }
if (aa > hourlen) { return shours + ' ' + smins; }
if (aa > 10*minlen) { return smins; }
if (aa > minlen) { return smins + ' ' + ssecs; }
return ssecs;
}
function addunit(num,str) { return '' + num + ' ' + ((num!=1) ? popupString(str+'s') : popupString(str)) ;}
function runPopupFilters(list, data, download) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (list[i] && typeof list[i] == 'function') {
var s=list[i](data, download, download.owner.article);
if (s) { ret.push(s); }
}
}
return ret;
}
function getPageInfo(data, download) {
if (!data || data.length === 0) { return popupString('Empty page'); }
var popupFilters=getValueOf('popupFilters') || [];
var extraPopupFilters = getValueOf('extraPopupFilters') || [];
var pageInfoArray = runPopupFilters(popupFilters.concat(extraPopupFilters), data, download);
var pageInfo=pageInfoArray.join(', ');
if (pageInfo !== '' ) { pageInfo = upcaseFirst(pageInfo); }
return pageInfo;
}
// this could be improved!
function countLinks(wikiText) { return wikiText.split('[[').length - 1; }
// if N = # matches, n = # brackets, then
// String.parenSplit(regex) intersperses the N+1 split elements
// with Nn other elements. So total length is
// L= N+1 + Nn = N(n+1)+1. So N=(L-1)/(n+1).
function countImages(wikiText) {
return (wikiText.parenSplit(pg.re.image).length - 1) / (pg.re.imageBracketCount + 1);
}
function countCategories(wikiText) {
return (wikiText.parenSplit(pg.re.category).length - 1) / (pg.re.categoryBracketCount + 1);
}
function popupFilterStubDetect(data, download, article) {
var counts=stubCount(data, article);
if (counts.real) { return popupString('stub'); }
if (counts.sect) { return popupString('section stub'); }
return '';
}
function popupFilterDisambigDetect(data, download, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return ''; }
return (isDisambig(data, article)) ? popupString('disambig') : '';
}
function formatBytes(num) {
return (num > 949) ? (Math.round(num/100)/10+popupString('kB')) : (num +' ' + popupString('bytes')) ;
}
//</NOLITE>
// ENDFILE: pageinfo.js
// STARTFILE: titles.js
/**
@fileoverview Defines the {@link Title} class, and associated crufty functions.
<code>Title</code> deals with article titles and their various
forms. {@link Stringwrapper} is the parent class of
<code>Title</code>, which exists simply to make things a little
neater.
*/
/**
Creates a new Stringwrapper.
@constructor
@class the Stringwrapper class. This base class is not really
useful on its own; it just wraps various common string operations.
*/
function Stringwrapper() {
/**
Wrapper for this.toString().indexOf()
@param {String} x
@type integer
*/
this.indexOf=function(x){return this.toString().indexOf(x);};
/**
Returns this.value.
@type String
*/
this.toString=function(){return this.value;};
/**
Wrapper for {@link String#parenSplit} applied to this.toString()
@param {RegExp} x
@type Array
*/
this.parenSplit=function(x){return this.toString().parenSplit(x);};
/**
Wrapper for this.toString().substring()
@param {String} x
@param {String} y (optional)
@type String
*/
this.substring=function(x,y){
if (typeof y=='undefined') { return this.toString().substring(x); }
return this.toString().substring(x,y);
};
/**
Wrapper for this.toString().split()
@param {String} x
@type Array
*/
this.split=function(x){return this.toString().split(x);};
/**
Wrapper for this.toString().replace()
@param {String} x
@param {String} y
@type String
*/
this.replace=function(x,y){ return this.toString().replace(x,y); };
}
/**
Creates a new <code>Title</code>.
@constructor
@class The Title class. Holds article titles and converts them into
various forms. Also deals with anchors, by which we mean the bits
of the article URL after a # character, representing locations
within an article.
@param {String} value The initial value to assign to the
article. This must be the canonical title (see {@link
Title#value}. Omit this in the constructor and use another function
to set the title if this is unavailable.
*/
function Title(val) {
/**
The canonical article title. This must be in UTF-8 with no
entities, escaping or nasties. Also, underscores should be
replaced with spaces.
@type String
@private
*/
this.value=null;
/**
The canonical form of the anchor. This should be exactly as
it appears in the URL, i.e. with the .C3.0A bits in.
@type String
*/
this.anchor='';
this.setUtf(val);
}
Title.prototype=new Stringwrapper();
/**
Returns the canonical representation of the article title, optionally without anchor.
@param {boolean} omitAnchor
@fixme Decide specs for anchor
@return String The article title and the anchor.
*/
Title.prototype.toString=function(omitAnchor) {
return this.value + ( (!omitAnchor && this.anchor) ? '#' + this.anchorString() : '' );
};
Title.prototype.anchorString=function() {
if (!this.anchor) { return ''; }
var split=this.anchor.parenSplit(/((?:[.][0-9A-F]{2})+)/);
var len=split.length;
for (var j=1; j<len; j+=2) {
// FIXME s/decodeURI/decodeURIComponent/g ?
split[j]=decodeURIComponent(split[j].split('.').join('%')).split('_').join(' ');
}
return split.join('');
};
Title.prototype.urlAnchor=function() {
var split=this.anchor.parenSplit('/((?:[%][0-9A-F]{2})+)/');
var len=split.length;
for (var j=1; j<len; j+=2) {
split[j]=split[j].split('%').join('.');
}
return split.join('');
};
Title.prototype.anchorFromUtf=function(str) {
this.anchor=encodeURIComponent(str.split(' ').join('_'))
.split('%3A').join(':').split("'").join('%27').split('%').join('.');
};
Title.fromURL=function(h) {
return new Title().fromURL(h);
};
Title.prototype.fromURL=function(h) {
if (typeof h != 'string') {
this.value=null;
return this;
}
// NOTE : playing with decodeURI, encodeURI, escape, unescape,
// we seem to be able to replicate the IE borked encoding
// IE doesn't do this new-fangled utf-8 thing.
// and it's worse than that.
// IE seems to treat the query string differently to the rest of the url
// the query is treated as bona-fide utf8, but the first bit of the url is pissed around with
// we fix up & for all browsers, just in case.
var splitted=h.split('?');
splitted[0]=splitted[0].split('&').join('%26');
h=splitted.join('?');
var contribs=pg.re.contribs.exec(h);
if (contribs) {
if (contribs[1]=='title=') { contribs[3]=contribs[3].split('+').join(' '); }
var u=new Title(contribs[3]);
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + u.stripNamespace()));
return this;
}
var email=pg.re.email.exec(h);
if (email) {
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(email[3]).stripNamespace()));
return this;
}
var backlinks=pg.re.backlinks.exec(h);
if (backlinks) {
this.setUtf(this.decodeNasties(new Title(backlinks[3])));
return this;
}
//A dummy title object for a Special:Diff link.
var specialdiff=pg.re.specialdiff.exec(h);
if (specialdiff) {
this.setUtf(this.decodeNasties(new Title(mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Diff')));
return this;
}
// no more special cases to check --
// hopefully it's not a disguised user-related or specially treated special page
var m=pg.re.main.exec(h);
if(m === null) { this.value=null; }
else {
var fromBotInterface = /[?](.+[&])?title=/.test(h);
if (fromBotInterface) {
m[2]=m[2].split('+').join('_');
}
var extracted = m[2] + (m[3] ? '#' + m[3] : '');
if (pg.flag.isSafari && /%25[0-9A-Fa-f]{2}/.test(extracted)) {
// Fix Safari issue
// Safari sometimes encodes % as %25 in UTF-8 encoded strings like %E5%A3 -> %25E5%25A3.
this.setUtf(decodeURIComponent(unescape(extracted)));
} else {
this.setUtf(this.decodeNasties(extracted));
}
}
return this;
};
Title.prototype.decodeNasties=function(txt) {
var ret= this.decodeEscapes(decodeURI(txt));
ret = ret.replace(/[_ ]*$/, '');
return ret;
};
Title.prototype.decodeEscapes=function(txt) {
var split=txt.parenSplit(/((?:[%][0-9A-Fa-f]{2})+)/);
var len=split.length;
for (var i=1; i<len; i=i+2) {
// FIXME is decodeURIComponent better?
split[i]=unescape(split[i]);
}
return split.join('');
};
Title.fromAnchor=function(a) {
return new Title().fromAnchor(a);
};
Title.prototype.fromAnchor=function(a) {
if (!a) { this.value=null; return this; }
return this.fromURL(a.href);
};
Title.fromWikiText=function(txt) {
return new Title().fromWikiText(txt);
};
Title.prototype.fromWikiText=function(txt) {
// FIXME - testing needed
txt=myDecodeURI(txt);
this.setUtf(txt);
return this;
};
Title.prototype.hintValue=function(){
if(!this.value) { return ''; }
return safeDecodeURI(this.value);
};
//<NOLITE>
Title.prototype.toUserName=function(withNs) {
if (this.namespaceId() != pg.nsUserId && this.namespaceId() != pg.nsUsertalkId) {
this.value=null;
return;
}
this.value = (withNs ? mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' : '') + this.stripNamespace().split('/')[0];
};
Title.prototype.userName=function(withNs) {
var t=(new Title(this.value));
t.toUserName(withNs);
if (t.value) { return t; }
return null;
};
Title.prototype.toTalkPage=function() {
// convert article to a talk page, or if we can't, return null
// In other words: return null if this ALREADY IS a talk page
// and return the corresponding talk page otherwise
//
// Per https://www.mediawiki.org/wiki/Manual:Namespace#Subject_and_talk_namespaces
// * All discussion namespaces have odd-integer indices
// * The discussion namespace index for a specific namespace with index n is n + 1
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId>=0 && namespaceId % 2 === 0) //non-special and subject namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId+1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
//</NOLITE>
// Return canonical, localized namespace
Title.prototype.namespace=function() {
return mw.config.get('wgFormattedNamespaces')[this.namespaceId()];
};
Title.prototype.namespaceId=function() {
var n=this.value.indexOf(':');
if (n<0) { return 0; } //mainspace
var namespaceId = mw.config.get('wgNamespaceIds')[this.value.substring(0,n).split(' ').join('_').toLowerCase()];
if (typeof namespaceId=='undefined') return 0; //mainspace
return namespaceId;
};
//<NOLITE>
Title.prototype.talkPage=function() {
var t=new Title(this.value);
t.toTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.isTalkPage=function() {
if (this.talkPage()===null) { return true; }
return false;
};
Title.prototype.toArticleFromTalkPage=function() {
//largely copy/paste from toTalkPage above.
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId >= 0 && namespaceId % 2 == 1) //non-special and talk namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId-1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
Title.prototype.articleFromTalkPage=function() {
var t=new Title(this.value);
t.toArticleFromTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.articleFromTalkOrArticle=function() {
var t=new Title(this.value);
if ( t.toArticleFromTalkPage() ) { return t; }
return this;
};
Title.prototype.isIpUser=function() {
return pg.re.ipUser.test(this.userName());
};
//</NOLITE>
Title.prototype.stripNamespace=function(){ // returns a string, not a Title
var n=this.value.indexOf(':');
if (n<0) { return this.value; }
var namespaceId = this.namespaceId();
if (namespaceId === pg.nsMainspaceId) return this.value;
return this.value.substring(n+1);
};
Title.prototype.setUtf=function(value){
if (!value) { this.value=''; return; }
var anch=value.indexOf('#');
if(anch < 0) { this.value=value.split('_').join(' '); this.anchor=''; return; }
this.value=value.substring(0,anch).split('_').join(' ');
this.anchor=value.substring(anch+1);
this.ns=null; // wait until namespace() is called
};
Title.prototype.setUrl=function(urlfrag) {
var anch=urlfrag.indexOf('#');
this.value=safeDecodeURI(urlfrag.substring(0,anch));
this.anchor=this.value.substring(anch+1);
};
Title.prototype.append=function(x){
this.setUtf(this.value + x);
};
Title.prototype.urlString=function(x) {
if(!x) { x={}; }
var v=this.toString(true);
if (!x.omitAnchor && this.anchor) { v+= '#' + this.urlAnchor(); }
if (!x.keepSpaces) { v=v.split(' ').join('_'); }
return encodeURI(v).split('&').join('%26').split('?').join('%3F').split('+').join('%2B');
};
Title.prototype.removeAnchor=function() {
return new Title(this.toString(true));
};
Title.prototype.toUrl=function() {
return pg.wiki.titlebase + this.urlString();
};
function parseParams(url) {
var specialDiff = pg.re.specialdiff.exec(url);
if (specialDiff)
{
var split= specialDiff[1].split('/');
if (split.length==1) return {oldid:split[0], diff: 'prev'};
else if (split.length==2) return {oldid: split[0], diff: split[1]};
}
var ret={};
if (url.indexOf('?')==-1) { return ret; }
url = url.split('#')[0];
var s=url.split('?').slice(1).join();
var t=s.split('&');
for (var i=0; i<t.length; ++i) {
var z=t[i].split('=');
z.push(null);
ret[z[0]]=z[1];
}
//Diff revision with no oldid is interpreted as a diff to the previous revision by MediaWiki
if (ret.diff && typeof(ret.oldid)==='undefined')
{
ret.oldid = "prev";
}
//Documentation seems to say something different, but oldid can also accept prev/next, and Echo is emitting such URLs. Simple fixup during parameter decoding:
if (ret.oldid && (ret.oldid==='prev' || ret.oldid==='next' || ret.oldid==='cur'))
{
var helper = ret.diff;
ret.diff = ret.oldid;
ret.oldid = helper;
}
return ret;
}
// (a) myDecodeURI (first standard decodeURI, then pg.re.urlNoPopup)
// (b) change spaces to underscores
// (c) encodeURI (just the straight one, no pg.re.urlNoPopup)
function myDecodeURI (str) {
var ret;
// FIXME decodeURIComponent??
try { ret=decodeURI(str.toString()); }
catch (summat) { return str; }
for (var i=0; i<pg.misc.decodeExtras.length; ++i) {
var from=pg.misc.decodeExtras[i].from;
var to=pg.misc.decodeExtras[i].to;
ret=ret.split(from).join(to);
}
return ret;
}
function safeDecodeURI(str) { var ret=myDecodeURI(str); return ret || str; }
///////////
// TESTS //
///////////
//<NOLITE>
function isDisambig(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
return ! article.isTalkPage() && pg.re.disambig.test(data);
}
function stubCount(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
var sectStub=0;
var realStub=0;
if (pg.re.stub.test(data)) {
var s=data.parenSplit(pg.re.stub);
for (var i=1; i<s.length; i=i+2) {
if (s[i]) { ++sectStub; }
else { ++realStub; }
}
}
return { real: realStub, sect: sectStub };
}
function isValidImageName(str){ // extend as needed...
return ( str.indexOf('{') == -1 );
}
function isInStrippableNamespace(article) {
// Does the namespace allow subpages
// Note, would be better if we had access to wgNamespacesWithSubpages
return ( article.namespaceId() !== 0 );
}
function isInMainNamespace(article) { return article.namespaceId() === 0; }
function anchorContainsImage(a) {
// iterate over children of anchor a
// see if any are images
if (a === null) { return false; }
var kids=a.childNodes;
for (var i=0; i<kids.length; ++i) { if (kids[i].nodeName=='IMG') { return true; } }
return false;
}
//</NOLITE>
function isPopupLink(a) {
// NB for performance reasons, TOC links generally return true
// they should be stripped out later
if (!markNopopupSpanLinks.done) { markNopopupSpanLinks(); }
if (a.inNopopupSpan) { return false; }
// FIXME is this faster inline?
if (a.onmousedown || a.getAttribute('nopopup')) { return false; }
var h=a.href;
if (h === document.location.href+'#') { return false; }
if (!pg.re.basenames.test(h)) { return false; }
if (!pg.re.urlNoPopup.test(h)) { return true; }
return (
(pg.re.email.test(h) || pg.re.contribs.test(h) || pg.re.backlinks.test(h) || pg.re.specialdiff.test(h)) &&
h.indexOf('&limit=') == -1 );
}
function markNopopupSpanLinks() {
if( !getValueOf('popupOnlyArticleLinks'))
fixVectorMenuPopups();
var s = $('.nopopups').toArray();
for (var i=0; i<s.length; ++i) {
var as=s[i].getElementsByTagName('a');
for (var j=0; j<as.length; ++j) {
as[j].inNopopupSpan=true;
}
}
markNopopupSpanLinks.done=true;
}
function fixVectorMenuPopups() {
$('div.vectorMenu h3:first a:first').prop('inNopopupSpan', true);
}
// ENDFILE: titles.js
// STARTFILE: getpage.js
//////////////////////////////////////////////////
// Wiki-specific downloading
//
// Schematic for a getWiki call
//
// getPageWithCaching
// |
// false | true
// getPage<-[findPictureInCache]->-onComplete(a fake download)
// \.
// (async)->addPageToCache(download)->-onComplete(download)
// check cache to see if page exists
function getPageWithCaching(url, onComplete, owner) {
log('getPageWithCaching, url='+url);
var i=findInPageCache(url);
var d;
if (i > -1) {
d=fakeDownload(url, owner.idNumber, onComplete,
pg.cache.pages[i].data, pg.cache.pages[i].lastModified,
owner);
} else {
d=getPage(url, onComplete, owner);
if (d && owner && owner.addDownload) {
owner.addDownload(d);
d.owner=owner;
}
}
}
function getPage(url, onComplete, owner) {
log('getPage');
var callback= function (d) { if (!d.aborted) {addPageToCache(d); onComplete(d);} };
return startDownload(url, owner.idNumber, callback);
}
function findInPageCache(url) {
for (var i=0; i<pg.cache.pages.length; ++i) {
if (url==pg.cache.pages[i].url) { return i; }
}
return -1;
}
function addPageToCache(download) {
log('addPageToCache '+download.url);
var page = {url: download.url, data: download.data, lastModified: download.lastModified};
return pg.cache.pages.push(page);
}
// ENDFILE: getpage.js
// STARTFILE: parensplit.js
//////////////////////////////////////////////////
// parenSplit
// String.prototype.parenSplit should do what ECMAscript says String.prototype.split does,
// interspersing paren matches (regex capturing groups) between the split elements.
// i.e. 'abc'.split(/(b)/)) should return ['a','b','c'], not ['a','c']
if (String('abc'.split(/(b)/))!='a,b,c') {
// broken String.split, e.g. konq, IE < 10
String.prototype.parenSplit=function (re) {
re=nonGlobalRegex(re);
var s=this;
var m=re.exec(s);
var ret=[];
while (m && s) {
// without the following loop, we have
// 'ab'.parenSplit(/a|(b)/) != 'ab'.split(/a|(b)/)
for(var i=0; i<m.length; ++i) {
if (typeof m[i]=='undefined') m[i]='';
}
ret.push(s.substring(0,m.index));
ret = ret.concat(m.slice(1));
s=s.substring(m.index + m[0].length);
m=re.exec(s);
}
ret.push(s);
return ret;
};
} else {
String.prototype.parenSplit=function (re) { return this.split(re); };
String.prototype.parenSplit.isNative=true;
}
function nonGlobalRegex(re) {
var s=re.toString();
var flags='';
for (var j=s.length; s.charAt(j) != '/'; --j) {
if (s.charAt(j) != 'g') { flags += s.charAt(j); }
}
var t=s.substring(1,j);
return RegExp(t,flags);
}
// ENDFILE: parensplit.js
// STARTFILE: tools.js
// IE madness with encoding
// ========================
//
// suppose throughout that the page is in utf8, like wikipedia
//
// if a is an anchor DOM element and a.href should consist of
//
// http://host.name.here/wiki/foo?bar=baz
//
// then IE gives foo as "latin1-encoded" utf8; we have foo = decode_utf8(decodeURI(foo_ie))
// but IE gives bar=baz correctly as plain utf8
//
// ---------------------------------
//
// IE's xmlhttp doesn't understand utf8 urls. Have to use encodeURI here.
//
// ---------------------------------
//
// summat else
// Source: http://aktuell.de.selfhtml.org/artikel/javascript/utf8b64/utf8.htm
//<NOLITE>
function getJsObj(json) {
try {
var json_ret = JSON.parse(json);
if( json_ret.warnings ) {
for( var w=0; w < json_ret.warnings.length; w++ ) {
if( json_ret.warnings[w]['*'] ) {
log( json_ret.warnings[w]['*'] );
} else {
log( json_ret.warnings[w]['warnings'] );
}
}
} else if ( json_ret.error ) {
errlog( json_ret.error.code + ': ' + json_ret.error.info );
}
return json_ret;
} catch (someError) {
errlog('Something went wrong with getJsObj, json='+json);
return 1;
}
}
function anyChild(obj) {
for (var p in obj) {
return obj[p];
}
return null;
}
//</NOLITE>
function upcaseFirst(str) {
if (typeof str != typeof '' || str === '') return '';
return str.charAt(0).toUpperCase() + str.substring(1);
}
function findInArray(arr, foo) {
if (!arr || !arr.length) { return -1; }
var len=arr.length;
for (var i=0; i<len; ++i) { if (arr[i]==foo) { return i; } }
return -1;
}
/* eslint-disable no-unused-vars */
function nextOne (array, value) {
// NB if the array has two consecutive entries equal
// then this will loop on successive calls
var i=findInArray(array, value);
if (i<0) { return null; }
return array[i+1];
}
/* eslint-enable no-unused-vars */
function literalizeRegex(str){
return mw.util.escapeRegExp(str);
}
String.prototype.entify=function() {
//var shy='­';
return this.split('&').join('&').split('<').join('<').split('>').join('>'/*+shy*/).split('"').join('"');
};
// Array filter function
function removeNulls(val) { return val !== null; }
function joinPath(list) {
return list.filter(removeNulls).join('/');
}
function simplePrintf(str, subs) {
if (!str || !subs) { return str; }
var ret=[];
var s=str.parenSplit(/(%s|\$[0-9]+)/);
var i=0;
do {
ret.push(s.shift());
if ( !s.length ) { break; }
var cmd=s.shift();
if (cmd == '%s') {
if ( i < subs.length ) { ret.push(subs[i]); } else { ret.push(cmd); }
++i;
} else {
var j=parseInt( cmd.replace('$', ''), 10 ) - 1;
if ( j > -1 && j < subs.length ) { ret.push(subs[j]); } else { ret.push(cmd); }
}
} while (s.length > 0);
return ret.join('');
}
/* eslint-disable no-unused-vars */
function isString(x) { return (typeof x === 'string' || x instanceof String); }
function isNumber(x) { return (typeof x === 'number' || x instanceof Number); }
function isRegExp(x) { return x instanceof RegExp; }
function isArray (x) { return x instanceof Array; }
function isObject(x) { return x instanceof Object; }
function isFunction(x) {
return !isRegExp(x) && (typeof x === 'function' || x instanceof Function);
}
/* eslint-enable no-unused-vars */
function repeatString(s,mult) {
var ret='';
for (var i=0; i<mult; ++i) { ret += s; }
return ret;
}
function zeroFill(s, min) {
min = min || 2;
var t=s.toString();
return repeatString('0', min - t.length) + t;
}
function map(f, o) {
if (isArray(o)) { return map_array(f,o); }
return map_object(f,o);
}
function map_array(f,o) {
var ret=[];
for (var i=0; i<o.length; ++i) {
ret.push(f(o[i]));
}
return ret;
}
function map_object(f,o) {
var ret={};
for (var i in o) { ret[o]=f(o[i]); }
return ret;
}
pg.escapeQuotesHTML = function ( text ) {
return text
.replace(/&/g, "&")
.replace(/"/g, """)
.replace(/</g, "<")
.replace(/>/g, ">");
};
// ENDFILE: tools.js
// STARTFILE: dab.js
//<NOLITE>
//////////////////////////////////////////////////
// Dab-fixing code
//
function retargetDab(newTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) {
log('retargetDab: newTarget='+newTarget + ' oldTarget=' + oldTarget);
return changeLinkTargetLink(
{newTarget: newTarget,
text: newTarget.split(' ').join(' '),
hint: tprintf('disambigHint', [newTarget]),
summary: simplePrintf(
getValueOf('popupFixDabsSummary'), [friendlyCurrentArticleName, newTarget ]),
clickButton: getValueOf('popupDabsAutoClick'), minor: true, oldTarget: oldTarget,
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit});
}
function listLinks(wikitext, oldTarget, titleToEdit) {
// mediawiki strips trailing spaces, so we do the same
// testcase: https://en.wikipedia.org/w/index.php?title=Radial&oldid=97365633
var reg=RegExp('\\[\\[([^|]*?) *(\\||\\]\\])', 'gi');
var ret=[];
var splitted=wikitext.parenSplit(reg);
// ^[a-z]+ should match interwiki links, hopefully (case-insensitive)
// and ^[a-z]* should match those and [[:Category...]] style links too
var omitRegex=RegExp('^[a-z]*:|^[Ss]pecial:|^[Ii]mage|^[Cc]ategory');
var friendlyCurrentArticleName= oldTarget.toString();
var wikPos = getValueOf('popupDabWiktionary');
for (var i=1; i<splitted.length; i=i+3) {
if (typeof splitted[i] == typeof 'string' && splitted[i].length>0 && !omitRegex.test(splitted[i])) {
ret.push( retargetDab(splitted[i], oldTarget, friendlyCurrentArticleName, titleToEdit) );
} /* if */
} /* for loop */
ret = rmDupesFromSortedList(ret.sort());
if (wikPos) {
var wikTarget='wiktionary:' +
friendlyCurrentArticleName.replace( RegExp('^(.+)\\s+[(][^)]+[)]\\s*$'), '$1' );
var meth;
if (wikPos.toLowerCase() == 'first') { meth = 'unshift'; }
else { meth = 'push'; }
ret[meth]( retargetDab(wikTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) );
}
ret.push(changeLinkTargetLink(
{ newTarget: null,
text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this disambig page from this article"),
clickButton: getValueOf('popupDabsAutoClick'), oldTarget: oldTarget,
summary: simplePrintf(getValueOf('popupRmDabLinkSummary'), [friendlyCurrentArticleName]),
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit
}));
return ret;
}
function rmDupesFromSortedList(list) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (ret.length === 0 || list[i]!=ret[ret.length-1]) { ret.push(list[i]); }
}
return ret;
}
function makeFixDab(data, navpop) {
// grab title from parent popup if there is one; default exists in changeLinkTargetLink
var titleToEdit=(navpop.parentPopup && navpop.parentPopup.article.toString());
var list=listLinks(data, navpop.originalArticle, titleToEdit);
if (list.length === 0) { log('listLinks returned empty list'); return null; }
var html='<hr />' + popupString('Click to disambiguate this link to:') + '<br>';
html+=list.join(', ');
return html;
}
function makeFixDabs(wikiText, navpop) {
if (getValueOf('popupFixDabs') && isDisambig(wikiText, navpop.article) &&
Title.fromURL(location.href).namespaceId() != pg.nsSpecialId &&
navpop.article.talkPage() ) {
setPopupHTML(makeFixDab(wikiText, navpop), 'popupFixDab', navpop.idNumber);
}
}
function popupRedlinkHTML(article) {
return changeLinkTargetLink(
{ newTarget: null, text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this page from this article"),
clickButton: getValueOf('popupRedlinkAutoClick'),
oldTarget: article.toString(),
summary: simplePrintf(getValueOf('popupRedlinkSummary'), [article.toString()])});
}
//</NOLITE>
// ENDFILE: dab.js
// STARTFILE: htmloutput.js
// this has to use a timer loop as we don't know if the DOM element exists when we want to set the text
function setPopupHTML (str, elementId, popupId, onSuccess, append) {
if (typeof popupId === 'undefined') {
//console.error('popupId is not defined in setPopupHTML, html='+str.substring(0,100));
popupId = pg.idNumber;
}
var popupElement=document.getElementById(elementId+popupId);
if (popupElement) {
if (!append) { popupElement.innerHTML=''; }
if (isString(str)) {
popupElement.innerHTML+=str;
} else {
popupElement.appendChild(str);
}
if (onSuccess) { onSuccess(); }
setTimeout(checkPopupPosition, 100);
return true;
} else {
// call this function again in a little while...
setTimeout(function(){
setPopupHTML(str,elementId,popupId,onSuccess);
}, 600);
}
return null;
}
//<NOLITE>
function setPopupTrailer(str,id) {return setPopupHTML(str, 'popupData', id);}
//</NOLITE>
// args.navpopup is mandatory
// optional: args.redir, args.redirTarget
// FIXME: ye gods, this is ugly stuff
function fillEmptySpans(args) {
// if redir is present and true then redirTarget is mandatory
var redir=true;
var rcid;
if (typeof args != 'object' || typeof args.redir == 'undefined' || !args.redir) { redir=false; }
var a=args.navpopup.parentAnchor;
var article, hint=null, oldid=null, params={};
if (redir && typeof args.redirTarget == typeof {}) {
article=args.redirTarget;
//hint=article.hintValue();
} else {
article=(new Title()).fromAnchor(a);
hint=a.originalTitle || article.hintValue();
params=parseParams(a.href);
oldid=(getValueOf('popupHistoricalLinks')) ? params.oldid : null;
rcid=params.rcid;
}
var x={ article:article, hint: hint, oldid: oldid, rcid: rcid, navpop:args.navpopup, params:params };
var structure=pg.structures[getValueOf('popupStructure')];
if (typeof structure != 'object') {
setPopupHTML('popupError', 'Unknown structure (this should never happen): '+
pg.option.popupStructure, args.navpopup.idNumber);
return;
}
var spans=flatten(pg.misc.layout);
var numspans = spans.length;
var redirs=pg.misc.redirSpans;
for (var i=0; i<numspans; ++i) {
var found = redirs && (redirs.indexOf( spans[i] ) !== -1);
//log('redir='+redir+', found='+found+', spans[i]='+spans[i]);
if ( (found && !redir) || (!found && redir) ) {
//log('skipping this set of the loop');
continue;
}
var structurefn=structure[spans[i]];
var setfn = setPopupHTML;
if (getValueOf('popupActiveNavlinks') &&
(spans[i].indexOf('popupTopLinks')===0 || spans[i].indexOf('popupRedirTopLinks')===0)
) {
setfn = setPopupTipsAndHTML;
}
switch (typeof structurefn) {
case 'function':
log('running '+spans[i]+'({article:'+x.article+', hint:'+x.hint+', oldid: '+x.oldid+'})');
setfn(structurefn(x), spans[i], args.navpopup.idNumber);
break;
case 'string':
setfn(structurefn, spans[i], args.navpopup.idNumber);
break;
default:
errlog('unknown thing with label '+spans[i] + ' (span index was ' + i + ')');
break;
}
}
}
// flatten an array
function flatten(list, start) {
var ret=[];
if (typeof start == 'undefined') { start=0; }
for (var i=start; i<list.length; ++i) {
if (typeof list[i] == typeof []) {
return ret.concat(flatten(list[i])).concat(flatten(list, i+1));
}
else { ret.push(list[i]); }
}
return ret;
}
// Generate html for whole popup
function popupHTML (a) {
getValueOf('popupStructure');
var structure=pg.structures[pg.option.popupStructure];
if (typeof structure != 'object') {
//return 'Unknown structure: '+pg.option.popupStructure;
// override user choice
pg.option.popupStructure=pg.optionDefault.popupStructure;
return popupHTML(a);
}
if (typeof structure.popupLayout != 'function') { return 'Bad layout'; }
pg.misc.layout=structure.popupLayout();
if (typeof structure.popupRedirSpans === 'function') { pg.misc.redirSpans=structure.popupRedirSpans(); }
else { pg.misc.redirSpans=[]; }
return makeEmptySpans(pg.misc.layout, a.navpopup);
}
function makeEmptySpans (list, navpop) {
var ret='';
for (var i=0; i<list.length; ++i) {
if (typeof list[i] == typeof '') {
ret += emptySpanHTML(list[i], navpop.idNumber, 'div');
} else if (typeof list[i] == typeof [] && list[i].length > 0 ) {
ret = ret.parenSplit(RegExp('(</[^>]*?>$)')).join(makeEmptySpans(list[i], navpop));
} else if (typeof list[i] == typeof {} && list[i].nodeType ) {
ret += emptySpanHTML(list[i].name, navpop.idNumber, list[i].nodeType);
}
}
return ret;
}
function emptySpanHTML(name, id, tag, classname) {
tag = tag || 'span';
if (!classname) { classname = emptySpanHTML.classAliases[name]; }
classname = classname || name;
if (name == getValueOf('popupDragHandle')) { classname += ' popupDragHandle'; }
return simplePrintf('<%s id="%s" class="%s"></%s>', [tag, name + id, classname, tag]);
}
emptySpanHTML.classAliases={ 'popupSecondPreview': 'popupPreview' };
// generate html for popup image
// <a id="popupImageLinkn"><img id="popupImagen">
// where n=idNumber
function imageHTML(article, idNumber) {
return simplePrintf('<a id="popupImageLink$1">' +
'<img align="right" valign="top" id="popupImg$1" style="display: none;"></img>' +
'</a>', [ idNumber ]);
}
function popTipsSoonFn(id, when, popData) {
if (!when) { when=250; }
var popTips=function(){ setupTooltips(document.getElementById(id), false, true, popData); };
return function() { setTimeout( popTips, when, popData ); };
}
function setPopupTipsAndHTML(html, divname, idnumber, popData) {
setPopupHTML(html, divname, idnumber,
getValueOf('popupSubpopups') ?
popTipsSoonFn(divname + idnumber, null, popData) :
null);
}
// ENDFILE: htmloutput.js
// STARTFILE: mouseout.js
//////////////////////////////////////////////////
// fuzzy checks
function fuzzyCursorOffMenus(x,y, fuzz, parent) {
if (!parent) { return null; }
var uls=parent.getElementsByTagName('ul');
for (var i=0; i<uls.length; ++i) {
if (uls[i].className=='popup_menu') {
if (uls[i].offsetWidth > 0) return false;
} // else {document.title+='.';}
}
return true;
}
function checkPopupPosition () { // stop the popup running off the right of the screen
// FIXME avoid pg.current.link
if (pg.current.link && pg.current.link.navpopup)
pg.current.link.navpopup.limitHorizontalPosition();
}
function mouseOutWikiLink () {
//console ('mouseOutWikiLink');
var a=this;
removeModifierKeyHandler(a);
if (a.navpopup === null || typeof a.navpopup === 'undefined') return;
if ( ! a.navpopup.isVisible() ) {
a.navpopup.banish();
return;
}
restoreTitle(a);
Navpopup.tracker.addHook(posCheckerHook(a.navpopup));
}
function posCheckerHook(navpop) {
return function() {
if (!navpop.isVisible()) { return true; /* remove this hook */ }
if (Navpopup.tracker.dirty) {
return false;
}
var x=Navpopup.tracker.x, y=Navpopup.tracker.y;
var mouseOverNavpop = navpop.isWithin(x,y,navpop.fuzz, navpop.mainDiv) ||
!fuzzyCursorOffMenus(x,y,navpop.fuzz, navpop.mainDiv);
// FIXME it'd be prettier to do this internal to the Navpopup objects
var t=getValueOf('popupHideDelay');
if (t) { t = t * 1000; }
if (!t) {
if(!mouseOverNavpop) {
if(navpop.parentAnchor) {
restoreTitle( navpop.parentAnchor );
}
navpop.banish();
return true; /* remove this hook */
}
return false;
}
// we have a hide delay set
var d=+(new Date());
if ( !navpop.mouseLeavingTime ) {
navpop.mouseLeavingTime = d;
return false;
}
if ( mouseOverNavpop ) {
navpop.mouseLeavingTime=null;
return false;
}
if (d - navpop.mouseLeavingTime > t) {
navpop.mouseLeavingTime=null;
navpop.banish(); return true; /* remove this hook */
}
return false;
};
}
function runStopPopupTimer(navpop) {
// at this point, we should have left the link but remain within the popup
// so we call this function again until we leave the popup.
if (!navpop.stopPopupTimer) {
navpop.stopPopupTimer=setInterval(posCheckerHook(navpop), 500);
navpop.addHook(function(){clearInterval(navpop.stopPopupTimer);},
'hide', 'before');
}
}
// ENDFILE: mouseout.js
// STARTFILE: previewmaker.js
/**
@fileoverview
Defines the {@link Previewmaker} object, which generates short previews from wiki markup.
*/
/**
Creates a new Previewmaker
@constructor
@class The Previewmaker class. Use an instance of this to generate short previews from Wikitext.
@param {String} wikiText The Wikitext source of the page we wish to preview.
@param {String} baseUrl The url we should prepend when creating relative urls.
@param {Navpopup} owner The navpop associated to this preview generator
*/
function Previewmaker(wikiText, baseUrl, owner) {
/** The wikitext which is manipulated to generate the preview. */
this.originalData=wikiText;
this.baseUrl=baseUrl;
this.owner=owner;
this.maxCharacters=getValueOf('popupMaxPreviewCharacters');
this.maxSentences=getValueOf('popupMaxPreviewSentences');
this.setData();
}
Previewmaker.prototype.setData=function() {
var maxSize=Math.max(10000, 2*this.maxCharacters);
this.data=this.originalData.substring(0,maxSize);
};
/** Remove HTML comments
@private
*/
Previewmaker.prototype.killComments = function () {
// this also kills one trailing newline, eg [[diamyo]]
this.data=this.data.replace(RegExp('^<!--[^$]*?-->\\n|\\n<!--[^$]*?-->(?=\\n)|<!--[^$]*?-->', 'g'), '');
};
/**
@private
*/
Previewmaker.prototype.killDivs = function () {
// say goodbye, divs (can be nested, so use * not *?)
this.data=this.data.replace(RegExp('< *div[^>]* *>[\\s\\S]*?< */ *div *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killGalleries = function () {
this.data=this.data.replace(RegExp('< *gallery[^>]* *>[\\s\\S]*?< */ *gallery *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.kill = function(opening, closing, subopening, subclosing, repl) {
var oldk=this.data;
var k=this.killStuff(this.data, opening, closing, subopening, subclosing, repl);
while (k.length < oldk.length) {
oldk=k;
k=this.killStuff(k, opening, closing, subopening, subclosing, repl);
}
this.data=k;
};
/**
@private
*/
Previewmaker.prototype.killStuff = function (txt, opening, closing, subopening, subclosing, repl) {
var op=this.makeRegexp(opening);
var cl=this.makeRegexp(closing, '^');
var sb=subopening ? this.makeRegexp(subopening, '^') : null;
var sc=subclosing ? this.makeRegexp(subclosing, '^') : cl;
if (!op || !cl) {
alert('Navigation Popups error: op or cl is null! something is wrong.');
return;
}
if (!op.test(txt)) { return txt; }
var ret='';
var opResult = op.exec(txt);
ret = txt.substring(0,opResult.index);
txt=txt.substring(opResult.index+opResult[0].length);
var depth = 1;
while (txt.length > 0) {
var removal=0;
if (depth==1 && cl.test(txt)) {
depth--;
removal=cl.exec(txt)[0].length;
} else if (depth > 1 && sc.test(txt)) {
depth--;
removal=sc.exec(txt)[0].length;
}else if (sb && sb.test(txt)) {
depth++;
removal=sb.exec(txt)[0].length;
}
if ( !removal ) { removal = 1; }
txt=txt.substring(removal);
if (depth === 0) { break; }
}
return ret + (repl || '') + txt;
};
/**
@private
*/
Previewmaker.prototype.makeRegexp = function (x, prefix, suffix) {
prefix = prefix || '';
suffix = suffix || '';
var reStr='';
var flags='';
if (isString(x)) {
reStr=prefix + literalizeRegex(x) + suffix;
} else if (isRegExp(x)) {
var s=x.toString().substring(1);
var sp=s.split('/');
flags=sp[sp.length-1];
sp[sp.length-1]='';
s=sp.join('/');
s=s.substring(0,s.length-1);
reStr= prefix + s + suffix;
} else {
log ('makeRegexp failed');
}
log ('makeRegexp: got reStr=' + reStr + ', flags=' + flags);
return RegExp(reStr, flags);
};
/**
@private
*/
Previewmaker.prototype.killBoxTemplates = function () {
// taxobox removal... in fact, there's a saudiprincebox_begin, so let's be more general
// also, have float_begin, ... float_end
this.kill(RegExp('[{][{][^{}\\s|]*?(float|box)[_ ](begin|start)', 'i'), /[}][}]\s*/, '{{');
// infoboxes etc
// from User:Zyxw/popups.js: kill frames too
this.kill(RegExp('[{][{][^{}\\s|]*?(infobox|elementbox|frame)[_ ]', 'i'), /[}][}]\s*/, '{{');
};
/**
@private
*/
Previewmaker.prototype.killTemplates = function () {
// the plus sign is to avoid transcluding [[Template:', ']]
this.kill('{'+'{', '}'+'}', '{', '}', ' ');
};
/**
@private
*/
Previewmaker.prototype.killTables = function () {
// tables are bad, too
// this can be slow, but it's an inprovement over a browser hang
// torture test: [[Comparison_of_Intel_Central_Processing_Units]]
this.kill('{|', /[|]}\s*/, '{|');
this.kill(/<table.*?>/i, /<\/table.*?>/i, /<table.*?>/i);
// remove lines starting with a pipe for the hell of it (?)
this.data=this.data.replace(RegExp('^[|].*$', 'mg'), '');
};
/**
@private
*/
Previewmaker.prototype.killImages = function () {
var forbiddenNamespaceAliases = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=pg.nsImageId && _namespaceId!=pg.nsCategoryId) return;
forbiddenNamespaceAliases.push(_localizedNamespaceLc.split(' ').join('[ _]')); //todo: escape regexp fragments!
});
// images and categories are a nono
this.kill(RegExp('[[][[]\\s*(' + forbiddenNamespaceAliases.join('|') + ')\\s*:', 'i'),
/\]\]\s*/, '[', ']');
};
/**
@private
*/
Previewmaker.prototype.killHTML = function () {
// kill <ref ...>...</ref>
this.kill(/<ref\b[^/>]*?>/i, /<\/ref>/i);
// let's also delete entire lines starting with <. it's worth a try.
this.data=this.data.replace(RegExp('(^|\\n) *<.*', 'g'), '\n');
// and those pesky html tags, but not <nowiki> or <blockquote>
var splitted=this.data.parenSplit(/(<[\w\W]*?(?:>|$|(?=<)))/);
var len=splitted.length;
for (var i=1; i<len; i=i+2) {
switch (splitted[i]) {
case '<nowiki>':
case '</nowiki>':
case '<blockquote>':
case '</blockquote>':
break;
default:
splitted[i]='';
}
}
this.data=splitted.join('');
};
/**
@private
*/
Previewmaker.prototype.killChunks = function() { // heuristics alert
// chunks of italic text? you crazy, man?
var italicChunkRegex=new RegExp
("((^|\\n)\\s*:*\\s*''[^']([^']|'''|'[^']){20}(.|\\n[^\\n])*''[.!?\\s]*\\n)+", 'g');
// keep stuff separated, though, so stick in \n (fixes [[Union Jack]]?
this.data=this.data.replace(italicChunkRegex, '\n');
};
/**
@private
*/
Previewmaker.prototype.mopup = function () {
// we simply *can't* be doing with horizontal rules right now
this.data=this.data.replace(RegExp('^-{4,}','mg'),'');
// no indented lines
this.data=this.data.replace(RegExp('(^|\\n) *:[^\\n]*','g'), '');
// replace __TOC__, __NOTOC__ and whatever else there is
// this'll probably do
this.data=this.data.replace(RegExp('^__[A-Z_]*__ *$', 'gmi'),'');
};
/**
@private
*/
Previewmaker.prototype.firstBit = function () {
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d=this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data=this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data=this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data=this.data.replace(RegExp('^[\\s\\n]*'), '');
var stuff=(RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) { d = stuff[0]; }
if (!getValueOf('popupPreviewFirstParOnly')) { d = this.data; }
/// now put \n\n after sections so that bullets and numbered lists work
d=d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// Split sentences. Superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["'+"'"+']*\\s)','g'));
// leading space is bad, mmkay?
d[0]=d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds=RegExp('([^.][a-z][.] *[a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|op|cit|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
this.fullLength=d.join('').length;
var n=this.maxSentences;
var dd=this.firstSentences(d,n);
do {
dd=this.firstSentences(d,n); --n;
} while ( dd.length > this.maxCharacters && n !== 0 );
this.data = dd;
};
/**
@private
*/
Previewmaker.prototype.fixSentenceEnds = function(strs, reg) {
// take an array of strings, strs
// join strs[i] to strs[i+1] & strs[i+2] if strs[i] matches regex reg
for (var i=0; i<strs.length-2; ++i) {
if (reg.test(strs[i])) {
var a=[];
for (var j=0; j<strs.length; ++j) {
if (j<i) a[j]=strs[j];
if (j==i) a[i]=strs[i]+strs[i+1]+strs[i+2];
if (j>i+2) a[j-2]=strs[j];
}
return this.fixSentenceEnds(a,reg);
}
}
return strs;
};
/**
@private
*/
Previewmaker.prototype.firstSentences = function(strs, howmany) {
var t=strs.slice(0, 2*howmany);
return t.join('');
};
/**
@private
*/
Previewmaker.prototype.killBadWhitespace = function() {
// also cleans up isolated '''', eg [[Suntory Sungoliath]]
this.data=this.data.replace(RegExp('^ *\'+ *$', 'gm'), '');
};
/**
Runs the various methods to generate the preview.
The preview is stored in the <code>html</html> field.
@private
*/
Previewmaker.prototype.makePreview = function() {
if (this.owner.article.namespaceId()!=pg.nsTemplateId &&
this.owner.article.namespaceId()!=pg.nsImageId ) {
this.killComments();
this.killDivs();
this.killGalleries();
this.killBoxTemplates();
if (getValueOf('popupPreviewKillTemplates')) {
this.killTemplates();
} else {
this.killMultilineTemplates();
}
this.killTables();
this.killImages();
this.killHTML();
this.killChunks();
this.mopup();
this.firstBit();
this.killBadWhitespace();
}
else
{
this.killHTML();
}
this.html=wiki2html(this.data, this.baseUrl); // needs livepreview
this.fixHTML();
this.stripLongTemplates();
};
/**
@private
*/
Previewmaker.prototype.esWiki2HtmlPart = function(data) {
var reLinks = /(?:\[\[([^|\]]*)(?:\|([^|\]]*))*]]([a-z]*))/gi; //match a wikilink
reLinks.lastIndex = 0; //reset regex
var match;
var result = "";
var postfixIndex = 0;
while ((match = reLinks.exec(data))) //match all wikilinks
{
//FIXME: the way that link is built here isn't perfect. It is clickable, but popups preview won't recognize it in some cases.
result += pg.escapeQuotesHTML(data.substring(postfixIndex, match.index)) +
'<a href="'+Insta.conf.paths.articles+pg.escapeQuotesHTML(match[1])+'">'+pg.escapeQuotesHTML((match[2]?match[2]:match[1])+match[3])+"</a>";
postfixIndex = reLinks.lastIndex;
}
//append the rest
result += pg.escapeQuotesHTML(data.substring(postfixIndex));
return result;
};
Previewmaker.prototype.editSummaryPreview=function() {
var reAes = /\/\* *(.*?) *\*\//g; //match the first section marker
reAes.lastIndex = 0; //reset regex
var match;
match = reAes.exec(this.data);
if (match)
{
//we have a section link. Split it, process it, combine it.
var prefix = this.data.substring(0,match.index-1);
var section = match[1];
var postfix = this.data.substring(reAes.lastIndex);
var start = "<span class='autocomment'>";
var end = "</span>";
if (prefix.length>0) start = this.esWiki2HtmlPart(prefix) + " " + start + "- ";
if (postfix.length>0) end = ": " + end + this.esWiki2HtmlPart(postfix);
var t=new Title().fromURL(this.baseUrl);
t.anchorFromUtf(section);
var sectionLink = Insta.conf.paths.articles + pg.escapeQuotesHTML(t.toString(true)) + '#' + pg.escapeQuotesHTML(t.anchor);
return start + '<a href="'+sectionLink+'">→</a> '+pg.escapeQuotesHTML(section) + end;
}
//else there's no section link, htmlify the whole thing.
return this.esWiki2HtmlPart(this.data);
};
//<NOLITE>
/** Test function for debugging preview problems one step at a time.
*/
/*eslint-disable */
function previewSteps(txt) {
try {
txt=txt || document.editform.wpTextbox1.value;
} catch (err) {
if (pg.cache.pages.length > 0) {
txt=pg.cache.pages[pg.cache.pages.length-1].data;
} else {
alert('provide text or use an edit page');
}
}
txt=txt.substring(0,10000);
var base=pg.wiki.articlebase + Title.fromURL(document.location.href).urlString();
var p=new Previewmaker(txt, base, pg.current.link.navpopup);
if (this.owner.article.namespaceId() != pg.nsTemplateId) {
p.killComments(); if (!confirm('done killComments(). Continue?\n---\n' + p.data)) { return; }
p.killDivs(); if (!confirm('done killDivs(). Continue?\n---\n' + p.data)) { return; }
p.killGalleries(); if (!confirm('done killGalleries(). Continue?\n---\n' + p.data)) { return; }
p.killBoxTemplates(); if (!confirm('done killBoxTemplates(). Continue?\n---\n' + p.data)) { return; }
if (getValueOf('popupPreviewKillTemplates')) {
p.killTemplates(); if (!confirm('done killTemplates(). Continue?\n---\n' + p.data)) { return; }
} else {
p.killMultilineTemplates(); if (!confirm('done killMultilineTemplates(). Continue?\n---\n' + p.data)) { return; }
}
p.killTables(); if (!confirm('done killTables(). Continue?\n---\n' + p.data)) { return; }
p.killImages(); if (!confirm('done killImages(). Continue?\n---\n' + p.data)) { return; }
p.killHTML(); if (!confirm('done killHTML(). Continue?\n---\n' + p.data)) { return; }
p.killChunks(); if (!confirm('done killChunks(). Continue?\n---\n' + p.data)) { return; }
p.mopup(); if (!confirm('done mopup(). Continue?\n---\n' + p.data)) { return; }
p.firstBit(); if (!confirm('done firstBit(). Continue?\n---\n' + p.data)) { return; }
p.killBadWhitespace(); if (!confirm('done killBadWhitespace(). Continue?\n---\n' + p.data)) { return; }
}
p.html=wiki2html(p.data, base); // needs livepreview
p.fixHTML(); if (!confirm('done fixHTML(). Continue?\n---\n' + p.html)) { return; }
p.stripLongTemplates(); if (!confirm('done stripLongTemplates(). Continue?\n---\n' + p.html)) { return; }
alert('finished preview - end result follows.\n---\n' + p.html);
}
/*eslint-enable */
//</NOLITE>
/**
Works around livepreview bugs.
@private
*/
Previewmaker.prototype.fixHTML = function() {
if(!this.html) return;
var ret = this.html;
// fix question marks in wiki links
// maybe this'll break some stuff :-(
ret=ret.replace(RegExp('(<a href="' + pg.wiki.articlePath + '/[^"]*)[?](.*?")', 'g'), '$1%3F$2');
ret=ret.replace(RegExp('(<a href=\'' + pg.wiki.articlePath + '/[^\']*)[?](.*?\')', 'g'), '$1%3F$2');
// FIXME fix up % too
this.html=ret;
};
/**
Generates the preview and displays it in the current popup.
Does nothing if the generated preview is invalid or consists of whitespace only.
Also activates wikilinks in the preview for subpopups if the popupSubpopups option is true.
*/
Previewmaker.prototype.showPreview = function () {
this.makePreview();
if (typeof this.html != typeof '') return;
if (RegExp('^\\s*$').test(this.html)) return;
setPopupHTML('<hr />', 'popupPrePreviewSep', this.owner.idNumber);
setPopupTipsAndHTML(this.html, 'popupPreview', this.owner.idNumber, { owner: this.owner });
var more = (this.fullLength > this.data.length) ? this.moreLink() : '';
setPopupHTML(more, 'popupPreviewMore', this.owner.idNumber);
};
/**
@private
*/
Previewmaker.prototype.moreLink=function() {
var a=document.createElement('a');
a.className='popupMoreLink';
a.innerHTML=popupString('more...');
var savedThis=this;
a.onclick=function() {
savedThis.maxCharacters+=2000;
savedThis.maxSentences+=20;
savedThis.setData();
savedThis.showPreview();
};
return a;
};
/**
@private
*/
Previewmaker.prototype.stripLongTemplates = function() {
// operates on the HTML!
this.html=this.html.replace(RegExp('^.{0,1000}[{][{][^}]*?(<(p|br)( /)?>\\s*){2,}([^{}]*?[}][}])?', 'gi'), '');
this.html=this.html.split('\n').join(' '); // workaround for <pre> templates
this.html=this.html.replace(RegExp('[{][{][^}]*<pre>[^}]*[}][}]','gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killMultilineTemplates = function() {
this.kill('{{{', '}}}');
this.kill(RegExp('\\s*[{][{][^{}]*\\n'), '}}', '{{');
};
// ENDFILE: previewmaker.js
// STARTFILE: querypreview.js
function loadAPIPreview(queryType, article, navpop) {
var art=new Title(article).urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
var htmlGenerator=function(/*a, d*/){alert('invalid html generator');};
var usernameart = '';
switch (queryType) {
case 'history':
url += 'titles=' + art + '&prop=revisions&rvlimit=' +
getValueOf('popupHistoryPreviewLimit');
htmlGenerator=APIhistoryPreviewHTML;
break;
case 'category':
url += 'list=categorymembers&cmtitle=' + art;
htmlGenerator=APIcategoryPreviewHTML;
break;
case 'userinfo':
var username = new Title( article ).userName();
usernameart = encodeURIComponent( username );
if (pg.re.ipUser.test(username)) {
url += 'list=blocks&bkprop=range|restrictions&bkip=' + usernameart;
} else {
url += 'list=users|usercontribs&usprop=blockinfo|groups|editcount|registration|gender&ususers=' + usernameart + "&meta=globaluserinfo&guiprop=groups|unattached&guiuser="+ usernameart + "&uclimit=1&ucprop=timestamp&ucuser=" + usernameart;
}
htmlGenerator=APIuserInfoPreviewHTML;
break;
case 'contribs':
usernameart = encodeURIComponent( new Title( article ).userName() );
url += 'list=usercontribs&ucuser=' + usernameart +
'&uclimit=' + getValueOf('popupContribsPreviewLimit');
htmlGenerator=APIcontribsPreviewHTML;
break;
case 'imagepagepreview':
var trail='';
if (getValueOf('popupImageLinks')) { trail = '&list=imageusage&iutitle=' + art; }
url += 'titles=' + art + '&prop=revisions|imageinfo&rvprop=content' + trail;
htmlGenerator=APIimagepagePreviewHTML;
break;
case 'backlinks':
url += 'list=backlinks&bltitle=' + art;
htmlGenerator=APIbacklinksPreviewHTML;
break;
case 'revision':
if (article.oldid) {
url += 'revids=' + article.oldid;
} else {
url += 'titles=' + article.removeAnchor().urlString();
}
url += '&prop=revisions|pageprops|info|images|categories&rvprop=ids|timestamp|flags|comment|user|content&cllimit=max&imlimit=max';
htmlGenerator=APIrevisionPreviewHTML;
break;
}
pendingNavpopTask(navpop);
var callback=function(d){
log( "callback of API functions was hit" );
showAPIPreview(queryType, htmlGenerator(article,d,navpop), navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_'+queryType+'_QUERY_DATA'); }
}
function linkList(list) {
list.sort(function(x,y) { return (x==y ? 0 : (x<y ? -1 : 1)); });
var buf=[];
for (var i=0; i<list.length; ++i) {
buf.push(wikiLink({article: new Title(list[i]),
text: list[i].split(' ').join(' '),
action: 'view'}));
}
return buf.join(', ');
}
function getTimeOffset() {
var tz = mw.user.options.get('timecorrection');
if(tz) {
if( tz.indexOf('|') > -1 ) {
// New format
return parseInt(tz.split('|')[1],10);
} else if ( tz.indexOf(':') > -1 ) {
// Old format
return( parseInt(tz,10)*60 + parseInt(tz.split(':')[1],10) );
}
}
return 0;
}
/*
* Creates a HTML table that's shown in the history and user-contribs popups.
* @param {Object[]} h - a list of revisions, returned from the API
* @param {boolean} reallyContribs - true only if we're displaying user contributions
*/
function editPreviewTable(article, h, reallyContribs, timeOffset) {
var html=['<table>'];
var day=null;
var curart=article;
var page=null;
var makeFirstColumnLinks;
if(reallyContribs) {
// We're showing user contributions, so make (diff | hist) links
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&diff=prev' +
'&oldid=' + currentRevision.revid + '">' + popupString('diff') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&action=history">' +
popupString('hist') + '</a>';
result += ')';
return result;
};
} else {
// It's a regular history page, so make (cur | last) links
var firstRevid = h[0].revid;
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=' + firstRevid + '&oldid=' + currentRevision.revid + '">' + popupString('cur') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=prev&oldid=' + currentRevision.revid + '">' + popupString('last') + '</a>';
result += ')';
return result;
};
}
for (var i=0; i<h.length; ++i) {
if (reallyContribs) {
page = h[i].title;
curart = new Title(page);
}
var minor = h[i].minor ? '<b>m </b>' : '';
var editDate = adjustDate(getDateFromTimestamp(h[i].timestamp), timeOffset);
var thisDay = dayFormat(editDate);
var thisTime = timeFormat(editDate);
if (thisDay == day) {
thisDay = '';
} else {
day = thisDay;
}
if (thisDay) {
html.push( '<tr><td colspan=3><span class="popup_history_date">' +
thisDay+'</span></td></tr>' );
}
html.push('<tr class="popup_history_row_' + ( (i%2) ? 'odd' : 'even') + '">');
html.push('<td>' + makeFirstColumnLinks(h[i]) + '</td>');
html.push('<td>' +
'<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&oldid=' + h[i].revid + '">' + thisTime + '</a></td>');
var col3url='', col3txt='';
if (!reallyContribs) {
var user=h[i].user;
if( !h[i].userhidden ) {
if( pg.re.ipUser.test(user) ) {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Contributions&target=' + new Title(user).urlString();
} else {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(user).urlString();
}
col3txt=pg.escapeQuotesHTML(user);
} else {
col3url=getValueOf('popupRevDelUrl');
col3txt=pg.escapeQuotesHTML( popupString('revdel'));
}
} else {
col3url=pg.wiki.titlebase + curart.urlString();
col3txt=pg.escapeQuotesHTML(page);
}
html.push('<td>' + (reallyContribs ? minor : '') +
'<a href="' + col3url + '">' + col3txt + '</a></td>');
var comment='';
var c=h[i].comment || h[i].content;
if (c) {
comment=new Previewmaker(c, new Title(curart).toUrl()).editSummaryPreview();
} else if ( h[i].commenthidden ) {
comment=popupString('revdel');
}
html.push('<td>' + (!reallyContribs ? minor : '') + comment + '</td>');
html.push('</tr>');
html=[html.join('')];
}
html.push('</table>');
return html.join('');
}
function getDateFromTimestamp(t) {
var s=t.split(/[^0-9]/);
switch(s.length) {
case 0: return null;
case 1: return new Date(s[0]);
case 2: return new Date(s[0], s[1]-1);
case 3: return new Date(s[0], s[1]-1, s[2]);
case 4: return new Date(s[0], s[1]-1, s[2], s[3]);
case 5: return new Date(s[0], s[1]-1, s[2], s[3], s[4]);
case 6: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5]);
default: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5], s[6]);
}
}
function adjustDate(d, offset) {
// offset is in minutes
var o=offset * 60 * 1000;
return new Date( +d + o);
}
function dayFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCFullYear(), editDate.getUTCMonth()+1, editDate.getUTCDate()]).join('-'); }
return map(zeroFill, [editDate.getFullYear(), editDate.getMonth()+1, editDate.getDate()]).join('-');
}
function timeFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCHours(), editDate.getUTCMinutes(), editDate.getUTCSeconds()]).join(':'); }
return map(zeroFill, [editDate.getHours(), editDate.getMinutes(), editDate.getSeconds()]).join(':');
}
function showAPIPreview(queryType, html, id, navpop, download) {
// DJ: done
var target='popupPreview';
completedNavpopTask(navpop);
switch (queryType) {
case 'imagelinks':
case 'category':
target='popupPostPreview'; break;
case 'userinfo':
target='popupUserData'; break;
case 'revision':
insertPreview(download);
return;
}
setPopupTipsAndHTML(html, target, id);
}
function APIrevisionPreviewHTML(article, download) {
try{
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
if( page.missing ) {
// TODO we need to fix this proper later on
download.owner = null;
return;
}
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
download.data = content;
download.lastModified = new Date(page.revisions[0].timestamp);
}
} catch(someError) {
return 'Revision preview failed :(';
}
}
function APIbacklinksPreviewHTML(article, download/*, navpop*/ ) {
try {
var jsObj=getJsObj(download.data);
var list=jsObj.query.backlinks;
var html=[];
if (!list) { return popupString('No backlinks found'); }
for ( var i=0; i < list.length; i++ ) {
var t=new Title(list[i].title);
html.push('<a href="' + pg.wiki.titlebase + t.urlString() + '">' + t + '</a>');
}
html=html.join(', ');
if (jsObj['continue'] && jsObj['continue'].blcontinue) {
html += popupString(' and more');
}
return html;
} catch (someError) {
return 'backlinksPreviewHTML went wonky';
}
}
pg.fn.APIsharedImagePagePreviewHTML = function APIsharedImagePagePreviewHTML(obj) {
log( "APIsharedImagePagePreviewHTML" );
var popupid = obj.requestid;
if( obj.query && obj.query.pages )
{
var page=anyChild(obj.query.pages );
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
/* Not entirely safe, but the best we can do */
var p=new Previewmaker(content, pg.current.link.navpopup.article, pg.current.link.navpopup);
p.makePreview();
setPopupHTML( p.html, "popupSecondPreview", popupid );
}
}
};
function APIimagepagePreviewHTML(article, download, navpop) {
try {
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
var content= (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
var ret='';
var alt='';
try{alt=navpop.parentAnchor.childNodes[0].alt;} catch(e){}
if (alt) {
ret = ret + '<hr /><b>' + popupString('Alt text:') + '</b> ' + pg.escapeQuotesHTML(alt);
}
if (typeof content === 'string') {
var p=prepPreviewmaker(content, article, navpop);
p.makePreview();
if (p.html) { ret += '<hr />' + p.html; }
if (getValueOf('popupSummaryData')) {
var info=getPageInfo(content, download);
log(info);
setPopupTrailer(info, navpop.idNumber);
}
}
if (page && page.imagerepository == "shared" ) {
var art=new Title(article);
var encart = encodeURIComponent( "File:" + art.stripNamespace() );
var shared_url = pg.wiki.apicommonsbase + '?format=json&formatversion=2' +
'&callback=pg.fn.APIsharedImagePagePreviewHTML' +
'&requestid=' + navpop.idNumber +
'&action=query&prop=revisions&rvprop=content&titles=' + encart;
ret = ret +'<hr />' + popupString( 'Image from Commons') +
': <a href="' + pg.wiki.commonsbase + '?title=' + encart + '">' +
popupString( 'Description page') + '</a>';
mw.loader.load( shared_url );
}
showAPIPreview('imagelinks', APIimagelinksPreviewHTML(article,download), navpop.idNumber, download);
return ret;
} catch (someError) {
return 'API imagepage preview failed :(';
}
}
function APIimagelinksPreviewHTML(article, download) {
try {
var jsobj=getJsObj(download.data);
var list=jsobj.query.imageusage;
if (list) {
var ret=[];
for (var i=0; i < list.length; i++) {
ret.push(list[i].title);
}
if (ret.length === 0) { return popupString('No image links found'); }
return '<h2>' + popupString('File links') + '</h2>' + linkList(ret);
} else {
return popupString('No image links found');
}
} catch(someError) {
return 'Image links preview generation failed :(';
}
}
function APIcategoryPreviewHTML(article, download) {
try{
var jsobj=getJsObj(download.data);
var list=jsobj.query.categorymembers;
var ret=[];
for (var p=0; p < list.length; p++) {
ret.push(list[p].title);
}
if (ret.length === 0) { return popupString('Empty category'); }
ret = '<h2>' + tprintf('Category members (%s shown)', [ret.length]) + '</h2>' +linkList(ret);
if (jsobj['continue'] && jsobj['continue'].cmcontinue) {
ret += popupString(' and more');
}
return ret;
} catch(someError) {
return 'Category preview failed :(';
}
}
function APIuserInfoPreviewHTML(article, download) {
var ret=[];
var queryobj = {};
try{
queryobj=getJsObj(download.data).query;
} catch(someError) { return 'Userinfo preview failed :('; }
var user=anyChild(queryobj.users);
if (user) {
var globaluserinfo=queryobj.globaluserinfo;
if (user.invalid === '') {
ret.push( popupString( 'Invalid user') );
} else if (user.missing === '') {
ret.push( popupString( 'Not a registered username') );
}
if( user.blockedby ) {
if( user.blockpartial ) {
ret.push('<b>' + popupString('Has blocks') + '</b>');
} else {
ret.push('<b>' + popupString('CẤM') + '</b>');
}
}
if( globaluserinfo && ( 'locked' in globaluserinfo || 'hidden' in globaluserinfo ) ) {
var lockedSulAccountIsAttachedToThis = true;
for( var i=0; globaluserinfo.unattached && i < globaluserinfo.unattached.length; i++) {
if ( globaluserinfo.unattached[i].wiki === mw.config.get('wgDBname') ) {
lockedSulAccountIsAttachedToThis=false;
break;
}
}
if (lockedSulAccountIsAttachedToThis) {
if ( 'locked' in globaluserinfo ) ret.push('<b><i>' + popupString('LOCKED') + '</i></b>');
if ( 'hidden' in globaluserinfo ) ret.push('<b><i>' + popupString('HIDDEN') + '</i></b>');
}
}
if( getValueOf('popupShowGender') && user.gender ) {
switch( user.gender ) {
case "male": ret.push( popupString( "\u2642" ) ); break;
case "female": ret.push( popupString( "\u2640" ) ); break;
}
}
if( user.groups ) {
for( var j=0; j < user.groups.length; j++) {
var currentGroup = user.groups[j];
if( ["*", "user", "autoconfirmed", "extendedconfirmed"].indexOf( currentGroup ) === -1 ) {
ret.push( pg.escapeQuotesHTML(user.groups[j]) );
}
}
}
if( globaluserinfo && globaluserinfo.groups ) {
for( var k=0; k < globaluserinfo.groups.length; k++) {
ret.push( '<i>'+pg.escapeQuotesHTML(globaluserinfo.groups[k])+'</i>' );
}
}
if( user.registration )
ret.push( pg.escapeQuotesHTML((user.editcount?user.editcount:'0') + popupString(' sửa đổi từ: ') + (user.registration?dayFormat(getDateFromTimestamp(user.registration)):'')) );
}
if (queryobj.usercontribs && queryobj.usercontribs.length) {
ret.push( popupString('sửa đổi lần cuối vào ') + dayFormat(getDateFromTimestamp(queryobj.usercontribs[0].timestamp)) );
}
if (queryobj.blocks) {
ret.push( popupString( 'IP user') ); //we only request list=blocks for IPs
for (var l=0; l<queryobj.blocks.length; l++) {
console.log(queryobj);
var rbstr = queryobj.blocks[l].rangestart === queryobj.blocks[l].rangeend ? 'BLOCK' : 'RANGEBLOCK';
rbstr = (!Array.isArray(queryobj.blocks[l].restrictions) ? 'Has ' + rbstr.toLowerCase() + 's' : rbstr + 'ED');
ret.push('<b>' + popupString(rbstr) + '</b>' );
}
}
ret = '<hr />' + ret.join( ', ' );
return ret;
}
function APIcontribsPreviewHTML(article, download, navpop) {
return APIhistoryPreviewHTML(article, download, navpop, true);
}
function APIhistoryPreviewHTML(article, download, navpop, reallyContribs) {
try {
var jsobj=getJsObj(download.data);
var edits = [];
if( reallyContribs ) {
edits=jsobj.query.usercontribs;
} else {
edits=anyChild(jsobj.query.pages).revisions;
}
var ret=editPreviewTable(article, edits, reallyContribs, getTimeOffset());
return ret;
} catch (someError) {
return 'History preview failed :-(';
}
}
//</NOLITE>
// ENDFILE: querypreview.js
// STARTFILE: debug.js
////////////////////////////////////////////////////////////////////
// Debugging functions
////////////////////////////////////////////////////////////////////
function setupDebugging() {
//<NOLITE>
if (window.popupDebug) { // popupDebug is set from .version
window.log=function(x) { //if(gMsg!='')gMsg += '\n'; gMsg+=time() + ' ' + x; };
window.console.log(x);
};
window.errlog=function(x) {
window.console.error(x);
};
log('Initializing logger');
} else {
//</NOLITE>
window.log = function() {};
window.errlog = function() {};
//<NOLITE>
}
//</NOLITE>
}
// ENDFILE: debug.js
// STARTFILE: images.js
// load image of type Title.
function loadImage(image, navpop) {
if (typeof image.stripNamespace != 'function') { alert('loadImages bad'); }
// API call to retrieve image info.
if ( !getValueOf('popupImages') ) return;
if ( !isValidImageName(image) ) return false;
var art=image.urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query';
url += '&prop=imageinfo&iiprop=url|mime&iiurlwidth=' + getValueOf('popupImageSizeLarge');
url += '&titles=' + art;
pendingNavpopTask(navpop);
var callback=function(d){
popupsInsertImage(navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'after', 'DOWNLOAD_IMAGE_QUERY_DATA'); }
}
function popupsInsertImage(id, navpop, download) {
log( "popupsInsertImage");
var imageinfo;
try {
var jsObj=getJsObj(download.data);
var imagepage=anyChild(jsObj.query.pages);
if (typeof imagepage.imageinfo === 'undefined') return;
imageinfo = imagepage.imageinfo[0];
} catch (someError) {
log( "popupsInsertImage failed :(" );
return;
}
var popupImage = document.getElementById("popupImg"+id);
if (!popupImage) {
log( "could not find insertion point for image");
return;
}
popupImage.width=getValueOf('popupImageSize');
popupImage.style.display='inline';
// Set the source for the image.
if( imageinfo.thumburl )
popupImage.src=imageinfo.thumburl;
else if( imageinfo.mime.indexOf("image") === 0 ){
popupImage.src=imageinfo.url;
log( "a thumb could not be found, using original image" );
} else log( "fullsize imagethumb, but not sure if it's an image");
var a=document.getElementById("popupImageLink"+id);
if (a === null) { return null; }
// Determine the action of the surrouding imagelink.
switch (getValueOf('popupThumbAction')) {
case 'imagepage':
if (pg.current.article.namespaceId()!=pg.nsImageId) {
a.href=imageinfo.descriptionurl;
// FIXME: unreliable pg.idNumber
popTipsSoonFn('popupImage' + id)();
break;
}
/* falls through */
case 'sizetoggle':
a.onclick=toggleSize;
a.title=popupString('Toggle image size');
return;
case 'linkfull':
a.href = imageinfo.url;
a.title=popupString('Open full-size image');
return;
}
}
// Toggles the image between inline small and navpop fullwidth.
// It's the same image, no actual sizechange occurs, only display width.
function toggleSize() {
var imgContainer=this;
if (!imgContainer) {
alert('imgContainer is null :/');
return;
}
var img=imgContainer.firstChild;
if (!img) {
alert('img is null :/');
return;
}
if (!img.style.width || img.style.width==='') {
img.style.width='100%';
} else {
img.style.width='';
}
}
// Returns one title of an image from wikiText.
function getValidImageFromWikiText(wikiText) {
// nb in pg.re.image we're interested in the second bracketed expression
// this may change if the regex changes :-(
//var match=pg.re.image.exec(wikiText);
var matched=null;
var match;
// strip html comments, used by evil bots :-(
var t = removeMatchesUnless(wikiText, RegExp('(<!--[\\s\\S]*?-->)'), 1,
RegExp('^<!--[^[]*popup', 'i'));
while ( ( match = pg.re.image.exec(t) ) ) {
// now find a sane image name - exclude templates by seeking {
var m = match[2] || match[6];
if ( isValidImageName(m) ) {
matched=m;
break;
}
}
pg.re.image.lastIndex=0;
if (!matched) { return null; }
return mw.config.get('wgFormattedNamespaces')[pg.nsImageId]+':'+upcaseFirst(matched);
}
function removeMatchesUnless(str, re1, parencount, re2) {
var split=str.parenSplit(re1);
var c=parencount + 1;
for (var i=0; i<split.length; ++i) {
if ( i%c === 0 || re2.test(split[i]) ) { continue; }
split[i]='';
}
return split.join('');
}
//</NOLITE>
// ENDFILE: images.js
// STARTFILE: namespaces.js
// Set up namespaces and other non-strings.js localization
// (currently that means redirs too)
function setNamespaces() {
pg.nsSpecialId = -1;
pg.nsMainspaceId = 0;
pg.nsImageId = 6;
pg.nsUserId = 2;
pg.nsUsertalkId = 3;
pg.nsCategoryId = 14;
pg.nsTemplateId = 10;
}
function setRedirs() {
var r='redirect';
var R='REDIRECT';
var redirLists={
//<NOLITE>
'ar': [ R, 'تحويل' ],
'be': [ r, 'перанакіраваньне' ],
'bg': [ r, 'пренасочване', 'виж' ],
'bs': [ r, 'Preusmjeri', 'preusmjeri', 'PREUSMJERI' ],
'cs': [ R, 'PŘESMĚRUJ' ],
'cy': [ r, 'ail-cyfeirio' ],
'de': [ R, 'WEITERLEITUNG' ],
'el': [ R, 'ΑΝΑΚΑΤΕΥΘΥΝΣΗ'],
'eo': [ R, 'ALIDIREKTU', 'ALIDIREKTI' ],
'es': [ R, 'REDIRECCIÓN' ],
'et': [ r, 'suuna' ],
'ga': [ r, 'athsheoladh' ],
'gl': [ r, 'REDIRECCIÓN', 'REDIRECIONAMENTO'],
'he': [ R, 'הפניה' ],
'hu': [ R, 'ÁTIRÁNYÍTÁS' ],
'is': [ r, 'tilvísun', 'TILVÍSUN' ],
'it': [ R, 'RINVIA', 'Rinvia'],
'ja': [ R, '転送' ],
'mk': [ r, 'пренасочување', 'види' ],
'nds': [ r, 'wiederleiden' ],
'nl': [ R, 'DOORVERWIJZING' ],
'nn': [ r, 'omdiriger' ],
'pl': [ R, 'PATRZ', 'PRZEKIERUJ', 'TAM' ],
'pt': [ R, 'redir' ],
'ru': [ R, 'ПЕРЕНАПРАВЛЕНИЕ', 'ПЕРЕНАПР' ],
'sk': [ r, 'presmeruj' ],
'sr': [ r, 'Преусмери', 'преусмери', 'ПРЕУСМЕРИ', 'Preusmeri', 'preusmeri', 'PREUSMERI' ],
'tt': [ R, 'yünältü', 'перенаправление', 'перенапр' ],
'uk': [ R, 'ПЕРЕНАПРАВЛЕННЯ', 'ПЕРЕНАПР' ],
'vi': [ r, 'đổi' ],
'zh': [ R, '重定向'] // no comma
//</NOLITE>
};
var redirList=redirLists[ pg.wiki.lang ] || [r, R];
// Mediawiki is very tolerant about what comes after the #redirect at the start
pg.re.redirect=RegExp('^\\s*[#](' + redirList.join('|') + ').*?\\[{2}([^\\|\\]]*)(|[^\\]]*)?\\]{2}\\s*(.*)', 'i');
}
function setInterwiki() {
if (pg.wiki.wikimedia) {
// From https://meta.wikimedia.org/wiki/List_of_Wikipedias
pg.wiki.interwiki='aa|ab|ace|af|ak|als|am|an|ang|ar|arc|arz|as|ast|av|ay|az|ba|bar|bat-smg|bcl|be|be-x-old|bg|bh|bi|bjn|bm|bn|bo|bpy|br|bs|bug|bxr|ca|cbk-zam|cdo|ce|ceb|ch|cho|chr|chy|ckb|co|cr|crh|cs|csb|cu|cv|cy|da|de|diq|dsb|dv|dz|ee|el|eml|en|eo|es|et|eu|ext|fa|ff|fi|fiu-vro|fj|fo|fr|frp|frr|fur|fy|ga|gag|gan|gd|gl|glk|gn|got|gu|gv|ha|hak|haw|he|hi|hif|ho|hr|hsb|ht|hu|hy|hz|ia|id|ie|ig|ii|ik|ilo|io|is|it|iu|ja|jbo|jv|ka|kaa|kab|kbd|kg|ki|kj|kk|kl|km|kn|ko|koi|kr|krc|ks|ksh|ku|kv|kw|ky|la|lad|lb|lbe|lg|li|lij|lmo|ln|lo|lt|ltg|lv|map-bms|mdf|mg|mh|mhr|mi|mk|ml|mn|mo|mr|mrj|ms|mt|mus|mwl|my|myv|mzn|na|nah|nap|nds|nds-nl|ne|new|ng|nl|nn|no|nov|nrm|nv|ny|oc|om|or|os|pa|pag|pam|pap|pcd|pdc|pfl|pi|pih|pl|pms|pnb|pnt|ps|pt|qu|rm|rmy|rn|ro|roa-rup|roa-tara|ru|rue|rw|sa|sah|sc|scn|sco|sd|se|sg|sh|si|simple|sk|sl|sm|sn|so|sq|sr|srn|ss|st|stq|su|sv|sw|szl|ta|te|tet|tg|th|ti|tk|tl|tn|to|tpi|tr|ts|tt|tum|tw|ty|udm|ug|uk|ur|uz|ve|vec|vi|vls|vo|wa|war|wo|wuu|xal|xh|yi|yo|za|zea|zh|zh-classical|zh-min-nan|zh-yue|zu';
pg.re.interwiki=RegExp('^'+pg.wiki.interwiki+':');
} else {
pg.wiki.interwiki=null;
pg.re.interwiki=RegExp('^$');
}
}
// return a regexp pattern matching all variants to write the given namespace
function nsRe(namespaceId) {
var imageNamespaceVariants = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=namespaceId) return;
_localizedNamespaceLc = upcaseFirst(_localizedNamespaceLc);
imageNamespaceVariants.push(mw.util.escapeRegExp(_localizedNamespaceLc).split(' ').join('[ _]'));
imageNamespaceVariants.push(mw.util.escapeRegExp(encodeURI(_localizedNamespaceLc)));
});
return '(?:' + imageNamespaceVariants.join('|') + ')';
}
function nsReImage() {
return nsRe(pg.nsImageId);
}
// ENDFILE: namespaces.js
// STARTFILE: selpop.js
//<NOLITE>
function getEditboxSelection() {
// see http://www.webgurusforum.com/8/12/0
var editbox;
try {
editbox=document.editform.wpTextbox1;
} catch (dang) { return; }
// IE, Opera
if (document.selection) { return document.selection.createRange().text; }
// Mozilla
var selStart = editbox.selectionStart;
var selEnd = editbox.selectionEnd;
return (editbox.value).substring(selStart, selEnd);
}
function doSelectionPopup() {
// popup if the selection looks like [[foo|anything afterwards at all
// or [[foo|bar]]text without ']]'
// or [[foo|bar]]
var sel=getEditboxSelection();
var open=sel.indexOf('[[');
var pipe=sel.indexOf('|');
var close=sel.indexOf(']]');
if (open == -1 || ( pipe == -1 && close == -1) ) { return; }
if (pipe != -1 && open > pipe || close != -1 && open > close) { return; }
if (getValueOf('popupOnEditSelection')=='boxpreview') {
return doSeparateSelectionPopup(sel);
}
var article=new Title(sel.substring(open+2, (pipe < 0) ? close : pipe)).urlString();
if (close > 0 && sel.substring(close+2).indexOf('[[') >= 0) {
return;
}
var a=document.createElement('a');
a.href=pg.wiki.titlebase + article;
mouseOverWikiLink2(a);
if (a.navpopup) {
a.navpopup.addHook(function(){runStopPopupTimer(a.navpopup);}, 'unhide', 'after');
}
}
function doSeparateSelectionPopup(str) {
var div=document.getElementById('selectionPreview');
if (!div) {
div = document.createElement('div');
div.id='selectionPreview';
try {
var box=document.editform.wpTextbox1;
box.parentNode.insertBefore(div, box);
} catch (error) {
return;
}
}
div.innerHTML=wiki2html(str);
div.ranSetupTooltipsAlready = false;
popTipsSoonFn('selectionPreview')();
}
//</NOLITE>
// ENDFILE: selpop.js
// STARTFILE: navpopup.js
/**
@fileoverview Defines two classes: {@link Navpopup} and {@link Mousetracker}.
<code>Navpopup</code> describes popups: when they appear, where, what
they look like and so on.
<code>Mousetracker</code> "captures" the mouse using
<code>document.onmousemove</code>.
*/
/**
Creates a new Mousetracker.
@constructor
@class The Mousetracker class. This monitors mouse movements and manages associated hooks.
*/
function Mousetracker() {
/**
Interval to regularly run the hooks anyway, in milliseconds.
@type Integer
*/
this.loopDelay=400;
/**
Timer for the loop.
@type Timer
*/
this.timer=null;
/**
Flag - are we switched on?
@type Boolean
*/
this.active=false;
/**
Flag - are we probably inaccurate, i.e. not reflecting the actual mouse position?
*/
this.dirty=true;
/**
Array of hook functions.
@private
@type Array
*/
this.hooks=[];
}
/**
Adds a hook, to be called when we get events.
@param {Function} f A function which is called as
<code>f(x,y)</code>. It should return <code>true</code> when it
wants to be removed, and <code>false</code> otherwise.
*/
Mousetracker.prototype.addHook = function (f) {
this.hooks.push(f);
};
/**
Runs hooks, passing them the x
and y coords of the mouse. Hook functions that return true are
passed to {@link Mousetracker#removeHooks} for removal.
@private
*/
Mousetracker.prototype.runHooks = function () {
if (!this.hooks || !this.hooks.length) { return; }
//log('Mousetracker.runHooks; we got some hooks to run');
var remove=false;
var removeObj={};
// this method gets called a LOT -
// pre-cache some variables
var x=this.x, y=this.y, len = this.hooks.length;
for (var i=0; i<len; ++i) {
//~ run the hook function, and remove it if it returns true
if (this.hooks[i](x, y)===true) {
remove=true;
removeObj[i]=true;
}
}
if (remove) { this.removeHooks(removeObj); }
};
/**
Removes hooks.
@private
@param {Object} removeObj An object whose keys are the index
numbers of functions for removal, with values that evaluate to true
*/
Mousetracker.prototype.removeHooks = function(removeObj) {
var newHooks=[];
var len = this.hooks.length;
for (var i=0; i<len; ++i) {
if (! removeObj[i]) { newHooks.push(this.hooks[i]); }
}
this.hooks=newHooks;
};
/**
Event handler for mouse wiggles.
We simply grab the event, set x and y and run the hooks.
This makes the cpu all hot and bothered :-(
@private
@param {Event} e Mousemove event
*/
Mousetracker.prototype.track=function (e) {
//~ Apparently this is needed in IE.
e = e || window.event;
var x, y;
if (e) {
if (e.pageX) { x=e.pageX; y=e.pageY; }
else if (typeof e.clientX!='undefined') {
var left, top, docElt = document.documentElement;
if (docElt) { left=docElt.scrollLeft; }
left = left || document.body.scrollLeft || document.scrollLeft || 0;
if (docElt) { top=docElt.scrollTop; }
top = top || document.body.scrollTop || document.scrollTop || 0;
x=e.clientX + left;
y=e.clientY + top;
} else { return; }
this.setPosition(x,y);
}
};
/**
Sets the x and y coordinates stored and takes appropriate action,
running hooks as appropriate.
@param {Integer} x, y Screen coordinates to set
*/
Mousetracker.prototype.setPosition=function(x,y) {
this.x = x;
this.y = y;
if (this.dirty || this.hooks.length === 0) { this.dirty=false; return; }
if (typeof this.lastHook_x != 'number') { this.lastHook_x = -100; this.lastHook_y=-100; }
var diff = (this.lastHook_x - x)*(this.lastHook_y - y);
diff = (diff >= 0) ? diff : -diff;
if ( diff > 1 ) {
this.lastHook_x=x;
this.lastHook_y=y;
if (this.dirty) { this.dirty = false; }
else { this.runHooks(); }
}
};
/**
Sets things in motion, unless they are already that is, registering an event handler on <code>document.onmousemove</code>.
A half-hearted attempt is made to preserve the old event handler if there is one.
*/
Mousetracker.prototype.enable = function () {
if (this.active) { return; }
this.active=true;
//~ Save the current handler for mousemove events. This isn't too
//~ robust, of course.
this.savedHandler=document.onmousemove;
//~ Gotta save @tt{this} again for the closure, and use apply for
//~ the member function.
var savedThis=this;
document.onmousemove=function (e) {savedThis.track.apply(savedThis, [e]);};
if (this.loopDelay) { this.timer = setInterval(function() { //log('loop delay in mousetracker is working');
savedThis.runHooks();}, this.loopDelay); }
};
/**
Disables the tracker, removing the event handler.
*/
Mousetracker.prototype.disable = function () {
if (!this.active) { return; }
if (typeof this.savedHandler === 'function') {
document.onmousemove=this.savedHandler;
} else { delete document.onmousemove; }
if (this.timer) { clearInterval(this.timer); }
this.active=false;
};
/**
Creates a new Navpopup.
Gets a UID for the popup and
@param init Contructor object. If <code>init.draggable</code> is true or absent, the popup becomes draggable.
@constructor
@class The Navpopup class. This generates popup hints, and does some management of them.
*/
function Navpopup(/*init*/) {
//alert('new Navpopup(init)');
/** UID for each Navpopup instance.
Read-only.
@type integer
*/
this.uid=Navpopup.uid++;
/**
Read-only flag for current visibility of the popup.
@type boolean
@private
*/
this.visible=false;
/** Flag to be set when we want to cancel a previous request to
show the popup in a little while.
@private
@type boolean
*/
this.noshow=false;
/** Categorised list of hooks.
@see #runHooks
@see #addHook
@private
@type Object
*/
this.hooks={
'create': [],
'unhide': [],
'hide': []
};
/** list of unique IDs of hook functions, to avoid duplicates
@private
*/
this.hookIds={};
/** List of downloads associated with the popup.
@private
@type Array
*/
this.downloads=[];
/** Number of uncompleted downloads.
@type integer
*/
this.pending=null;
/** Tolerance in pixels when detecting whether the mouse has left the popup.
@type integer
*/
this.fuzz=5;
/** Flag to toggle running {@link #limitHorizontalPosition} to regulate the popup's position.
@type boolean
*/
this.constrained=true;
/** The popup width in pixels.
@private
@type integer
*/
this.width=0;
/** The popup width in pixels.
@private
@type integer
*/
this.height=0;
/** The main content DIV element.
@type HTMLDivElement
*/
this.mainDiv=null;
this.createMainDiv();
// if (!init || typeof init.popups_draggable=='undefined' || init.popups_draggable) {
// this.makeDraggable(true);
// }
}
/**
A UID for each Navpopup. This constructor property is just a counter.
@type integer
@private
*/
Navpopup.uid=0;
/**
Retrieves the {@link #visible} attribute, indicating whether the popup is currently visible.
@type boolean
*/
Navpopup.prototype.isVisible=function() {
return this.visible;
};
/**
Repositions popup using CSS style.
@private
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@param {boolean} noLimitHor Don't call {@link #limitHorizontalPosition}
*/
Navpopup.prototype.reposition= function (x,y, noLimitHor) {
log ('reposition('+x+','+y+','+noLimitHor+')');
if (typeof x != 'undefined' && x !== null) { this.left=x; }
if (typeof y != 'undefined' && y !== null) { this.top=y; }
if (typeof this.left != 'undefined' && typeof this.top != 'undefined') {
this.mainDiv.style.left=this.left + 'px';
this.mainDiv.style.top=this.top + 'px';
}
if (!noLimitHor) { this.limitHorizontalPosition(); }
//console.log('navpop'+this.uid+' - (left,top)=(' + this.left + ',' + this.top + '), css=('
//+ this.mainDiv.style.left + ',' + this.mainDiv.style.top + ')');
};
/**
Prevents popups from being in silly locations. Hopefully.
Should not be run if {@link #constrained} is true.
@private
*/
Navpopup.prototype.limitHorizontalPosition=function() {
if (!this.constrained || this.tooWide) { return; }
this.updateDimensions();
var x=this.left;
var w=this.width;
var cWidth=document.body.clientWidth;
// log('limitHorizontalPosition: x='+x+
// ', this.left=' + this.left +
// ', this.width=' + this.width +
// ', cWidth=' + cWidth);
if ( (x+w) >= cWidth ||
( x > 0 &&
this.maxWidth &&
this.width < this.maxWidth &&
this.height > this.width &&
x > cWidth - this.maxWidth ) ) {
// This is a very nasty hack. There has to be a better way!
// We find the "natural" width of the div by positioning it at the far left
// then reset it so that it should be flush right (well, nearly)
this.mainDiv.style.left='-10000px';
this.mainDiv.style.width = this.maxWidth + 'px';
var naturalWidth=parseInt(this.mainDiv.offsetWidth, 10);
var newLeft=cWidth - naturalWidth - 1;
if (newLeft < 0) { newLeft = 0; this.tooWide=true; } // still unstable for really wide popups?
log ('limitHorizontalPosition: moving to ('+newLeft + ','+ this.top+');' + ' naturalWidth=' + naturalWidth + ', clientWidth=' + cWidth);
this.reposition(newLeft, null, true);
}
};
/**
Counter indicating the z-order of the "highest" popup.
We start the z-index at 1000 so that popups are above everything
else on the screen.
@private
@type integer
*/
Navpopup.highest=1000;
/**
Brings popup to the top of the z-order.
We increment the {@link #highest} property of the contructor here.
@private
*/
Navpopup.prototype.raise = function () {
this.mainDiv.style.zIndex=Navpopup.highest + 1;
++Navpopup.highest;
};
/**
Shows the popup provided {@link #noshow} is not true.
Updates the position, brings the popup to the top of the z-order and unhides it.
*/
Navpopup.prototype.show = function () {
//document.title+='s';
if (this.noshow) { return; }
//document.title+='t';
this.reposition();
this.raise();
this.unhide();
};
/**
Checks to see if the mouse pointer has
stabilised (checking every <code>time</code>/2 milliseconds) and runs the
{@link #show} method if it has.
@param {integer} time The minimum time (ms) before the popup may be shown.
*/
Navpopup.prototype.showSoonIfStable = function (time) {
log ('showSoonIfStable, time='+time);
if (this.visible) { return; }
this.noshow = false;
//~ initialize these variables so that we never run @tt{show} after
//~ just half the time
this.stable_x = -10000; this.stable_y = -10000;
var stableShow = function() {
log('stableShow called');
var new_x = Navpopup.tracker.x, new_y = Navpopup.tracker.y;
var dx = savedThis.stable_x - new_x, dy = savedThis.stable_y - new_y;
var fuzz2 = 0; // savedThis.fuzz * savedThis.fuzz;
//document.title += '[' + [savedThis.stable_x,new_x, savedThis.stable_y,new_y, dx, dy, fuzz2].join(',') + '] ';
if ( dx * dx <= fuzz2 && dy * dy <= fuzz2 ) {
log ('mouse is stable');
clearInterval(savedThis.showSoonStableTimer);
savedThis.reposition.apply(savedThis, [new_x + 2, new_y + 2]);
savedThis.show.apply(savedThis, []);
savedThis.limitHorizontalPosition.apply(savedThis, []);
return;
}
savedThis.stable_x = new_x; savedThis.stable_y = new_y;
};
var savedThis = this;
this.showSoonStableTimer = setInterval(stableShow, time/2);
};
/**
Sets the {@link #noshow} flag and hides the popup. This should be called
when the mouse leaves the link before
(or after) it's actually been displayed.
*/
Navpopup.prototype.banish = function () {
log ('banish called');
// hide and prevent showing with showSoon in the future
this.noshow=true;
if (this.showSoonStableTimer) {
log('clearing showSoonStableTimer');
clearInterval(this.showSoonStableTimer);
}
this.hide();
};
/**
Runs hooks added with {@link #addHook}.
@private
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
*/
Navpopup.prototype.runHooks = function (key, when) {
if (!this.hooks[key]) { return; }
var keyHooks=this.hooks[key];
var len=keyHooks.length;
for (var i=0; i< len; ++i) {
if (keyHooks[i] && keyHooks[i].when == when) {
if (keyHooks[i].hook.apply(this, [])) {
// remove the hook
if (keyHooks[i].hookId) {
delete this.hookIds[keyHooks[i].hookId];
}
keyHooks[i]=null;
}
}
}
};
/**
Adds a hook to the popup. Hook functions are run with <code>this</code> set to refer to the Navpopup instance, and no arguments.
@param {Function} hook The hook function. Functions that return true are deleted.
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
@param {String} uid A truthy string identifying the hook function; if it matches another hook in this position, it won't be added again.
*/
Navpopup.prototype.addHook = function ( hook, key, when, uid ) {
when = when || 'after';
if (!this.hooks[key]) { return; }
// if uid is specified, don't add duplicates
var hookId=null;
if (uid) {
hookId=[key,when,uid].join('|');
if (this.hookIds[hookId]) {
return;
}
this.hookIds[hookId]=true;
}
this.hooks[key].push( {hook: hook, when: when, hookId: hookId} );
};
/**
Creates the main DIV element, which contains all the actual popup content.
Runs hooks with key 'create'.
@private
*/
Navpopup.prototype.createMainDiv = function () {
if (this.mainDiv) { return; }
this.runHooks('create', 'before');
var mainDiv=document.createElement('div');
var savedThis=this;
mainDiv.onclick=function(e) {savedThis.onclickHandler(e);};
mainDiv.className=(this.className) ? this.className : 'navpopup_maindiv';
mainDiv.id=mainDiv.className + this.uid;
mainDiv.style.position='absolute';
mainDiv.style.minWidth = '350px';
mainDiv.style.display='none';
mainDiv.className='navpopup';
// easy access to javascript object through DOM functions
mainDiv.navpopup=this;
this.mainDiv=mainDiv;
document.body.appendChild(mainDiv);
this.runHooks('create', 'after');
};
/**
Calls the {@link #raise} method.
@private
*/
Navpopup.prototype.onclickHandler=function(/*e*/) {
this.raise();
};
/**
Makes the popup draggable, using a {@link Drag} object.
@private
*/
Navpopup.prototype.makeDraggable=function(handleName) {
if (!this.mainDiv) { this.createMainDiv(); }
var drag=new Drag();
if (!handleName) {
drag.startCondition=function(e) {
try { if (!e.shiftKey) { return false; } } catch (err) { return false; }
return true;
};
}
var dragHandle;
if (handleName) dragHandle = document.getElementById(handleName);
if (!dragHandle) dragHandle = this.mainDiv;
var np=this;
drag.endHook=function(x,y) {
Navpopup.tracker.dirty=true;
np.reposition(x,y);
};
drag.init(dragHandle,this.mainDiv);
};
/** Hides the popup using CSS. Runs hooks with key 'hide'.
Sets {@link #visible} appropriately. {@link #banish} should be called externally instead of this method.
@private
*/
Navpopup.prototype.hide = function () {
this.runHooks('hide', 'before');
this.abortDownloads();
if (typeof this.visible != 'undefined' && this.visible) {
this.mainDiv.style.display='none';
this.visible=false;
}
this.runHooks('hide', 'after');
};
/** Shows the popup using CSS. Runs hooks with key 'unhide'.
Sets {@link #visible} appropriately. {@link #show} should be called externally instead of this method.
@private
*/
Navpopup.prototype.unhide = function () {
this.runHooks('unhide', 'before');
if (typeof this.visible != 'undefined' && !this.visible) {
this.mainDiv.style.display='inline';
this.visible=true;
}
this.runHooks('unhide', 'after');
};
/**
Sets the <code>innerHTML</code> attribute of the main div containing the popup content.
@param {String} html The HTML to set.
*/
Navpopup.prototype.setInnerHTML = function (html) {
this.mainDiv.innerHTML = html;
};
/**
Updates the {@link #width} and {@link #height} attributes with the CSS properties.
@private
*/
Navpopup.prototype.updateDimensions = function () {
this.width=parseInt(this.mainDiv.offsetWidth, 10);
this.height=parseInt(this.mainDiv.offsetHeight, 10);
};
/**
Checks if the point (x,y) is within {@link #fuzz} of the
{@link #mainDiv}.
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@type boolean
*/
Navpopup.prototype.isWithin = function(x,y) {
//~ If we're not even visible, no point should be considered as
//~ being within the popup.
if (!this.visible) { return false; }
this.updateDimensions();
var fuzz=this.fuzz || 0;
//~ Use a simple box metric here.
return (x+fuzz >= this.left && x-fuzz <= this.left + this.width &&
y+fuzz >= this.top && y-fuzz <= this.top + this.height);
};
/**
Adds a download to {@link #downloads}.
@param {Downloader} download
*/
Navpopup.prototype.addDownload=function(download) {
if (!download) { return; }
this.downloads.push(download);
};
/**
Aborts the downloads listed in {@link #downloads}.
@see Downloader#abort
*/
Navpopup.prototype.abortDownloads=function() {
for(var i=0; i<this.downloads.length; ++i) {
var d=this.downloads[i];
if (d && d.abort) { d.abort(); }
}
this.downloads=[];
};
/**
A {@link Mousetracker} instance which is a property of the constructor (pseudo-global).
*/
Navpopup.tracker=new Mousetracker();
// ENDFILE: navpopup.js
// STARTFILE: diff.js
//<NOLITE>
/*
* Javascript Diff Algorithm
* By John Resig (http://ejohn.org/) and [[:en:User:Lupin]]
*
* More Info:
* http://ejohn.org/projects/javascript-diff-algorithm/
*/
function delFmt(x) {
if (!x.length) { return ''; }
return "<del class='popupDiff'>" + x.join('') +"</del>";
}
function insFmt(x) {
if (!x.length) { return ''; }
return "<ins class='popupDiff'>" + x.join('') +"</ins>";
}
function countCrossings(a, b, i, eject) {
// count the crossings on the edge starting at b[i]
if (!b[i].row && b[i].row !== 0) { return -1; }
var count=0;
for (var j=0; j<a.length; ++j) {
if (!a[j].row && a[j].row !== 0) { continue; }
if ( (j-b[i].row)*(i-a[j].row) > 0) {
if(eject) { return true; }
count++;
}
}
return count;
}
function shortenDiffString(str, context) {
var re=RegExp('(<del[\\s\\S]*?</del>|<ins[\\s\\S]*?</ins>)');
var splitted=str.parenSplit(re);
var ret=[''];
for (var i=0; i<splitted.length; i+=2) {
if (splitted[i].length < 2*context) {
ret[ret.length-1] += splitted[i];
if (i+1<splitted.length) { ret[ret.length-1] += splitted[i+1]; }
continue;
}
else {
if (i > 0) { ret[ret.length-1] += splitted[i].substring(0,context); }
if (i+1 < splitted.length) {
ret.push(splitted[i].substring(splitted[i].length-context) +
splitted[i+1]);
}
}
}
while (ret.length > 0 && !ret[0]) { ret = ret.slice(1); }
return ret;
}
function diffString( o, n, simpleSplit ) {
var splitRe=RegExp('([[]{2}|[\\]]{2}|[{]{2,3}|[}]{2,3}|[|]|=|<|>|[*:]+|\\s|\\b)');
// We need to split the strings o and n first, and entify() the parts
// individually, so that the HTML entities are never cut apart. (AxelBoldt)
var out, i, oSplitted, nSplitted;
if (simpleSplit) {
oSplitted=o.split(/\b/);
nSplitted=n.split(/\b/);
} else {
oSplitted=o.parenSplit(splitRe);
nSplitted=n.parenSplit(splitRe);
}
for (i=0; i<oSplitted.length; ++i) {oSplitted[i]=oSplitted[i].entify();}
for (i=0; i<nSplitted.length; ++i) {nSplitted[i]=nSplitted[i].entify();}
out = diff (oSplitted, nSplitted);
var str = "";
var acc=[]; // accumulator for prettier output
// crossing pairings -- eg 'A B' vs 'B A' -- cause problems, so let's iron them out
// this doesn't always do things optimally but it should be fast enough
var maxOutputPair=0;
for (i=0; i<out.n.length; ++i) {
if ( out.n[i].paired ) {
if( maxOutputPair > out.n[i].row ) {
// tangle - delete pairing
out.o[ out.n[i].row ]=out.o[ out.n[i].row ].text;
out.n[i]=out.n[i].text;
}
if (maxOutputPair < out.n[i].row) { maxOutputPair = out.n[i].row; }
}
}
// output the stuff preceding the first paired old line
for (i=0; i<out.o.length && !out.o[i].paired; ++i) { acc.push( out.o[i] ); }
str += delFmt(acc); acc=[];
// main loop
for ( i = 0; i < out.n.length; ++i ) {
// output unpaired new "lines"
while ( i < out.n.length && !out.n[i].paired ) { acc.push( out.n[i++] ); }
str += insFmt(acc); acc=[];
if ( i < out.n.length ) { // this new "line" is paired with the (out.n[i].row)th old "line"
str += out.n[i].text;
// output unpaired old rows starting after this new line's partner
var m = out.n[i].row + 1;
while ( m < out.o.length && !out.o[m].paired ) { acc.push ( out.o[m++] ); }
str += delFmt(acc); acc=[];
}
}
return str;
}
// see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Object
// FIXME: use obj.hasOwnProperty instead of this kludge!
var jsReservedProperties=RegExp('^(constructor|prototype|__((define|lookup)[GS]etter)__' +
'|eval|hasOwnProperty|propertyIsEnumerable' +
'|to(Source|String|LocaleString)|(un)?watch|valueOf)$');
function diffBugAlert(word) {
if (!diffBugAlert.list[word]) {
diffBugAlert.list[word]=1;
alert('Bad word: '+word+'\n\nPlease report this bug.');
}
}
diffBugAlert.list={};
function makeDiffHashtable(src) {
var ret={};
for ( var i = 0; i < src.length; i++ ) {
if ( jsReservedProperties.test(src[i]) ) { src[i] += '<!-- -->'; }
if ( !ret[ src[i] ] ) { ret[ src[i] ] = []; }
try { ret[ src[i] ].push( i ); } catch (err) { diffBugAlert(src[i]); }
}
return ret;
}
function diff( o, n ) {
// pass 1: make hashtable ns with new rows as keys
var ns = makeDiffHashtable(n);
// pass 2: make hashtable os with old rows as keys
var os = makeDiffHashtable(o);
// pass 3: pair unique new rows and matching unique old rows
var i;
for ( i in ns ) {
if ( ns[i].length == 1 && os[i] && os[i].length == 1 ) {
n[ ns[i][0] ] = { text: n[ ns[i][0] ], row: os[i][0], paired: true };
o[ os[i][0] ] = { text: o[ os[i][0] ], row: ns[i][0], paired: true };
}
}
// pass 4: pair matching rows immediately following paired rows (not necessarily unique)
for ( i = 0; i < n.length - 1; i++ ) {
if ( n[i].paired && ! n[i+1].paired && n[i].row + 1 < o.length && ! o[ n[i].row + 1 ].paired &&
n[i+1] == o[ n[i].row + 1 ] ) {
n[i+1] = { text: n[i+1], row: n[i].row + 1, paired: true };
o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1, paired: true };
}
}
// pass 5: pair matching rows immediately preceding paired rows (not necessarily unique)
for ( i = n.length - 1; i > 0; i-- ) {
if ( n[i].paired && ! n[i-1].paired && n[i].row > 0 && ! o[ n[i].row - 1 ].paired &&
n[i-1] == o[ n[i].row - 1 ] ) {
n[i-1] = { text: n[i-1], row: n[i].row - 1, paired: true };
o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1, paired: true };
}
}
return { o: o, n: n };
}
//</NOLITE>
// ENDFILE: diff.js
// STARTFILE: init.js
function setSiteInfo() {
if (window.popupLocalDebug) {
pg.wiki.hostname = 'en.wikipedia.org';
} else {
pg.wiki.hostname = location.hostname; // use in preference to location.hostname for flexibility (?)
}
pg.wiki.wikimedia=RegExp('(wiki([pm]edia|source|books|news|quote|versity)|wiktionary|mediawiki)[.]org').test(pg.wiki.hostname);
pg.wiki.wikia=RegExp('[.]wikia[.]com$', 'i').test(pg.wiki.hostname);
pg.wiki.isLocal=RegExp('^localhost').test(pg.wiki.hostname);
pg.wiki.commons=( pg.wiki.wikimedia && pg.wiki.hostname != 'commons.wikimedia.org') ? 'commons.wikimedia.org' : null;
pg.wiki.lang = mw.config.get('wgContentLanguage');
var port = location.port ? ':' + location.port : '';
pg.wiki.sitebase = pg.wiki.hostname + port;
}
function setUserInfo() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
list: 'users',
ususers: mw.config.get('wgUserName'),
usprop: 'rights'
};
pg.user.canReview = false;
if (getValueOf('popupReview')) {
api.get(params).done(function(data){
var rights = data.query.users[0].rights;
pg.user.canReview = rights.indexOf('review') !== -1; // TODO: Should it be a getValueOf('ReviewRight') ?
});
}
}
function setTitleBase() {
var protocol = ( window.popupLocalDebug ? 'http:' : location.protocol );
pg.wiki.articlePath = mw.config.get('wgArticlePath').replace(/\/\$1/, ""); // as in http://some.thing.com/wiki/Article
pg.wiki.botInterfacePath = mw.config.get('wgScript');
pg.wiki.APIPath = mw.config.get('wgScriptPath') +"/api.php";
// default mediawiki setting is paths like http://some.thing.com/articlePath/index.php?title=foo
var titletail = pg.wiki.botInterfacePath + '?title=';
//var titletail2 = joinPath([pg.wiki.botInterfacePath, 'wiki.phtml?title=']);
// other sites may need to add code here to set titletail depending on how their urls work
pg.wiki.titlebase = protocol + '//' + pg.wiki.sitebase + titletail;
//pg.wiki.titlebase2 = protocol + '//' + joinPath([pg.wiki.sitebase, titletail2]);
pg.wiki.wikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.botInterfacePath;
pg.wiki.apiwikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.APIPath;
pg.wiki.articlebase = protocol + '//' + pg.wiki.sitebase + pg.wiki.articlePath;
pg.wiki.commonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.botInterfacePath;
pg.wiki.apicommonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.APIPath;
pg.re.basenames = RegExp( '^(' +
map( literalizeRegex, [ pg.wiki.titlebase, //pg.wiki.titlebase2,
pg.wiki.articlebase ]).join('|') + ')' );
}
//////////////////////////////////////////////////
// Global regexps
function setMainRegex() {
var reStart='[^:]*://';
var preTitles = literalizeRegex( mw.config.get('wgScriptPath') ) + '/(?:index[.]php|wiki[.]phtml)[?]title=';
preTitles += '|' + literalizeRegex( pg.wiki.articlePath + '/' );
var reEnd='(' + preTitles + ')([^&?#]*)[^#]*(?:#(.+))?';
pg.re.main = RegExp(reStart + literalizeRegex(pg.wiki.sitebase) + reEnd);
}
function setRegexps() {
// TODO: We shoud use an api call to get the aliases for special pages, now it does not work for non-English wikipedias:
// E.g., https://ru.wikipedia.org/w/api.php?action=query&meta=siteinfo&siprop=specialpagealiases&formatversion=2
setMainRegex();
var sp=nsRe(pg.nsSpecialId);
pg.re.urlNoPopup=RegExp('((title=|/)' + sp + '(?:%3A|:)|section=[0-9]|^#$)') ;
pg.re.contribs =RegExp('(title=|/)' + sp + '(?:%3A|:)Contributions' + '(&target=|/|/' + nsRe(pg.nsUserId)+':)(.*)') ;
pg.re.email =RegExp('(title=|/)' + sp + '(?:%3A|:)EmailUser' + '(&target=|/|/(?:' + nsRe(pg.nsUserId)+':)?)(.*)') ;
pg.re.backlinks =RegExp('(title=|/)' + sp + '(?:%3A|:)WhatLinksHere' + '(&target=|/)([^&]*)');
pg.re.specialdiff=RegExp('/' + sp + '(?:%3A|:)Diff/([^?#]*)');
//<NOLITE>
var im=nsReImage();
// note: tries to get images in infobox templates too, e.g. movie pages, album pages etc
// (^|\[\[)image: *([^|\]]*[^|\] ]) *
// (^|\[\[)image: *([^|\]]*[^|\] ])([^0-9\]]*([0-9]+) *px)?
// $4 = 120 as in 120px
pg.re.image = RegExp('(^|\\[\\[)' + im + ': *([^|\\]]*[^|\\] ])' +
'([^0-9\\]]*([0-9]+) *px)?|(?:\\n *[|]?|[|]) *' +
'(' + getValueOf('popupImageVarsRegexp') + ')' +
' *= *(?:\\[\\[ *)?(?:' + im + ':)?' +
'([^|]*?)(?:\\]\\])? *[|]? *\\n', 'img') ;
pg.re.imageBracketCount = 6;
pg.re.category = RegExp('\\[\\[' +nsRe(pg.nsCategoryId) +
': *([^|\\]]*[^|\\] ]) *', 'i');
pg.re.categoryBracketCount = 1;
pg.re.ipUser=RegExp('^' +
// IPv6
'(?::(?::|(?::[0-9A-Fa-f]{1,4}){1,7})|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,6}::|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){7})' +
// IPv4
'|(((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}' +
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9]))$');
pg.re.stub= RegExp(getValueOf('popupStubRegexp'), 'im');
pg.re.disambig=RegExp(getValueOf('popupDabRegexp'), 'im');
//</NOLITE>
// FIXME replace with general parameter parsing function, this is daft
pg.re.oldid=RegExp('[?&]oldid=([^&]*)');
pg.re.diff=RegExp('[?&]diff=([^&]*)');
}
//////////////////////////////////////////////////
// miscellany
function setupCache() {
// page caching
pg.cache.pages = [];
}
function setMisc() {
pg.current.link=null;
pg.current.links=[];
pg.current.linksHash={};
setupCache();
pg.timer.checkPopupPosition=null;
pg.counter.loop=0;
// ids change with each popup: popupImage0, popupImage1 etc
pg.idNumber=0;
// for myDecodeURI
pg.misc.decodeExtras = [
{from: '%2C', to: ',' },
{from: '_', to: ' ' },
{from: '%24', to: '$'},
{from: '%26', to: '&' } // no ,
];
pg.misc.userAgent = 'Navigation popups/1.0 (' + mw.config.get( 'wgServerName' ) +')';
}
// We need a callback since this might end up asynchronous because of
// the mw.loader.using() call.
function setupPopups( callback ) {
if ( setupPopups.completed ) {
if ( typeof callback === 'function' ) {
callback();
}
return;
}
// These dependencies are also enforced from the gadget,
// but not everyone loads this as a gadget, so double check
mw.loader.using( ['mediawiki.util', 'mediawiki.user', 'user.options'] ).then( function() {
// NB translatable strings should be set up first (strings.js)
// basics
setupDebugging();
setSiteInfo();
setTitleBase();
setOptions(); // see options.js
setUserInfo();
// namespaces etc
setNamespaces();
setInterwiki();
// regexps
setRegexps();
setRedirs();
// other stuff
setMisc();
setupLivePreview();
// main deal here
setupTooltips();
log('In setupPopups(), just called setupTooltips()');
Navpopup.tracker.enable();
setupPopups.completed = true;
if ( typeof callback === 'function' ) {
callback();
}
});
}
// ENDFILE: init.js
// STARTFILE: navlinks.js
//<NOLITE>
//////////////////////////////////////////////////
// navlinks... let the fun begin
//
function defaultNavlinkSpec() {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
if (getValueOf('popupLastEditLink')) {
str += '*<<lastEdit|shortcut=/>>|<<lastContrib>>|<<sinceMe>>if(oldid){|<<oldEdit>>|<<diffCur>>}';
}
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>*<<userlog|shortcut=L|log>>';
str+='if(ipuser){*<<arin>>}if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>|<<blocklog|log>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' + editstr + '}';
var historystr='<<history|shortcut=h>>|<<editors|shortcut=E|>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str+='<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>*<<move|shortcut=m>>';
// admin links
str += 'if(admin){<br><<unprotect|unprotectShort>>|<<protect|shortcut=p>>|<<protectlog|log>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>|<<deletelog|log>>}';
return str;
}
function navLinksHTML (article, hint, params) { //oldid, rcid) {
var str = '<span class="popupNavLinks">' + defaultNavlinkSpec() + '</span>';
// BAM
return navlinkStringToHTML(str, article, params);
}
function expandConditionalNavlinkString(s,article,z,recursionCount) {
var oldid=z.oldid, rcid=z.rcid, diff=z.diff;
// nested conditionals (up to 10 deep) are ok, hopefully! (work from the inside out)
if (typeof recursionCount!=typeof 0) { recursionCount=0; }
var conditionalSplitRegex=RegExp(
//(1 if \\( (2 2) \\) {(3 3)} (4 else {(5 5)} 4)1)
'(;?\\s*if\\s*\\(\\s*([\\w]*)\\s*\\)\\s*\\{([^{}]*)\\}(\\s*else\\s*\\{([^{}]*?)\\}|))', 'i');
var splitted=s.parenSplit(conditionalSplitRegex);
// $1: whole conditional
// $2: test condition
// $3: true expansion
// $4: else clause (possibly empty)
// $5: false expansion (possibly null)
var numParens=5;
var ret = splitted[0];
for (var i=1; i<splitted.length; i=i+numParens+1) {
var testString=splitted[i+2-1];
var trueString=splitted[i+3-1];
var falseString=splitted[i+5-1];
if (typeof falseString=='undefined' || !falseString) { falseString=''; }
var testResult=null;
switch (testString) {
case 'user':
testResult=(article.userName())?true:false;
break;
case 'talk':
testResult=(article.talkPage())?false:true; // talkPage converts _articles_ to talkPages
break;
case 'admin':
testResult=getValueOf('popupAdminLinks')?true:false;
break;
case 'oldid':
testResult=(typeof oldid != 'undefined' && oldid)?true:false;
break;
case 'rcid':
testResult=(typeof rcid != 'undefined' && rcid)?true:false;
break;
case 'ipuser':
testResult=(article.isIpUser())?true:false;
break;
case 'mainspace_en':
testResult=isInMainNamespace(article) &&
pg.wiki.hostname=='en.wikipedia.org';
break;
case 'wikimedia':
testResult=(pg.wiki.wikimedia) ? true : false;
break;
case 'diff':
testResult=(typeof diff != 'undefined' && diff)?true:false;
break;
}
switch(testResult) {
case null: ret+=splitted[i]; break;
case true: ret+=trueString; break;
case false: ret+=falseString; break;
}
// append non-conditional string
ret += splitted[i+numParens];
}
if (conditionalSplitRegex.test(ret) && recursionCount < 10) {
return expandConditionalNavlinkString(ret,article,z,recursionCount+1);
}
return ret;
}
function navlinkStringToArray(s, article, params) {
s=expandConditionalNavlinkString(s,article,params);
var splitted=s.parenSplit(RegExp('<<(.*?)>>'));
var ret=[];
for (var i=0; i<splitted.length; ++i) {
if (i%2) { // i odd, so s is a tag
var t=new navlinkTag();
var ss=splitted[i].split('|');
t.id=ss[0];
for (var j=1; j<ss.length; ++j) {
var sss=ss[j].split('=');
if (sss.length>1) {
t[sss[0]]=sss[1];
}
else { // no assignment (no "="), so treat this as a title (overwriting the last one)
t.text=popupString(sss[0]);
}
}
t.article=article;
var oldid=params.oldid, rcid=params.rcid, diff=params.diff;
if (typeof oldid !== 'undefined' && oldid !== null) { t.oldid=oldid; }
if (typeof rcid !== 'undefined' && rcid !== null) { t.rcid=rcid; }
if (typeof diff !== 'undefined' && diff !== null) { t.diff=diff; }
if (!t.text && t.id !== 'mainlink') { t.text=popupString(t.id); }
ret.push(t);
}
else { // plain HTML
ret.push(splitted[i]);
}
}
return ret;
}
function navlinkSubstituteHTML(s) {
return s.split('*').join(getValueOf('popupNavLinkSeparator'))
.split('<menurow>').join('<li class="popup_menu_row">')
.split('</menurow>').join('</li>')
.split('<menu>').join('<ul class="popup_menu">')
.split('</menu>').join('</ul>');
}
function navlinkDepth(magic,s) {
return s.split('<' + magic + '>').length - s.split('</' + magic + '>').length;
}
// navlinkString: * becomes the separator
// <<foo|bar=baz|fubar>> becomes a foo-link with attribute bar='baz'
// and visible text 'fubar'
// if(test){...} and if(test){...}else{...} work too (nested ok)
function navlinkStringToHTML(s,article,params) {
//limitAlert(navlinkStringToHTML, 5, 'navlinkStringToHTML\n' + article + '\n' + (typeof article));
var p=navlinkStringToArray(s,article,params);
var html='';
var menudepth = 0; // nested menus not currently allowed, but doesn't do any harm to code for it
var menurowdepth = 0;
for (var i=0; i<p.length; ++i) {
if (typeof p[i] == typeof '') {
html+=navlinkSubstituteHTML(p[i]);
menudepth += navlinkDepth('menu', p[i]);
menurowdepth += navlinkDepth('menurow', p[i]);
// if (menudepth === 0) {
// tagType='span';
// } else if (menurowdepth === 0) {
// tagType='li';
// } else {
// tagType = null;
// }
} else if (typeof p[i].type != 'undefined' && p[i].type=='navlinkTag') {
if (menudepth > 0 && menurowdepth === 0) {
html += '<li class="popup_menu_item">' + p[i].html() + '</li>';
} else {
html+=p[i].html();
}
}
}
return html;
}
function navlinkTag() {
this.type='navlinkTag';
}
navlinkTag.prototype.html=function () {
this.getNewWin();
this.getPrintFunction();
var html='';
var opening, closing;
var tagType='span';
if (!tagType) {
opening = ''; closing = '';
} else {
opening = '<' + tagType + ' class="popup_' + this.id + '">';
closing = '</' + tagType + '>';
}
if (typeof this.print!='function') {
errlog ('Oh dear - invalid print function for a navlinkTag, id='+this.id);
} else {
html=this.print(this);
if (typeof html != typeof '') {html='';}
else if (typeof this.shortcut!='undefined') html=addPopupShortcut(html, this.shortcut);
}
return opening + html + closing;
};
navlinkTag.prototype.getNewWin=function() {
getValueOf('popupLinksNewWindow');
if (typeof pg.option.popupLinksNewWindow[this.id] === 'undefined') { this.newWin=null; }
this.newWin=pg.option.popupLinksNewWindow[this.id];
};
navlinkTag.prototype.getPrintFunction=function() { //think about this some more
// this.id and this.article should already be defined
if (typeof this.id!=typeof '' || typeof this.article!=typeof {} ) { return; }
this.noPopup=1;
switch (this.id) {
case 'contribs': case 'history': case 'whatLinksHere':
case 'userPage': case 'monobook': case 'userTalk':
case 'talk': case 'article': case 'lastEdit':
this.noPopup=null;
}
switch (this.id) {
case 'email': case 'contribs': case 'block': case 'unblock':
case 'userlog': case 'userSpace': case 'deletedContribs':
this.article=this.article.userName();
}
switch (this.id) {
case 'userTalk': case 'newUserTalk': case 'editUserTalk':
case 'userPage': case 'monobook': case 'editMonobook': case 'blocklog':
this.article=this.article.userName(true);
/* fall through */
case 'pagelog': case 'deletelog': case 'protectlog':
delete this.oldid;
}
if (this.id=='editMonobook' || this.id=='monobook') { this.article.append('/monobook.js'); }
if (this.id != 'mainlink') {
// FIXME anchor handling should be done differently with Title object
this.article=this.article.removeAnchor();
// if (typeof this.text=='undefined') this.text=popupString(this.id);
}
switch (this.id) {
case 'undelete': this.print=specialLink; this.specialpage='Undelete'; this.sep='/'; break;
case 'whatLinksHere': this.print=specialLink; this.specialpage='Whatlinkshere'; break;
case 'relatedChanges': this.print=specialLink; this.specialpage='Recentchangeslinked'; break;
case 'move': this.print=specialLink; this.specialpage='Movepage'; break;
case 'contribs': this.print=specialLink; this.specialpage='Contributions'; break;
case 'deletedContribs':this.print=specialLink; this.specialpage='Deletedcontributions'; break;
case 'email': this.print=specialLink; this.specialpage='EmailUser'; this.sep='/'; break;
case 'block': this.print=specialLink; this.specialpage='Blockip'; this.sep='&ip='; break;
case 'unblock': this.print=specialLink; this.specialpage='Ipblocklist'; this.sep='&action=unblock&ip='; break;
case 'userlog': this.print=specialLink; this.specialpage='Log'; this.sep='&user='; break;
case 'blocklog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=block&page='; break;
case 'pagelog': this.print=specialLink; this.specialpage='Log'; this.sep='&page='; break;
case 'protectlog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=protect&page='; break;
case 'deletelog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=delete&page='; break;
case 'userSpace': this.print=specialLink; this.specialpage='PrefixIndex'; this.sep='&namespace=2&prefix='; break;
case 'search': this.print=specialLink; this.specialpage='Search'; this.sep='&fulltext=Search&search='; break;
case 'thank': this.print=specialLink; this.specialpage='Thanks'; this.sep='/'; this.article.value = this.diff; break;
case 'unwatch': case 'watch':
this.print=magicWatchLink; this.action=this.id+'&autowatchlist=1&autoimpl=' + popupString('autoedit_version') + '&actoken='+autoClickToken(); break;
case 'history': case 'historyfeed':
case 'unprotect': case 'protect':
this.print=wikiLink; this.action=this.id; break;
case 'delete':
this.print=wikiLink; this.action='delete';
if (this.article.namespaceId()==pg.nsImageId) {
var img=this.article.stripNamespace();
this.action+='&image='+img;
}
break;
case 'markpatrolled':
case 'edit': // editOld should keep the oldid, but edit should not.
delete this.oldid;
/* fall through */
case 'view': case 'purge': case 'render':
this.print=wikiLink;
this.action=this.id; break;
case 'raw':
this.print=wikiLink; this.action='raw'; break;
case 'new':
this.print=wikiLink; this.action='edit§ion=new'; break;
case 'mainlink':
if (typeof this.text=='undefined') { this.text=this.article.toString().entify(); }
if (getValueOf('popupSimplifyMainLink') && isInStrippableNamespace(this.article)) {
// only show the /subpage part of the title text
var s=this.text.split('/'); this.text=s[s.length-1];
if (this.text==='' && s.length > 1) { this.text=s[s.length-2]; }
}
this.print=titledWikiLink;
if (typeof this.title==='undefined' && pg.current.link && typeof pg.current.link.href !== 'undefined') {
this.title=safeDecodeURI((pg.current.link.originalTitle)?pg.current.link.originalTitle:this.article);
if (typeof this.oldid !== 'undefined' && this.oldid) {
this.title=tprintf('Revision %s of %s', [this.oldid, this.title]);
}
}
this.action='view'; break;
case 'userPage':
case 'article':
case 'monobook':
case 'editMonobook':
case 'editArticle':
delete this.oldid;
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.article=this.article.articleFromTalkOrArticle();
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.print=wikiLink;
if (this.id.indexOf('edit')===0) {
this.action='edit';
} else { this.action='view';}
break;
case 'userTalk':
case 'talk':
this.article=this.article.talkPage();
delete this.oldid;
this.print=wikiLink;
this.action='view'; break;
case 'arin':
this.print=arinLink; break;
case 'count':
this.print=editCounterLink; break;
case 'google':
this.print=googleLink; break;
case 'editors':
this.print=editorListLink; break;
case 'globalsearch':
this.print=globalSearchLink; break;
case 'lastEdit':
this.print=titledDiffLink;
this.title=popupString('Show the last edit');
this.from='prev'; this.to='cur'; break;
case 'oldEdit':
this.print=titledDiffLink;
this.title=popupString('Show the edit made to get revision') + ' ' + this.oldid;
this.from='prev'; this.to=this.oldid; break;
case 'editOld':
this.print=wikiLink; this.action='edit'; break;
case 'undo':
this.print=wikiLink; this.action='edit&undo='; break;
case 'revert':
this.print=wikiLink; this.action='revert'; break;
case 'nullEdit':
this.print=wikiLink; this.action='nullEdit'; break;
case 'diffCur':
this.print=titledDiffLink;
this.title=tprintf('Show changes since revision %s', [this.oldid]);
this.from=this.oldid; this.to='cur'; break;
case 'editUserTalk':
case 'editTalk':
delete this.oldid;
this.article=this.article.talkPage();
this.action='edit'; this.print=wikiLink; break;
case 'newUserTalk':
case 'newTalk':
this.article=this.article.talkPage();
this.action='edit§ion=new'; this.print=wikiLink; break;
case 'lastContrib':
case 'sinceMe':
this.print=magicHistoryLink;
break;
case 'togglePreviews':
this.text=popupString(pg.option.simplePopups ? 'enable previews' : 'disable previews');
/* fall through */
case 'disablePopups': case 'purgePopups':
this.print=popupMenuLink;
break;
default:
this.print=function () {return 'Unknown navlink type: '+this.id+'';};
}
};
//
// end navlinks
//////////////////////////////////////////////////
//</NOLITE>
// ENDFILE: navlinks.js
// STARTFILE: shortcutkeys.js
//<NOLITE>
function popupHandleKeypress(evt) {
var keyCode = window.event ? window.event.keyCode : ( evt.keyCode ? evt.keyCode : evt.which);
if (!keyCode || !pg.current.link || !pg.current.link.navpopup) { return; }
if (keyCode==27) { // escape
killPopup();
return false; // swallow keypress
}
var letter=String.fromCharCode(keyCode);
var links=pg.current.link.navpopup.mainDiv.getElementsByTagName('A');
var startLink=0;
var i,j;
if (popupHandleKeypress.lastPopupLinkSelected) {
for (i=0; i<links.length; ++i) {
if (links[i]==popupHandleKeypress.lastPopupLinkSelected) { startLink=i; }
}
}
for (j=0; j<links.length; ++j) {
i=(startLink + j + 1) % links.length;
if (links[i].getAttribute('popupkey')==letter) {
if (evt && evt.preventDefault) evt.preventDefault();
links[i].focus();
popupHandleKeypress.lastPopupLinkSelected=links[i];
return false; // swallow keypress
}
}
// pass keypress on
if (document.oldPopupOnkeypress) { return document.oldPopupOnkeypress(evt); }
return true;
}
function addPopupShortcuts() {
if (document.onkeypress!=popupHandleKeypress) {
document.oldPopupOnkeypress=document.onkeypress;
}
document.onkeypress=popupHandleKeypress;
}
function rmPopupShortcuts() {
popupHandleKeypress.lastPopupLinkSelected=null;
try {
if (document.oldPopupOnkeypress && document.oldPopupOnkeypress==popupHandleKeypress) {
// panic
document.onkeypress=null; //function () {};
return;
}
document.onkeypress=document.oldPopupOnkeypress;
} catch (nasties) { /* IE goes here */ }
}
function addLinkProperty(html, property) {
// take "<a href=...>...</a> and add a property
// not sophisticated at all, easily broken
var i=html.indexOf('>');
if (i<0) { return html; }
return html.substring(0,i) + ' ' + property + html.substring(i);
}
function addPopupShortcut(html, key) {
if (!getValueOf('popupShortcutKeys')) { return html; }
var ret= addLinkProperty(html, 'popupkey="'+key+'"');
if (key==' ') { key=popupString('spacebar'); }
return ret.replace(RegExp('^(.*?)(title=")(.*?)(".*)$', 'i'),'$1$2$3 ['+key+']$4');
}
//</NOLITE>
// ENDFILE: shortcutkeys.js
// STARTFILE: diffpreview.js
//<NOLITE>
//lets jump through hoops to find the rev ids we need to retrieve
function loadDiff(article, oldid, diff, navpop) {
navpop.diffData={ oldRev: {}, newRev: {} };
mw.loader.using( 'mediawiki.api' ).then( function() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'compare',
prop: 'ids|title'
};
if(article.title){
params.fromtitle = article.title;
}
switch (diff) {
case 'cur':
switch ( oldid ) {
case null:
case '':
case 'prev':
// this can only work if we have the title
// cur -> prev
params.torelative = 'prev';
break;
default:
params.fromrev = oldid;
params.torelative = 'cur';
break;
}
break;
case 'prev':
if( oldid ) {
params.fromrev = oldid;
} else {
params.fromtitle;
}
params.torelative = 'prev';
break;
case 'next':
params.fromrev = oldid || 0;
params.torelative = 'next';
break;
default:
params.fromrev = oldid || 0;
params.torev = diff || 0;
break;
}
api.get( params ).then( function( data ) {
navpop.diffData.oldRev.revid = data.compare.fromrevid;
navpop.diffData.newRev.revid = data.compare.torevid;
addReviewLink(navpop, 'popupMiscTools');
var go = function() {
pendingNavpopTask(navpop);
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
url += 'revids=' + navpop.diffData.oldRev.revid + '|' + navpop.diffData.newRev.revid;
url += '&prop=revisions&rvprop=ids|timestamp|content';
getPageWithCaching(url, doneDiff, navpop);
return true; // remove hook once run
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_DIFFS'); }
} );
} );
}
// Put a "mark patrolled" link to an element target
// TODO: Allow patrol a revision, as well as a diff
function addReviewLink (navpop, target) {
if (! pg.user.canReview) return;
// If 'newRev' is older than 'oldRev' than it could be confusing, so we do not show the review link.
if (navpop.diffData.newRev.revid <= navpop.diffData.oldRev.revid) return;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
prop: 'info|flagged',
revids: navpop.diffData.oldRev.revid,
formatversion : 2
};
api.get (params).then(function(data){
var stable_revid = data.query.pages[0].flagged && data.query.pages[0].flagged.stable_revid || 0;
// The diff can be reviewed if the old version is the last reviewed version
// TODO: Other possible conditions that we may want to implement instead of this one:
// * old version is patrolled and the new version is not patrolled
// * old version is patrolled and the new version is more recent than the last reviewed version
if (stable_revid == navpop.diffData.oldRev.revid) {
var a = document.createElement('a');
a.innerHTML = popupString('mark patrolled');
a.title=popupString('markpatrolledHint');
a.onclick = function() {
var params = {
action: 'review',
revid: navpop.diffData.newRev.revid,
comment: tprintf('defaultpopupReviewedSummary', [navpop.diffData.oldRev.revid, navpop.diffData.newRev.revid])
};
api.postWithToken('csrf',params).done(function(){
a.style.display = "none";
// TODO: Update current page and other already constructed popups
} ).fail(function(){
alert(popupString('Could not marked this edit as patrolled'));
});
};
setPopupHTML(a, target, navpop.idNumber,null,true);
}
});
}
function doneDiff(download) {
if (!download.owner || !download.owner.diffData) { return; }
var navpop=download.owner;
completedNavpopTask(navpop);
var pages, revisions=[];
try{
// Process the downloads
pages = getJsObj(download.data).query.pages;
for(var i=0; i < pages.length; i++ ) {
revisions = revisions.concat(pages[i].revisions);
}
for(i=0; i< revisions.length; i++){
if(revisions[i].revid == navpop.diffData.oldRev.revid) {
navpop.diffData.oldRev.revision = revisions[i];
} else if (revisions[i].revid == navpop.diffData.newRev.revid) {
navpop.diffData.newRev.revision = revisions[i];
}
}
} catch(someError) {
errlog( 'Could not get diff' );
}
insertDiff(navpop);
}
function rmBoringLines(a,b,context) {
if (typeof context == 'undefined') { context=2; }
// this is fairly slow... i think it's quicker than doing a word-based diff from the off, though
var aa=[], aaa=[];
var bb=[], bbb=[];
var i, j;
// first, gather all disconnected nodes in a and all crossing nodes in a and b
for (i=0; i<a.length; ++i ) {
if(!a[i].paired) { aa[i]=1; }
else if (countCrossings(b,a,i, true)) {
aa[i]=1;
bb[ a[i].row ] = 1;
}
}
// pick up remaining disconnected nodes in b
for (i=0; i<b.length; ++i ) {
if (bb[i]==1) { continue; }
if(!b[i].paired) { bb[i]=1; }
}
// another pass to gather context: we want the neighbours of included nodes which are not yet included
// we have to add in partners of these nodes, but we don't want to add context for *those* nodes in the next pass
for (i=0; i<b.length; ++i) {
if ( bb[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(b.length, i+context); ++j) {
if ( !bb[j] ) { bb[j] = 1; aa[ b[j].row ] = 0.5; }
}
}
}
for (i=0; i<a.length; ++i) {
if ( aa[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(a.length, i+context); ++j) {
if ( !aa[j] ) { aa[j] = 1; bb[ a[j].row ] = 0.5; }
}
}
}
for (i=0; i<bb.length; ++i) {
if (bb[i] > 0) { // it's a row we need
if (b[i].paired) { bbb.push(b[i].text); } // joined; partner should be in aa
else {
bbb.push(b[i]);
}
}
}
for (i=0; i<aa.length; ++i) {
if (aa[i] > 0) { // it's a row we need
if (a[i].paired) { aaa.push(a[i].text); } // joined; partner should be in aa
else {
aaa.push(a[i]);
}
}
}
return { a: aaa, b: bbb};
}
function stripOuterCommonLines(a,b,context) {
var i=0;
while (i<a.length && i < b.length && a[i]==b[i]) { ++i; }
var j=a.length-1; var k=b.length-1;
while ( j>=0 && k>=0 && a[j]==b[k] ) { --j; --k; }
return { a: a.slice(Math.max(0,i - 1 - context), Math.min(a.length+1, j + context+1)),
b: b.slice(Math.max(0,i - 1 - context), Math.min(b.length+1, k + context+1)) };
}
function insertDiff(navpop) {
// for speed reasons, we first do a line-based diff, discard stuff that seems boring, then do a word-based diff
// FIXME: sometimes this gives misleading diffs as distant chunks are squashed together
var oldlines = navpop.diffData.oldRev.revision.content.split('\n');
var newlines = navpop.diffData.newRev.revision.content.split('\n');
var inner=stripOuterCommonLines(oldlines,newlines,getValueOf('popupDiffContextLines'));
oldlines=inner.a; newlines=inner.b;
var truncated=false;
getValueOf('popupDiffMaxLines');
if (oldlines.length > pg.option.popupDiffMaxLines || newlines.length > pg.option.popupDiffMaxLines) {
// truncate
truncated=true;
inner=stripOuterCommonLines(oldlines.slice(0,pg.option.popupDiffMaxLines),
newlines.slice(0,pg.option.popupDiffMaxLines),
pg.option.popupDiffContextLines);
oldlines=inner.a; newlines=inner.b;
}
var lineDiff=diff(oldlines, newlines);
var lines2=rmBoringLines(lineDiff.o, lineDiff.n);
var oldlines2=lines2.a; var newlines2=lines2.b;
var simpleSplit = !String.prototype.parenSplit.isNative;
var html='<hr />';
if (getValueOf('popupDiffDates')) {
html += diffDatesTable(navpop);
html += '<hr />';
}
html += shortenDiffString(
diffString(oldlines2.join('\n'), newlines2.join('\n'), simpleSplit),
getValueOf('popupDiffContextCharacters') ).join('<hr />');
setPopupTipsAndHTML(html.split('\n').join('<br>') +
(truncated ? '<hr /><b>'+popupString('Diff truncated for performance reasons')+'</b>' : '') ,
'popupPreview', navpop.idNumber);
}
function diffDatesTable( navpop ) {
var html='<table class="popup_diff_dates">';
html += diffDatesTableRow( navpop.diffData.newRev.revision, tprintf('New revision'));
html += diffDatesTableRow( navpop.diffData.oldRev.revision, tprintf('Old revision'));
html += '</table>';
return html;
}
function diffDatesTableRow( revision, label ) {
var txt='';
var lastModifiedDate = new Date(revision.timestamp);
var datePrint=getValueOf('popupDiffDatePrinter');
if (typeof lastModifiedDate[datePrint] == 'function') {
var d2 = adjustDate(lastModifiedDate, getTimeOffset());
txt = dayFormat(d2, true) + ' ' + timeFormat(d2, true);
} else {
txt = tprintf('Invalid %s %s', ['popupDiffDatePrinter', datePrint]);
}
var revlink = generalLink({url: mw.config.get('wgScript') + '?oldid='+revision.revid,
text: label, title: label});
return simplePrintf('<tr><td>%s</td><td>%s</td></tr>', [ revlink, txt ]);
}
//</NOLITE>
// ENDFILE: diffpreview.js
// STARTFILE: links.js
//<NOLITE>
/////////////////////
// LINK GENERATION //
/////////////////////
// titledDiffLink --> titledWikiLink --> generalLink
// wikiLink --> titledWikiLink --> generalLink
// editCounterLink --> generalLink
// TODO Make these functions return Element objects, not just raw HTML strings.
function titledDiffLink(l) { // article, text, title, from, to) {
return titledWikiLink({article: l.article, action: l.to + '&oldid=' + l.from,
newWin: l.newWin,
noPopup: l.noPopup,
text: l.text, title: l.title,
/* hack: no oldid here */
actionName: 'diff'});
}
function wikiLink(l) {
//{article:article, action:action, text:text, oldid, newid}) {
if (! (typeof l.article == typeof {} &&
typeof l.action == typeof '' &&
typeof l.text==typeof '')) return null;
if (typeof l.oldid == 'undefined') { l.oldid=null; }
var savedOldid = l.oldid;
if (!/^(edit|view|revert|render)$|^raw/.test(l.action)) { l.oldid=null; }
var hint=popupString(l.action + 'Hint'); // revertHint etc etc etc
var oldidData=[l.oldid, safeDecodeURI(l.article)];
var revisionString = tprintf('revision %s of %s', oldidData);
log('revisionString='+revisionString);
switch (l.action) {
case 'edit§ion=new': hint = popupString('newSectionHint'); break;
case 'edit&undo=':
if (l.diff && l.diff != 'prev' && savedOldid ) {
l.action += l.diff + '&undoafter=' + savedOldid;
} else if (savedOldid) {
l.action += savedOldid;
}
hint = popupString('undoHint');
break;
case 'raw&ctype=text/css': hint=popupString('rawHint'); break;
case 'revert':
var p=parseParams(pg.current.link.href);
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=' + revertSummary(l.oldid, p.diff);
if (p.diff=='prev') {
l.action += '&direction=prev';
revisionString = tprintf('the revision prior to revision %s of %s', oldidData);
}
if (getValueOf('popupRevertSummaryPrompt')) { l.action += '&autosummaryprompt=true'; }
if (getValueOf('popupMinorReverts')) { l.action += '&autominor=true'; }
log('revisionString is now '+revisionString);
break;
case 'nullEdit':
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=null';
break;
case 'historyfeed':
l.action='history&feed=rss';
break;
case 'markpatrolled':
l.action='markpatrolled&rcid='+l.rcid;
}
if (hint) {
if (l.oldid) {
hint = simplePrintf(hint, [revisionString]);
}
else {
hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
}
}
else {
hint = safeDecodeURI(l.article + '&action=' + l.action) + (l.oldid) ? '&oldid='+l.oldid : '';
}
return titledWikiLink({article: l.article, action: l.action, text: l.text, newWin:l.newWin,
title: hint, oldid: l.oldid, noPopup: l.noPopup, onclick: l.onclick});
}
function revertSummary(oldid, diff) {
var ret='';
if (diff == 'prev') {
ret=getValueOf('popupQueriedRevertToPreviousSummary');
} else { ret = getValueOf('popupQueriedRevertSummary'); }
return ret + '&autorv=' + oldid;
}
function titledWikiLink(l) {
// possible properties of argument:
// article, action, text, title, oldid, actionName, className, noPopup
// oldid = null is fine here
// article and action are mandatory args
if (typeof l.article == 'undefined' || typeof l.action=='undefined') {
errlog('got undefined article or action in titledWikiLink');
return null;
}
var base = pg.wiki.titlebase + l.article.urlString();
var url=base;
if (typeof l.actionName=='undefined' || !l.actionName) { l.actionName='action'; }
// no need to add &action=view, and this confuses anchors
if (l.action != 'view') { url = base + '&' + l.actionName + '=' + l.action; }
if (typeof l.oldid!='undefined' && l.oldid) { url+='&oldid='+l.oldid; }
var cssClass=pg.misc.defaultNavlinkClassname;
if (typeof l.className!='undefined' && l.className) { cssClass=l.className; }
return generalNavLink({url: url, newWin: l.newWin,
title: (typeof l.title != 'undefined') ? l.title : null,
text: (typeof l.text!='undefined')?l.text:null,
className: cssClass, noPopup:l.noPopup, onclick:l.onclick});
}
pg.fn.getLastContrib = function getLastContrib(wikipage, newWin) {
getHistoryInfo(wikipage, function(x) {
processLastContribInfo(x, {page: wikipage, newWin: newWin});
});
};
function processLastContribInfo(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: an odd thing happened. Please retry.'); return; }
if(!info.firstNewEditor) {
alert(tprintf('Only found one editor: %s made %s edits', [info.edits[0].editor,info.edits.length]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+info.firstNewEditor.oldid;
displayUrl(newUrl, stuff.newWin);
}
pg.fn.getDiffSinceMyEdit = function getDiffSinceMyEdit(wikipage, newWin) {
getHistoryInfo(wikipage, function(x){
processDiffSinceMyEdit(x, {page: wikipage, newWin: newWin});
});
};
function processDiffSinceMyEdit(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: something fishy happened. Please try again.'); return; }
var friendlyName=stuff.page.split('_').join(' ');
if(!info.myLastEdit) {
alert(tprintf('Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
[info.userName, getValueOf('popupHistoryLimit'), friendlyName]));
return;
}
if(info.myLastEdit.index === 0) {
alert(tprintf("%s seems to be the last editor to the page %s", [info.userName, friendlyName]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+ info.myLastEdit.oldid;
displayUrl(newUrl, stuff.newWin);
}
function displayUrl(url, newWin){
if(newWin) { window.open(url); }
else { document.location=url; }
}
pg.fn.purgePopups = function purgePopups() {
processAllPopups(true);
setupCache(); // deletes all cached items (not browser cached, though...)
pg.option={};
abortAllDownloads();
};
function processAllPopups(nullify, banish) {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
if (!pg.current.links[i].navpopup) { continue; }
if (nullify || banish) pg.current.links[i].navpopup.banish();
pg.current.links[i].simpleNoMore=false;
if (nullify) pg.current.links[i].navpopup=null;
}
}
pg.fn.disablePopups = function disablePopups(){
processAllPopups(false, true);
setupTooltips(null, true);
};
pg.fn.togglePreviews = function togglePreviews() {
processAllPopups(true, true);
pg.option.simplePopups=!pg.option.simplePopups;
abortAllDownloads();
};
function magicWatchLink(l) {
//Yuck!! Would require a thorough redesign to add this as a click event though ...
l.onclick = simplePrintf( 'pg.fn.modifyWatchlist(\'%s\',\'%s\');return false;', [l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), this.id] );
return wikiLink(l);
}
pg.fn.modifyWatchlist = function modifyWatchlist(title, action) {
var reqData = {
'action': 'watch',
'formatversion': 2,
'titles': title,
'uselang': mw.config.get('wgUserLanguage')
};
if ( action === 'unwatch' ) reqData.unwatch = true;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
// Load the Addedwatchtext or Removedwatchtext message and show it
var mwTitle = mw.Title.newFromText( title );
var messageName;
if ( mwTitle && mwTitle.getNamespaceId() > 0 && mwTitle.getNamespaceId() % 2 === 1 ) {
messageName = action === 'watch' ? 'addedwatchtext-talk' : 'removedwatchtext-talk';
} else {
messageName = action === 'watch' ? 'addedwatchtext' : 'removedwatchtext';
}
$.when(
api.postWithToken( 'watch', reqData ),
mw.loader.using( [ 'mediawiki.api', 'mediawiki.jqueryMsg' ] ).then( function () {
return api.loadMessagesIfMissing( [ messageName ] );
} )
).done( function () {
mw.notify( mw.message( messageName, title ).parseDom() );
} );
};
function magicHistoryLink(l) {
// FIXME use onclick change href trick to sort this out instead of window.open
var jsUrl='', title='', onClick='';
switch(l.id) {
case 'lastContrib':
onClick=simplePrintf('pg.fn.getLastContrib(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('lastContribHint');
break;
case 'sinceMe':
onClick=simplePrintf('pg.fn.getDiffSinceMyEdit(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('sinceMeHint');
break;
}
jsUrl = 'javascript:' + onClick; // jshint ignore:line
onClick += ';return false;';
return generalNavLink({url: jsUrl, newWin: false, // can't have new windows with JS links, I think
title: title, text: l.text, noPopup: l.noPopup, onclick: onClick });
}
function popupMenuLink(l) {
var jsUrl=simplePrintf('javascript:pg.fn.%s()', [l.id]); // jshint ignore:line
var title=popupString(simplePrintf('%sHint', [l.id]));
var onClick=simplePrintf('pg.fn.%s();return false;', [l.id]);
return generalNavLink({url: jsUrl, newWin:false, title:title, text:l.text, noPopup:l.noPopup, onclick: onClick});
}
function specialLink(l) {
// properties: article, specialpage, text, sep
if (typeof l.specialpage=='undefined'||!l.specialpage) return null;
var base = pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId]+':'+l.specialpage;
if (typeof l.sep == 'undefined' || l.sep === null) l.sep='&target=';
var article=l.article.urlString({keepSpaces: l.specialpage=='Search'});
var hint=popupString(l.specialpage+'Hint');
switch (l.specialpage) {
case 'Log':
switch (l.sep) {
case '&user=': hint=popupString('userLogHint'); break;
case '&type=block&page=': hint=popupString('blockLogHint'); break;
case '&page=': hint=popupString('pageLogHint'); break;
case '&type=protect&page=': hint=popupString('protectLogHint'); break;
case '&type=delete&page=': hint=popupString('deleteLogHint'); break;
default: log('Unknown log type, sep=' + l.sep); hint='Missing hint (FIXME)';
}
break;
case 'PrefixIndex': article += '/'; break;
}
if (hint) hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
else hint = safeDecodeURI(l.specialpage+':'+l.article) ;
var url = base + l.sep + article;
return generalNavLink({url: url, title: hint, text: l.text, newWin:l.newWin, noPopup:l.noPopup});
}
function generalLink(l) {
// l.url, l.text, l.title, l.newWin, l.className, l.noPopup, l.onclick
if (typeof l.url=='undefined') return null;
// only quotation marks in the url can screw us up now... I think
var url=l.url.split('"').join('%22');
var ret='<a href="' + url + '"';
if (typeof l.title!='undefined' && l.title) { ret += ' title="' + pg.escapeQuotesHTML(l.title) + '"'; }
if (typeof l.onclick!='undefined' && l.onclick) { ret += ' onclick="' + pg.escapeQuotesHTML(l.onclick) + '"'; }
if (l.noPopup) { ret += ' noPopup=1'; }
var newWin;
if (typeof l.newWin=='undefined' || l.newWin === null) { newWin=getValueOf('popupNewWindows'); }
else { newWin=l.newWin; }
if (newWin) { ret += ' target="_blank"'; }
if (typeof l.className!='undefined'&&l.className) { ret+=' class="'+l.className+'"'; }
ret += '>';
if (typeof l.text==typeof '') { ret+= l.text; }
ret +='</a>';
return ret;
}
function appendParamsToLink(linkstr, params) {
var sp=linkstr.parenSplit(RegExp('(href="[^"]+?)"', 'i'));
if (sp.length<2) return null;
var ret=sp.shift() + sp.shift();
ret += '&' + params + '"';
ret += sp.join('');
return ret;
}
function changeLinkTargetLink(x) { // newTarget, text, hint, summary, clickButton, minor, title (optional), alsoChangeLabel {
if (x.newTarget) {
log ('changeLinkTargetLink: newTarget=' + x.newTarget);
}
if (x.oldTarget !== decodeURIComponent( x.oldTarget ) ) {
log ('This might be an input problem: ' + x.oldTarget );
}
// FIXME: first character of page title as well as namespace should be case insensitive
// eg [[category:X1]] and [[Category:X1]] are equivalent
// this'll break if charAt(0) is nasty
var cA = mw.util.escapeRegExp(x.oldTarget);
var chs = cA.charAt(0).toUpperCase();
chs='['+chs + chs.toLowerCase()+']';
var currentArticleRegexBit=chs+cA.substring(1);
currentArticleRegexBit=currentArticleRegexBit
.split(RegExp('(?:[_ ]+|%20)', 'g')).join('(?:[_ ]+|%20)')
.split('\\(').join('(?:%28|\\()')
.split('\\)').join('(?:%29|\\))'); // why does this need to match encoded strings ? links in the document ?
// leading and trailing space should be ignored, and anchor bits optional:
currentArticleRegexBit = '\\s*(' + currentArticleRegexBit + '(?:#[^\\[\\|]*)?)\\s*';
// e.g. Computer (archaic) -> \s*([Cc]omputer[_ ](?:%2528|\()archaic(?:%2528|\)))\s*
// autoedit=s~\[\[([Cc]ad)\]\]~[[Computer-aided%20design|$1]]~g;s~\[\[([Cc]AD)[|]~[[Computer-aided%20design|~g
var title=x.title || mw.config.get('wgPageName').split('_').join(' ');
var lk=titledWikiLink({article: new Title(title), newWin:x.newWin,
action: 'edit',
text: x.text,
title: x.hint,
className: 'popup_change_title_link'
});
var cmd='';
if (x.newTarget) {
// escape '&' and other nasties
var t = x.newTarget;
var s = mw.util.escapeRegExp(x.newTarget);
if (x.alsoChangeLabel) {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + ']]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
} else {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + '|$1]]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
}
} else {
cmd += 's~\\[\\['+currentArticleRegexBit+'\\]\\]~$1~g;';
cmd += 's~\\[\\['+currentArticleRegexBit+'[|](.*?)\\]\\]~$2~g';
}
// Build query
cmd = 'autoedit=' + encodeURIComponent ( cmd );
cmd += '&autoclick='+ encodeURIComponent( x.clickButton ) + '&actoken=' + encodeURIComponent( autoClickToken() );
cmd += ( x.minor === null ) ? '' : '&autominor='+ encodeURIComponent( x.minor );
cmd += ( x.watch === null ) ? '' : '&autowatch='+ encodeURIComponent( x.watch );
cmd += '&autosummary='+encodeURIComponent(x.summary);
cmd += '&autoimpl='+encodeURIComponent( popupString('autoedit_version') );
return appendParamsToLink(lk, cmd);
}
function redirLink(redirMatch, article) {
// NB redirMatch is in wikiText
var ret='';
if (getValueOf('popupAppendRedirNavLinks') && getValueOf('popupNavLinks')) {
ret += '<hr />';
if (getValueOf('popupFixRedirs') && typeof autoEdit != 'undefined' && autoEdit) {
ret += popupString('Redirects to: (Fix ');
log('redirLink: newTarget=' + redirMatch);
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target'),
hint: popupString('Fix this redirect, changing just the link target'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages')
}), 'R');
ret += popupString(' or ');
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target & label'),
hint: popupString('Fix this redirect, changing the link target and label'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages'),
alsoChangeLabel: true
}), 'R');
ret += popupString(')');
}
else ret += popupString('Redirects') + popupString(' to ');
return ret;
}
else return '<br> ' + popupString('Redirects') + popupString(' to ') +
titledWikiLink({article: new Title().fromWikiText(redirMatch), action: 'view', /* FIXME: newWin */
text: safeDecodeURI(redirMatch), title: popupString('Bypass redirect')});
}
function arinLink(l) {
if (!saneLinkCheck(l)) { return null; }
if ( ! l.article.isIpUser() || ! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
return generalNavLink({url:'http://ws.arin.net/cgi-bin/whois.pl?queryinput=' + encodeURIComponent(uN), newWin:l.newWin,
title: tprintf('Look up %s in ARIN whois database', [uN]),
text: l.text, noPopup:1});
}
function toolDbName(cookieStyle) {
var ret = mw.config.get('wgDBname');
if (!cookieStyle) { ret+= '_p'; }
return ret;
}
function saneLinkCheck(l) {
if (typeof l.article != typeof {} || typeof l.text != typeof '') { return false; }
return true;
}
function editCounterLink(l) {
if(!saneLinkCheck(l)) return null;
if (! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
var tool=getValueOf('popupEditCounterTool');
var url;
var defaultToolUrl='//tools.wmflabs.org/supercount/index.php?user=$1&project=$2.$3';
switch(tool) {
case 'custom':
url=simplePrintf(getValueOf('popupEditCounterUrl'), [ encodeURIComponent(uN), toolDbName() ]);
break;
case 'soxred': // no longer available
case 'kate': // no longer available
case 'interiot':// no longer available
/* fall through */
case 'supercount':
default:
var theWiki=pg.wiki.hostname.split('.');
url=simplePrintf(defaultToolUrl, [ encodeURIComponent(uN), theWiki[0], theWiki[1] ]);
}
return generalNavLink({url:url, title: tprintf('editCounterLinkHint', [uN]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function globalSearchLink(l) {
if(!saneLinkCheck(l)) return null;
var base='http://vs.aka-online.de/cgi-bin/globalwpsearch.pl?timeout=120&search=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + article, newWin:l.newWin,
title: tprintf('globalSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function googleLink(l) {
if(!saneLinkCheck(l)) return null;
var base='https://www.google.com/search?q=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + '%22' + article + '%22', newWin:l.newWin,
title: tprintf('googleSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function editorListLink(l) {
if(!saneLinkCheck(l)) return null;
var article= l.article.articleFromTalkPage() || l.article;
var url='https://xtools.wmflabs.org/articleinfo/' +
encodeURI( pg.wiki.hostname ) + '/' +
article.urlString() +
'?uselang=' + mw.config.get('wgUserLanguage');
return generalNavLink({url:url,
title: tprintf('editorListHint', [article]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function generalNavLink(l) {
l.className = (l.className === null) ? 'popupNavLink' : l.className;
return generalLink(l);
}
//////////////////////////////////////////////////
// magic history links
//
function getHistoryInfo(wikipage, whatNext) {
log('getHistoryInfo');
getHistory(wikipage, whatNext ? function(d){whatNext(processHistory(d));} : processHistory);
}
// FIXME eliminate pg.idNumber ... how? :-(
function getHistory(wikipage, onComplete) {
log('getHistory');
var url = pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&prop=revisions&titles=' +
new Title(wikipage).urlString() + '&rvlimit=' + getValueOf('popupHistoryLimit');
log('getHistory: url='+url);
return startDownload(url, pg.idNumber+'history', onComplete);
}
function processHistory(download) {
var jsobj = getJsObj(download.data);
try {
var revisions = anyChild(jsobj.query.pages).revisions;
var edits=[];
for (var i=0; i<revisions.length; ++i) {
edits.push({ oldid: revisions[i].revid, editor: revisions[i].user });
}
log('processed ' + edits.length + ' edits');
return finishProcessHistory( edits, mw.config.get('wgUserName') );
} catch (someError) {
log('Something went wrong with JSON business');
return finishProcessHistory([]);
}
}
function finishProcessHistory(edits, userName) {
var histInfo={};
histInfo.edits=edits;
histInfo.userName=userName;
for (var i=0; i<edits.length; ++i) {
if (typeof histInfo.myLastEdit === 'undefined' && userName && edits[i].editor==userName) {
histInfo.myLastEdit={index: i, oldid: edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
if (typeof histInfo.firstNewEditor === 'undefined' && edits[i].editor != edits[0].editor) {
histInfo.firstNewEditor={index:i, oldid:edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
}
//pg.misc.historyInfo=histInfo;
return histInfo;
}
//</NOLITE>
// ENDFILE: links.js
// STARTFILE: options.js
//////////////////////////////////////////////////
// options
// check for existing value, else use default
function defaultize(x) {
if (pg.option[x]===null || typeof pg.option[x]=='undefined') {
if (typeof window[x] != 'undefined' ) pg.option[x]=window[x];
else pg.option[x]=pg.optionDefault[x];
}
}
function newOption(x, def) {
pg.optionDefault[x]=def;
}
function setDefault(x, def) {
return newOption(x, def);
}
function getValueOf(varName) {
defaultize(varName);
return pg.option[varName];
}
/*eslint-disable */
function useDefaultOptions() { // for testing
for (var p in pg.optionDefault) {
pg.option[p]=pg.optionDefault[p];
if (typeof window[p]!='undefined') { delete window[p]; }
}
}
/*eslint-enable */
function setOptions() {
// user-settable parameters and defaults
var userIsSysop = false;
if ( mw.config.get('wgUserGroups') ) {
for ( var g = 0; g < mw.config.get('wgUserGroups').length; ++g ) {
if ( mw.config.get('wgUserGroups')[g] == "bảo quản viên" )
userIsSysop = true;
}
}
// Basic options
newOption('popupDelay', 0.5);
newOption('popupHideDelay', 0.5);
newOption('simplePopups', false);
newOption('popupStructure', 'shortmenus'); // see later - default for popupStructure is 'original' if simplePopups is true
newOption('popupActionsMenu', true);
newOption('popupSetupMenu', true);
newOption('popupAdminLinks', userIsSysop);
newOption('popupShortcutKeys', false);
newOption('popupHistoricalLinks', true);
newOption('popupOnlyArticleLinks', true);
newOption('removeTitles', true);
newOption('popupMaxWidth', 350);
newOption('popupSimplifyMainLink', true);
newOption('popupAppendRedirNavLinks', true);
newOption('popupTocLinks', false);
newOption('popupSubpopups', true);
newOption('popupDragHandle', false /* 'popupTopLinks'*/);
newOption('popupLazyPreviews', true);
newOption('popupLazyDownloads', true);
newOption('popupAllDabsStubs', false);
newOption('popupDebugging', false);
newOption('popupActiveNavlinks', true);
newOption('popupModifier', false); // ctrl, shift, alt or meta
newOption('popupModifierAction', 'enable'); // or 'disable'
newOption('popupDraggable', true);
newOption('popupReview', false);
//<NOLITE>
// images
newOption('popupImages', true);
newOption('imagePopupsForImages', true);
newOption('popupNeverGetThumbs', false);
//newOption('popupImagesToggleSize', true);
newOption('popupThumbAction', 'imagepage'); //'sizetoggle');
newOption('popupImageSize', 60);
newOption('popupImageSizeLarge', 200);
// redirs, dabs, reversion
newOption('popupFixRedirs', false);
newOption('popupRedirAutoClick', 'wpDiff');
newOption('popupFixDabs', false);
newOption('popupDabsAutoClick', 'wpDiff');
newOption('popupRevertSummaryPrompt', false);
newOption('popupMinorReverts', false);
newOption('popupRedlinkRemoval', false);
newOption('popupRedlinkAutoClick', 'wpDiff');
newOption('popupWatchDisambiggedPages', null);
newOption('popupWatchRedirredPages', null);
newOption('popupDabWiktionary', 'last');
// navlinks
newOption('popupNavLinks', true);
newOption('popupNavLinkSeparator', ' ⋅ ');
newOption('popupLastEditLink', true);
newOption('popupEditCounterTool', 'supercount');
newOption('popupEditCounterUrl', '');
//</NOLITE>
// previews etc
newOption('popupPreviews', true);
newOption('popupSummaryData', true);
newOption('popupMaxPreviewSentences', 9);
newOption('popupMaxPreviewCharacters', 900);
newOption('popupLastModified', true);
newOption('popupPreviewKillTemplates', true);
newOption('popupPreviewRawTemplates', true);
newOption('popupPreviewFirstParOnly', false);
newOption('popupPreviewCutHeadings', false);
newOption('popupPreviewButton', false);
newOption('popupPreviewButtonEvent', 'click');
//<NOLITE>
// diffs
newOption('popupPreviewDiffs', true);
newOption('popupDiffMaxLines', 100);
newOption('popupDiffContextLines', 2);
newOption('popupDiffContextCharacters', 40);
newOption('popupDiffDates', true);
newOption('popupDiffDatePrinter', 'toLocaleString');
// edit summaries. God, these are ugly.
newOption('popupReviewedSummary', popupString('defaultpopupReviewedSummary') );
newOption('popupFixDabsSummary', popupString('defaultpopupFixDabsSummary') );
newOption('popupExtendedRevertSummary', popupString('defaultpopupExtendedRevertSummary') );
newOption('popupRevertSummary', popupString('defaultpopupRevertSummary') );
newOption('popupRevertToPreviousSummary', popupString('defaultpopupRevertToPreviousSummary') );
newOption('popupQueriedRevertSummary', popupString('defaultpopupQueriedRevertSummary') );
newOption('popupQueriedRevertToPreviousSummary', popupString('defaultpopupQueriedRevertToPreviousSummary') );
newOption('popupFixRedirsSummary', popupString('defaultpopupFixRedirsSummary') );
newOption('popupRedlinkSummary', popupString('defaultpopupRedlinkSummary') );
newOption('popupRmDabLinkSummary', popupString('defaultpopupRmDabLinkSummary') );
//</NOLITE>
// misc
newOption('popupHistoryLimit', 50);
//<NOLITE>
newOption('popupFilters', [popupFilterStubDetect, popupFilterDisambigDetect,
popupFilterPageSize, popupFilterCountLinks,
popupFilterCountImages, popupFilterCountCategories,
popupFilterLastModified]);
newOption('extraPopupFilters', []);
newOption('popupOnEditSelection', 'cursor');
newOption('popupPreviewHistory', true);
newOption('popupImageLinks', true);
newOption('popupCategoryMembers', true);
newOption('popupUserInfo', true);
newOption('popupHistoryPreviewLimit', 25);
newOption('popupContribsPreviewLimit',25);
newOption('popupRevDelUrl', '//en.wikipedia.org/wiki/Wikipedia:Revision_deletion');
newOption('popupShowGender', true);
//</NOLITE>
// new windows
newOption('popupNewWindows', false);
newOption('popupLinksNewWindow', {'lastContrib': true, 'sinceMe': true});
// regexps
newOption('popupDabRegexp', '(\\{\\{\\s*disambig(?!uation needed)|disambig(uation|)\\s*\\}\\}|disamb\\s*\\}\\}|dab\\s*\\}\\})|\\{\\{\\s*(((geo|hn|road?|school|number)dis)|[234][lc][acw]|(road|ship)index)(\\s*[|][^}]*)?\\s*[}][}]|is a .*disambiguation.*page');
newOption('popupAnchorRegexp', 'anchors?'); //how to identify an anchors template
newOption('popupStubRegexp', '(sect)?stub[}][}]|This .*-related article is a .*stub');
newOption('popupImageVarsRegexp', 'image|image_(?:file|skyline|name|flag|seal)|cover|badge|logo');
}
// ENDFILE: options.js
// STARTFILE: strings.js
//<NOLITE>
//////////////////////////////////////////////////
// Translatable strings
//////////////////////////////////////////////////
//
// See instructions at
// https://en.wikipedia.org/wiki/Wikipedia:Tools/Navigation_popups/Translation
pg.string = {
/////////////////////////////////////
// summary data, searching etc.
/////////////////////////////////////
'article': 'article',
'category': 'category',
'categories': 'categories',
'image': 'image',
'images': 'images',
'stub': 'stub',
'section stub': 'section stub',
'Empty page': 'Empty page',
'kB': 'kB',
'bytes': 'bytes',
'day': 'day',
'days': 'days',
'hour': 'hour',
'hours': 'hours',
'minute': 'minute',
'minutes': 'minutes',
'second': 'second',
'seconds': 'seconds',
'week': 'week',
'weeks': 'weeks',
'search': 'search',
'SearchHint': 'Find English Wikipedia articles containing %s',
'web': 'web',
'global': 'global',
'globalSearchHint': 'Search across Wikipedias in different languages for %s',
'googleSearchHint': 'Google for %s',
/////////////////////////////////////
// article-related actions and info
// (some actions also apply to user pages)
/////////////////////////////////////
'tác vụ': 'tác vụ', ///// view articles and view talk
'popupsMenu': 'popups',
'togglePreviewsHint': 'Toggle preview generation in popups on this page',
'enable previews': 'enable previews',
'disable previews': 'disable previews',
'toggle previews': 'toggle previews',
'show preview': 'show preview',
'reset': 'reset',
'more...': 'more...',
'disable': 'disable popups',
'disablePopupsHint': 'Disable popups on this page. Reload page to re-enable.',
'historyfeedHint': 'RSS feed of recent changes to this page',
'purgePopupsHint': 'Reset popups, clearing all cached popup data.',
'PopupsHint': 'Reset popups, clearing all cached popup data.',
'spacebar': 'space',
'view': 'view',
'view article': 'view article',
'viewHint': 'Go to %s',
'talk': 'talk',
'talk page': 'talk page',
'this revision': 'this revision',
'revision %s of %s': 'revision %s of %s',
'Revision %s of %s': 'Revision %s of %s',
'the revision prior to revision %s of %s': 'the revision prior to revision %s of %s',
'Toggle image size': 'Click to toggle image size',
'del': 'del', ///// delete, protect, move
'delete': 'delete',
'deleteHint': 'Delete %s',
'undeleteShort': 'un',
'UndeleteHint': 'Show the deletion history for %s',
'protect': 'protect',
'protectHint': 'Restrict editing rights to %s',
'unprotectShort': 'un',
'unprotectHint': 'Allow %s to be edited by anyone again',
'send thanks': 'send thanks',
'ThanksHint': 'Send a thank you notification to this user',
'move': 'move',
'move page': 'move page',
'MovepageHint': 'Change the title of %s',
'sửa': 'sửa', ///// edit articles and talk
'edit article': 'edit article',
'editHint': 'Change the content of %s',
'edit talk': 'edit talk',
'new': 'new',
'new topic': 'new topic',
'newSectionHint': 'Start a new section on %s',
'null edit': 'null edit',
'nullEditHint': 'Submit an edit to %s, making no changes ',
'hist': 'hist', ///// history, diffs, editors, related
'lịch sử': 'lịch sử',
'historyHint': 'List the changes made to %s',
'last': 'prev', // For labelling the previous revision in history pages; the key is "last" for backwards compatibility
'lastEdit': 'lastEdit',
'mark patrolled': 'đánh dấu tuần tra',
'markpatrolledHint': 'Mark this edit as patrolled',
'Could not marked this edit as patrolled': 'Could not marked this edit as patrolled',
'show last edit': 'lần sửa đổi gần nhất',
'Show the last edit': 'Show the effects of the most recent change',
'lastContrib': 'lastContrib',
'last set of edits': 'latest edits',
'lastContribHint': 'Show the net effect of changes made by the last editor',
'cur': 'cur',
'diffCur': 'diffCur',
'Show changes since revision %s': 'Show changes since revision %s',
'%s old': '%s old', // as in 4 weeks old
'oldEdit': 'oldEdit',
'purge': 'purge',
'purgeHint': 'Demand a fresh copy of %s',
'raw': 'source',
'rawHint': 'Download the source of %s',
'render': 'simple',
'renderHint': 'Show a plain HTML version of %s',
'Show the edit made to get revision': 'Show the edit made to get revision',
'sinceMe': 'sinceMe',
'changes since mine': 'diff my edit',
'sinceMeHint': 'Show changes since my last edit',
'Couldn\'t find an edit by %s\nin the last %s edits to\n%s': 'Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
'eds': 'eds',
'editors': 'editors',
'editorListHint': 'List the users who have edited %s',
'related': 'related',
'relatedChanges': 'relatedChanges',
'related changes': 'related changes',
'RecentchangeslinkedHint': 'Show changes in articles related to %s',
'editOld': 'editOld', ///// edit old version, or revert
'rv': 'rv',
'revert': 'revert',
'revertHint': 'Revert to %s',
'defaultpopupReviewedSummary': 'Accepted by reviewing the [[Special:diff/%s/%s|difference]] between this version and previously accepted version using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRedlinkSummary': 'Removing link to empty page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixDabsSummary': 'Disambiguate [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixRedirsSummary': 'Redirect bypass from [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupExtendedRevertSummary': 'Revert to revision dated %s by %s, oldid %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertToPreviousSummary': 'Revert to the revision prior to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertSummary': 'Revert to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertToPreviousSummary': 'Revert to the revision prior to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertSummary': 'Revert to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRmDabLinkSummary': 'Remove link to dab page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'Redirects': 'Redirects', // as in Redirects to ...
' to ': ' to ', // as in Redirects to ...
'Bypass redirect': 'Bypass redirect',
'Fix this redirect': 'Fix this redirect',
'disambig': 'disambig', ///// add or remove dab etc.
'disambigHint': 'Disambiguate this link to [[%s]]',
'Click to disambiguate this link to:': 'Click to disambiguate this link to:',
'remove this link': 'remove this link',
'remove all links to this page from this article': 'remove all links to this page from this article',
'remove all links to this disambig page from this article': 'remove all links to this disambig page from this article',
'mainlink': 'mainlink', ///// links, watch, unwatch
'wikiLink': 'wikiLink',
'wikiLinks': 'wikiLinks',
'links here': 'links here',
'whatLinksHere': 'whatLinksHere',
'what links here': 'what links here',
'WhatlinkshereHint': 'List the pages that are hyperlinked to %s',
'unwatchShort': 'un',
'watchThingy': 'watch', // called watchThingy because {}.watch is a function
'watchHint': 'Add %s to my watchlist',
'unwatchHint': 'Remove %s from my watchlist',
'Only found one editor: %s made %s edits': 'Only found one editor: %s made %s edits',
'%s seems to be the last editor to the page %s': '%s seems to be the last editor to the page %s',
'rss': 'rss',
/////////////////////////////////////
// diff previews
/////////////////////////////////////
'Diff truncated for performance reasons': 'Diff truncated for performance reasons',
'Old revision': 'Old revision',
'New revision': 'New revision',
'Something went wrong :-(': 'Something went wrong :-(',
'Empty revision, maybe non-existent': 'Empty revision, maybe non-existent',
'Unknown date': 'Unknown date',
/////////////////////////////////////
// other special previews
/////////////////////////////////////
'Empty category': 'Empty category',
'Category members (%s shown)': 'Category members (%s shown)',
'No image links found': 'No image links found',
'File links': 'File links',
'No image found': 'No image found',
'Image from Commons': 'Image from Commons',
'Description page': 'Description page',
'Alt text:': 'Alt text:',
'revdel':'Hidden revision',
/////////////////////////////////////
// user-related actions and info
/////////////////////////////////////
'user': 'user', ///// user page, talk, email, space
'user page': 'user page',
'user talk': 'user talk',
'edit user talk': 'edit user talk',
'leave comment': 'leave comment',
'email': 'email',
'email user': 'email user',
'EmailuserHint': 'Send an email to %s',
'space': 'space', // short form for userSpace link
'PrefixIndexHint': 'Show pages in the userspace of %s',
'count': 'count', ///// contributions, log
'edit counter': 'edit counter',
'editCounterLinkHint': 'Count the contributions made by %s',
'contribs': 'contribs',
'contributions': 'contributions',
'deletedContribs': 'deleted contributions',
'DeletedcontributionsHint': 'List deleted edits made by %s',
'ContributionsHint': 'List the contributions made by %s',
'log': 'log',
'user log': 'user log',
'userLogHint': 'Show %s\'s user log',
'arin': 'ARIN lookup', ///// ARIN lookup, block user or IP
'Look up %s in ARIN whois database': 'Look up %s in the ARIN whois database',
'unblockShort': 'un',
'block': 'block',
'block user': 'block user',
'IpblocklistHint': 'Unblock %s',
'BlockipHint': 'Prevent %s from editing',
'block log': 'block log',
'blockLogHint': 'Show the block log for %s',
'protectLogHint': 'Show the protection log for %s',
'pageLogHint': 'Show the page log for %s',
'deleteLogHint': 'Show the deletion log for %s',
'Invalid %s %s': 'The option %s is invalid: %s',
'No backlinks found': 'No backlinks found',
' and more': ' and more',
'undo': 'undo',
'undoHint': 'undo this edit',
'Download preview data': 'Download preview data',
'Invalid or IP user': 'Invalid or IP user',
'Not a registered username': 'Not a registered username',
'CẤM': 'CẤM',
'Has blocks': 'Has blocks',
' sửa đổi từ: ': ' sửa đổi từ: ',
'sửa đổi lần cuối vào ': 'sửa đổi lần cuối vào ',
/////////////////////////////////////
// Autoediting
/////////////////////////////////////
'Enter a non-empty edit summary or press cancel to abort': 'Enter a non-empty edit summary or press cancel to abort',
'Failed to get revision information, please edit manually.\n\n': 'Failed to get revision information, please edit manually.\n\n',
'The %s button has been automatically clicked. Please wait for the next page to load.': 'The %s button has been automatically clicked. Please wait for the next page to load.',
'Could not find button %s. Please check the settings in your javascript file.': 'Could not find button %s. Please check the settings in your javascript file.',
/////////////////////////////////////
// Popups setup
/////////////////////////////////////
'Open full-size image': 'Open full-size image',
'zxy': 'zxy',
'autoedit_version': 'np20140416'
};
function popupString(str) {
if (typeof popupStrings != 'undefined' && popupStrings && popupStrings[str]) { return popupStrings[str]; }
if (pg.string[str]) { return pg.string[str]; }
return str;
}
function tprintf(str,subs) {
if (typeof subs != typeof []) { subs = [subs]; }
return simplePrintf(popupString(str), subs);
}
//</NOLITE>
// ENDFILE: strings.js
// STARTFILE: run.js
////////////////////////////////////////////////////////////////////
// Run things
////////////////////////////////////////////////////////////////////
// For some reason popups requires a fully loaded page jQuery.ready(...) causes problems for some.
// The old addOnloadHook did something similar to the below
if (document.readyState=="complete")
autoEdit(); //will setup popups
else
$( window ).on( 'load', autoEdit );
// Support for MediaWiki's live preview, VisualEditor's saves and Echo's flyout.
( function () {
var once = true;
function dynamicContentHandler( $content ) {
// Try to detect the hook fired on initial page load and disregard
// it, we already hook to onload (possibly to different parts of
// page - it's configurable) and running twice might be bad. Ugly…
if ( $content.attr( 'id' ) == 'mw-content-text' ) {
if ( once ) {
once = false;
return;
}
}
function registerHooksForVisibleNavpops () {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
var navpop = pg.current.links[i].navpopup;
if (!navpop || !navpop.isVisible()) { continue; }
Navpopup.tracker.addHook(posCheckerHook(navpop));
}
}
function doIt () {
registerHooksForVisibleNavpops();
$content.each( function () {
this.ranSetupTooltipsAlready = false;
setupTooltips( this );
} );
}
setupPopups( doIt );
}
// This hook is also fired after page load.
mw.hook( 'wikipage.content' ).add( dynamicContentHandler );
mw.hook( 'ext.echo.overlay.beforeShowingOverlay' ).add( function($overlay){
dynamicContentHandler( $overlay.find(".mw-echo-state") );
});
} )();
// Forked from User:Connel MacKenzie/mess-with-popups.js
Previewmaker.prototype.firstBit = function() {
try{
if (mw.config.get("wgNamespaceNumber") !== 0){
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d = this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data = this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data = this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data = this.data.replace(RegExp('^[\\s\\n]*'), '');
stuff = (RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) {
d = stuff[0];
}
if (!getValueOf('popupPreviewFirstParOnly')) {
d = this.data;
}
/// now put \n\n after sections so that bullets and numbered lists work
d = d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["' + "'" + ']*\\s)', 'g'));
// leading space is bad, mmkay?
d[0] = d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds = RegExp('([^.][a-z][.][a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
var maxChars=getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd;
do {
dd = this.firstSentences(d, n);
--n;
}
while (dd.length > maxChars && n > 0);
this.data = dd;
} else {
//TODO: Limit this override to urls that start with http://*.wiktionary.org/
//TODO: extend the above section to also preview Wikipedia links
var charsWikt = 0;
var linesWikt = this.data.split('\n');
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[in]transitive}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[un]countable}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{cattag\|(.*?)}}/g, "");
linesWikt[jWikt] = linesWikt[jWikt].replace(/[\{\}]/g, "'");
if (linesWikt[jWikt].search(/^[#]/) == -1) {
if (linesWikt[jWikt].search(/^==[A-Z]/) == -1) {
linesWikt[jWikt] = '';
}
}
charsWikt += linesWikt[jWikt].length;
}
if (charsWikt < 2) {
linesWikt[0] = "No properly formatted content";
}
var maxChars = getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd = '';
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
if (linesWikt[jWikt] !== '') {
dd += linesWikt[jWikt] + '\n';
}
if (dd.length < maxChars) {
this.data = dd;
}
}
}
}
catch (e) {
console.log("error from mess with popups: ", e);
}
};
});
q2ui3o84dl6smwhwh2kkvmiz7o8qxis
2513984
2513983
2026-08-14T08:20:58Z
Hiyuune
50834
2513984
javascript
text/javascript
// {{documentation}}
// _________________________________________________________________________________________
// | |
// | === WARNING: GLOBAL GADGET FILE === |
// | Changes to this page affect many users. |
// | Please discuss changes on the [[Wiktionary:Grease Pit]] before editing. |
// |_________________________________________________________________________________________|
mw.loader.load('//en.wikipedia.org/w/index.php?action=raw&title=MediaWiki:Gadget-navpop.css&ctype=text/css', "text/css");
// Forked from [[:w:MediaWiki:Gadget-popups.js]]
$(function () {
//////////////////////////////////////////////////
// Globals
//
// Trying to shove as many of these as possible into the pg (popup globals) object
var pg = {
re: {}, // regexps
ns: {}, // namespaces
string: {}, // translatable strings
wiki: {}, // local site info
user: {}, // current user info
misc: {}, // YUCK PHOOEY
option: {}, // options, see newOption etc
optionDefault: {}, // default option values
flag: {}, // misc flags
cache: {}, // page and image cache
structures: {}, // navlink structures
timer: {}, // all sorts of timers (too damn many)
counter: {}, // .. and all sorts of counters
current: {}, // state info
fn: {}, // functions
endoflist: null
};
/* Bail if the gadget/script is being loaded twice */
if( window.pg ) {
return;
}
/* Export to global context */
window.pg = pg;
/// Local Variables: ///
/// mode:c ///
/// End: ///
// ENDFILE: main.js
// STARTFILE: actions.js
function setupTooltips(container, remove, force, popData) {
log('setupTooltips, container='+container+', remove='+remove);
if (!container) {
//<NOLITE>
// the main initial call
if (getValueOf('popupOnEditSelection') && document && document.editform && document.editform.wpTextbox1) {
document.editform.wpTextbox1.onmouseup=doSelectionPopup;
}
//</NOLITE>
// article/content is a structure-dependent thing
container = defaultPopupsContainer();
}
if (!remove && !force && container.ranSetupTooltipsAlready) { return; }
container.ranSetupTooltipsAlready = !remove;
var anchors;
anchors=container.getElementsByTagName('A');
setupTooltipsLoop(anchors, 0, 250, 100, remove, popData);
}
function defaultPopupsContainer() {
if (getValueOf('popupOnlyArticleLinks')) {
return document.getElementById('mw_content') ||
document.getElementById('content') ||
document.getElementById('article') || document;
}
return document;
}
function setupTooltipsLoop(anchors,begin,howmany,sleep, remove, popData) {
log(simplePrintf('setupTooltipsLoop(%s,%s,%s,%s,%s)', arguments));
var finish=begin+howmany;
var loopend = Math.min(finish, anchors.length);
var j=loopend - begin;
log ('setupTooltips: anchors.length=' + anchors.length + ', begin=' + begin +
', howmany=' + howmany + ', loopend=' + loopend + ', remove=' + remove);
var doTooltip= remove ? removeTooltip : addTooltip;
// try a faster (?) loop construct
if (j > 0) {
do {
var a=anchors[loopend - j];
if (typeof a==='undefined' || !a || !a.href) {
log('got null anchor at index ' + loopend - j);
continue;
}
doTooltip(a, popData);
} while (--j);
}
if (finish < anchors.length) {
setTimeout(function() {
setupTooltipsLoop(anchors,finish,howmany,sleep,remove,popData);},
sleep);
} else {
if ( !remove && ! getValueOf('popupTocLinks')) { rmTocTooltips(); }
pg.flag.finishedLoading=true;
}
}
// eliminate popups from the TOC
// This also kills any onclick stuff that used to be going on in the toc
function rmTocTooltips() {
var toc=document.getElementById('toc');
if (toc) {
var tocLinks=toc.getElementsByTagName('A');
var tocLen = tocLinks.length;
for (var j=0; j<tocLen; ++j) {
removeTooltip(tocLinks[j], true);
}
}
}
function addTooltip(a, popData) {
if ( !isPopupLink(a) ) { return; }
a.onmouseover=mouseOverWikiLink;
a.onmouseout= mouseOutWikiLink;
a.onmousedown = killPopup;
a.hasPopup = true;
a.popData = popData;
}
function removeTooltip(a) {
if ( !a.hasPopup ) { return; }
a.onmouseover = null;
a.onmouseout = null;
if (a.originalTitle) { a.title = a.originalTitle; }
a.hasPopup=false;
}
function removeTitle(a) {
if (!a.originalTitle) {
a.originalTitle=a.title;
}
a.title='';
}
function restoreTitle(a) {
if ( a.title || !a.originalTitle ) { return; }
a.title = a.originalTitle;
}
function registerHooks(np) {
var popupMaxWidth=getValueOf('popupMaxWidth');
if (typeof popupMaxWidth === 'number') {
var setMaxWidth = function () {
np.mainDiv.style.maxWidth = popupMaxWidth + 'px';
np.maxWidth = popupMaxWidth;
};
np.addHook(setMaxWidth, 'unhide', 'before');
}
//<NOLITE>
np.addHook(addPopupShortcuts, 'unhide', 'after');
np.addHook(rmPopupShortcuts, 'hide', 'before');
//</NOLITE>
}
function removeModifierKeyHandler(a) {
//remove listeners for modifier key if any that were added in mouseOverWikiLink
document.removeEventListener('keydown', a.modifierKeyHandler, false);
document.removeEventListener('keyup', a.modifierKeyHandler, false);
}
function mouseOverWikiLink(evt) {
if (!evt && window.event) {evt=window.event;}
// if the modifier is needed, listen for it,
// we will remove the listener when we mouseout of this link or kill popup.
if (getValueOf('popupModifier')) {
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
var key = action=='disable' ? 'keyup' : 'keydown';
var a = this;
a.modifierKeyHandler = function(evt) {
mouseOverWikiLink2(a, evt);
};
document.addEventListener(key, a.modifierKeyHandler, false);
}
return mouseOverWikiLink2(this, evt);
}
/**
* Gets the references list item that the provided footnote link targets. This
* is typically a li element within the ol.references element inside the reflist.
* @param {Element} a - A footnote link.
* @returns {Element|boolean} The targeted element, or false if one can't be found.
*/
function footnoteTarget(a) {
var aTitle=Title.fromAnchor(a);
// We want ".3A" rather than "%3A" or "?" here, so use the anchor property directly
var anch = aTitle.anchor;
if ( ! /^(cite_note-|_note-|endnote)/.test(anch) ) { return false; }
var lTitle=Title.fromURL(location.href);
if ( lTitle.toString(true) !== aTitle.toString(true) ) { return false; }
var el=document.getElementById(anch);
while ( el && typeof el.nodeName === 'string') {
var nt = el.nodeName.toLowerCase();
if ( nt === 'li' ) { return el; }
else if ( nt === 'body' ) { return false; }
else if ( el.parentNode ) { el=el.parentNode; }
else { return false; }
}
return false;
}
function footnotePreview(x, navpop) {
setPopupHTML('<hr />' + x.innerHTML, 'popupPreview', navpop.idNumber);
}
function modifierPressed(evt) {
var mod=getValueOf('popupModifier');
if (!mod) { return false; }
if (!evt && window.event) {evt=window.event;}
return ( evt && mod && evt[mod.toLowerCase() + 'Key'] );
}
// Checks if the correct modifier pressed/unpressed if needed
function isCorrectModifier(a,evt) {
if (!getValueOf('popupModifier')) { return true; }
// if popupModifierAction = enable, we should popup when the modifier is pressed
// if popupModifierAction = disable, we should popup unless the modifier is pressed
var action = getValueOf('popupModifierAction');
return ( action == 'enable' && modifierPressed(evt) ||
action == 'disable' && !modifierPressed(evt) );
}
function mouseOverWikiLink2(a, evt) {
if (!isCorrectModifier(a,evt)) { return; }
if ( getValueOf('removeTitles') ) { removeTitle(a); }
if ( a==pg.current.link && a.navpopup && a.navpopup.isVisible() ) { return; }
pg.current.link=a;
if (getValueOf('simplePopups') && !pg.option.popupStructure) {
// reset *default value* of popupStructure
setDefault('popupStructure', 'original');
}
var article=(new Title()).fromAnchor(a);
// set global variable (ugh) to hold article (wikipage)
pg.current.article = article;
if (!a.navpopup) {
a.navpopup=newNavpopup(a, article);
pg.current.linksHash[a.href] = a.navpopup;
pg.current.links.push(a);
}
if (a.navpopup.pending === null || a.navpopup.pending !== 0) {
// either fresh popups or those with unfinshed business are redone from scratch
simplePopupContent(a, article);
}
a.navpopup.showSoonIfStable(a.navpopup.delay);
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=setInterval(checkPopupPosition, 600);
if(getValueOf('simplePopups')) {
if (getValueOf('popupPreviewButton') && !a.simpleNoMore) {
var d=document.createElement('div');
d.className='popupPreviewButtonDiv';
var s=document.createElement('span');
d.appendChild(s);
s.className='popupPreviewButton';
s['on' + getValueOf('popupPreviewButtonEvent')] = function() {
a.simpleNoMore=true;
d.style.display = "none";
nonsimplePopupContent(a,article);
};
s.innerHTML=popupString('show preview');
setPopupHTML(d, 'popupPreview', a.navpopup.idNumber);
}
}
if (a.navpopup.pending !== 0 ) {
nonsimplePopupContent(a, article);
}
}
// simplePopupContent: the content that do not require additional download
// (it is shown even when simplePopups is true)
function simplePopupContent(a, article) {
/* FIXME hack */ a.navpopup.hasPopupMenu=false;
a.navpopup.setInnerHTML(popupHTML(a));
fillEmptySpans({navpopup:a.navpopup});
if (getValueOf('popupDraggable'))
{
var dragHandle = getValueOf('popupDragHandle') || null;
if (dragHandle && dragHandle != 'all') {
dragHandle += a.navpopup.idNumber;
}
setTimeout(function(){a.navpopup.makeDraggable(dragHandle);}, 150);
}
//<NOLITE>
if (getValueOf('popupRedlinkRemoval') && a.className=='new') {
setPopupHTML('<br>'+popupRedlinkHTML(article), 'popupRedlink', a.navpopup.idNumber);
}
//</NOLITE>
}
function debugData(navpopup) {
if(getValueOf('popupDebugging') && navpopup.idNumber) {
setPopupHTML('idNumber='+navpopup.idNumber + ', pending=' + navpopup.pending,
'popupError', navpopup.idNumber);
}
}
function newNavpopup(a, article) {
var navpopup = new Navpopup();
navpopup.fuzz=5;
navpopup.delay=getValueOf('popupDelay')*1000;
// increment global counter now
navpopup.idNumber = ++pg.idNumber;
navpopup.parentAnchor = a;
navpopup.parentPopup = (a.popData && a.popData.owner);
navpopup.article = article;
registerHooks(navpopup);
return navpopup;
}
// Should we show nonsimple context?
// If simplePopups is set to true, then we do not show nonsimple context,
// but if a bottom "show preview" was clicked we do show nonsimple context
function shouldShowNonSimple(a) {
return !getValueOf('simplePopups') || a.simpleNoMore;
}
// Should we show nonsimple context govern by the option (e.g. popupUserInfo)?
// If the user explicitly asked for nonsimple context by setting the option to true,
// then we show it even in nonsimple mode.
function shouldShow(a,option) {
if (shouldShowNonSimple(a)) {
return getValueOf(option);
} else {
return (typeof window[option] != 'undefined' ) && window[option];
}
}
function nonsimplePopupContent(a, article) {
var diff=null, history=null;
var params=parseParams(a.href);
var oldid=(typeof params.oldid=='undefined' ? null : params.oldid);
//<NOLITE>
if(shouldShow(a,'popupPreviewDiffs')) {
diff=params.diff;
}
if(shouldShow(a,'popupPreviewHistory')) {
history=(params.action=='history');
}
//</NOLITE>
a.navpopup.pending=0;
var referenceElement = footnoteTarget(a);
if (referenceElement) {
footnotePreview(referenceElement, a.navpopup);
//<NOLITE>
} else if ( diff || diff === 0 ) {
loadDiff(article, oldid, diff, a.navpopup);
} else if ( history ) {
loadAPIPreview('history', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.contribs.test(a.href) ) {
loadAPIPreview('contribs', article, a.navpopup);
} else if ( shouldShowNonSimple(a) && pg.re.backlinks.test(a.href) ) {
loadAPIPreview('backlinks', article, a.navpopup);
} else if ( // FIXME should be able to get all preview combinations with options
article.namespaceId()==pg.nsImageId &&
( shouldShow(a,'imagePopupsForImages') || ! anchorContainsImage(a) )
) {
loadAPIPreview('imagepagepreview', article, a.navpopup);
loadImage(article, a.navpopup);
//</NOLITE>
} else {
if (article.namespaceId() == pg.nsCategoryId &&
shouldShow(a,'popupCategoryMembers')) {
loadAPIPreview('category', article, a.navpopup);
} else if ((article.namespaceId() == pg.nsUserId || article.namespaceId() == pg.nsUsertalkId) &&
shouldShow(a,'popupUserInfo')) {
loadAPIPreview('userinfo', article, a.navpopup);
}
if (shouldShowNonSimple(a)) startArticlePreview(article, oldid, a.navpopup);
}
}
function pendingNavpopTask(navpop) {
if (navpop && navpop.pending === null) { navpop.pending=0; }
++navpop.pending;
debugData(navpop);
}
function completedNavpopTask(navpop) {
if (navpop && navpop.pending) { --navpop.pending; }
debugData(navpop);
}
function startArticlePreview(article, oldid, navpop) {
navpop.redir=0;
loadPreview(article, oldid, navpop);
}
function loadPreview(article, oldid, navpop) {
if (!navpop.redir) { navpop.originalArticle=article; }
article.oldid = oldid;
loadAPIPreview('revision', article, navpop);
}
function loadPreviewFromRedir(redirMatch, navpop) {
// redirMatch is a regex match
var target = new Title().fromWikiText(redirMatch[2]);
// overwrite (or add) anchor from original target
// mediawiki does overwrite; eg User:Lupin/foo3#Done
if ( navpop.article.anchor ) { target.anchor = navpop.article.anchor; }
navpop.redir++;
navpop.redirTarget=target;
//<NOLITE>
var warnRedir = redirLink(target, navpop.article);
setPopupHTML(warnRedir, 'popupWarnRedir', navpop.idNumber);
//</NOLITE>
navpop.article=target;
fillEmptySpans({redir: true, redirTarget: target, navpopup:navpop});
return loadPreview(target, null, navpop);
}
function insertPreview(download) {
if (!download.owner) { return; }
var redirMatch = pg.re.redirect.exec(download.data);
if (download.owner.redir === 0 && redirMatch) {
loadPreviewFromRedir(redirMatch, download.owner);
return;
}
if (download.owner.visible || !getValueOf('popupLazyPreviews')) {
insertPreviewNow(download);
} else {
var id=(download.owner.redir) ? 'PREVIEW_REDIR_HOOK' : 'PREVIEW_HOOK';
download.owner.addHook( function(){insertPreviewNow(download); return true;},
'unhide', 'after', id );
}
}
function insertPreviewNow(download) {
if (!download.owner) { return; }
var wikiText=download.data;
var navpop=download.owner;
var art=navpop.redirTarget || navpop.originalArticle;
//<NOLITE>
makeFixDabs(wikiText, navpop);
if (getValueOf('popupSummaryData')) {
getPageInfo(wikiText, download);
setPopupTrailer(getPageInfo(wikiText, download), navpop.idNumber);
}
var imagePage='';
if (art.namespaceId()==pg.nsImageId) { imagePage=art.toString(); }
else { imagePage=getValidImageFromWikiText(wikiText); }
if(imagePage) { loadImage(Title.fromWikiText(imagePage), navpop); }
//</NOLITE>
if (getValueOf('popupPreviews')) { insertArticlePreview(download, art, navpop); }
}
function insertArticlePreview(download, art, navpop) {
if (download && typeof download.data == typeof ''){
if (art.namespaceId()==pg.nsTemplateId && getValueOf('popupPreviewRawTemplates')) {
// FIXME compare/consolidate with diff escaping code for wikitext
var h='<hr /><span style="font-family: monospace;">' + download.data.entify().split('\\n').join('<br />\\n') + '</span>';
setPopupHTML(h, 'popupPreview', navpop.idNumber);
}
else {
var p=prepPreviewmaker(download.data, art, navpop);
p.showPreview();
}
}
}
function prepPreviewmaker(data, article, navpop) {
// deal with tricksy anchors
var d=anchorize(data, article.anchorString());
var urlBase=joinPath([pg.wiki.articlebase, article.urlString()]);
var p=new Previewmaker(d, urlBase, navpop);
return p;
}
// Try to imitate the way mediawiki generates HTML anchors from section titles
function anchorize(d, anch) {
if (!anch) { return d; }
var anchRe=RegExp('(?:=+\\s*' + literalizeRegex(anch).replace(/[_ ]/g, '[_ ]') + '\\s*=+|\\{\\{\\s*'+getValueOf('popupAnchorRegexp')+'\\s*(?:\\|[^|}]*)*?\\s*'+literalizeRegex(anch)+'\\s*(?:\\|[^}]*)?}})');
var match=d.match(anchRe);
if(match && match.length > 0 && match[0]) { return d.substring(d.indexOf(match[0])); }
// now try to deal with == foo [[bar|baz]] boom == -> #foo_baz_boom
var lines=d.split('\n');
for (var i=0; i<lines.length; ++i) {
lines[i]=lines[i].replace(RegExp('[[]{2}([^|\\]]*?[|])?(.*?)[\\]]{2}', 'g'), '$2')
.replace(/'''([^'])/g, '$1').replace(RegExp("''([^'])", 'g'), '$1');
if (lines[i].match(anchRe)) {
return d.split('\n').slice(i).join('\n').replace(RegExp('^[^=]*'), '');
}
}
return d;
}
function killPopup() {
removeModifierKeyHandler(this);
if (getValueOf('popupShortcutKeys')) { rmPopupShortcuts(); }
if (!pg) { return; }
if (pg.current.link && pg.current.link.navpopup) { pg.current.link.navpopup.banish(); }
pg.current.link=null;
abortAllDownloads();
if (pg.timer.checkPopupPosition) {
clearInterval(pg.timer.checkPopupPosition);
pg.timer.checkPopupPosition=null;
}
return true; // preserve default action
}
// ENDFILE: actions.js
// STARTFILE: domdrag.js
/**
@fileoverview
The {@link Drag} object, which enables objects to be dragged around.
<pre>
*************************************************
dom-drag.js
09.25.2001
www.youngpup.net
**************************************************
10.28.2001 - fixed minor bug where events
sometimes fired off the handle, not the root.
*************************************************
Pared down, some hooks added by User:Lupin
Copyright Aaron Boodman.
Saying stupid things daily since March 2001.
</pre>
*/
/**
Creates a new Drag object. This is used to make various DOM elements draggable.
@constructor
*/
function Drag () {
/**
Condition to determine whether or not to drag. This function should take one parameter, an Event.
To disable this, set it to <code>null</code>.
@type Function
*/
this.startCondition = null;
/**
Hook to be run when the drag finishes. This is passed the final coordinates of
the dragged object (two integers, x and y). To disables this, set it to <code>null</code>.
@type Function
*/
this.endHook = null;
}
/**
Gets an event in a cross-browser manner.
@param {Event} e
@private
*/
Drag.prototype.fixE = function(e) {
if (typeof e == 'undefined') { e = window.event; }
if (typeof e.layerX == 'undefined') { e.layerX = e.offsetX; }
if (typeof e.layerY == 'undefined') { e.layerY = e.offsetY; }
return e;
};
/**
Initialises the Drag instance by telling it which object you want to be draggable, and what you want to drag it by.
@param {DOMElement} o The "handle" by which <code>oRoot</code> is dragged.
@param {DOMElement} oRoot The object which moves when <code>o</code> is dragged, or <code>o</code> if omitted.
*/
Drag.prototype.init = function(o, oRoot) {
var dragObj = this;
this.obj = o;
o.onmousedown = function(e) { dragObj.start.apply( dragObj, [e]); };
o.dragging = false;
o.popups_draggable = true;
o.hmode = true;
o.vmode = true;
o.root = oRoot ? oRoot : o ;
if (isNaN(parseInt(o.root.style.left, 10))) { o.root.style.left = "0px"; }
if (isNaN(parseInt(o.root.style.top, 10))) { o.root.style.top = "0px"; }
o.root.onthisStart = function(){};
o.root.onthisEnd = function(){};
o.root.onthis = function(){};
};
/**
Starts the drag.
@private
@param {Event} e
*/
Drag.prototype.start = function(e) {
var o = this.obj; // = this;
e = this.fixE(e);
if (this.startCondition && !this.startCondition(e)) { return; }
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10);
o.root.onthisStart(x, y);
o.lastMouseX = e.clientX;
o.lastMouseY = e.clientY;
var dragObj = this;
o.onmousemoveDefault = document.onmousemove;
o.dragging = true;
document.onmousemove = function(e) { dragObj.drag.apply( dragObj, [e] ); };
document.onmouseup = function(e) { dragObj.end.apply( dragObj, [e] ); };
return false;
};
/**
Does the drag.
@param {Event} e
@private
*/
Drag.prototype.drag = function(e) {
e = this.fixE(e);
var o = this.obj;
var ey = e.clientY;
var ex = e.clientX;
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10);
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10 );
var nx, ny;
nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));
this.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
this.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
this.obj.lastMouseX = ex;
this.obj.lastMouseY = ey;
this.obj.root.onthis(nx, ny);
return false;
};
/**
Ends the drag.
@private
*/
Drag.prototype.end = function() {
document.onmousemove=this.obj.onmousemoveDefault;
document.onmouseup = null;
this.obj.dragging = false;
if (this.endHook) {
this.endHook( parseInt(this.obj.root.style[this.obj.hmode ? "left" : "right"], 10),
parseInt(this.obj.root.style[this.obj.vmode ? "top" : "bottom"], 10));
}
};
// ENDFILE: domdrag.js
// STARTFILE: structures.js
//<NOLITE>
pg.structures.original={};
pg.structures.original.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle',
'popupUserData', 'popupData', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.original.popupRedirSpans=function () {
return ['popupRedir', 'popupWarnRedir', 'popupRedirTopLinks',
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'];
};
pg.structures.original.popupTitle=function (x) {
log ('defaultstructure.popupTitle');
if (!getValueOf('popupNavLinks')) {
return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
}
return '';
};
pg.structures.original.popupTopLinks=function (x) {
log ('defaultstructure.popupTopLinks');
if (getValueOf('popupNavLinks')) { return navLinksHTML(x.article, x.hint, x.params); }
return '';
};
pg.structures.original.popupImage=function(x) {
log ('original.popupImage, x.article='+x.article+', x.navpop.idNumber='+x.navpop.idNumber);
return imageHTML(x.article, x.navpop.idNumber);
};
pg.structures.original.popupRedirTitle=pg.structures.original.popupTitle;
pg.structures.original.popupRedirTopLinks=pg.structures.original.popupTopLinks;
function copyStructure(oldStructure, newStructure) {
pg.structures[newStructure]={};
for (var prop in pg.structures[oldStructure]) {
pg.structures[newStructure][prop]=pg.structures[oldStructure][prop];
}
}
copyStructure('original', 'nostalgia');
pg.structures.nostalgia.popupTopLinks=function(x) {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>';
str+='if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' +
editstr + '}';
var historystr='<<history|shortcut=h>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str += '<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>';
str += 'if(admin){<br>}else{*}<<move|shortcut=m>>';
// admin links
str += 'if(admin){*<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>}';
return navlinkStringToHTML(str, x.article, x.params);
};
pg.structures.nostalgia.popupRedirTopLinks=pg.structures.nostalgia.popupTopLinks;
/** -- fancy -- **/
copyStructure('original', 'fancy');
pg.structures.fancy.popupTitle=function (x) {
return navlinkStringToHTML('<font size=+0><<mainlink>></font>',x.article,x.params);
};
pg.structures.fancy.popupTopLinks=function(x) {
var hist='<<history|shortcut=h|hist>>|<<lastEdit|shortcut=/|last>>|<<editors|shortcut=E|eds>>';
var watch='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
var move='<<move|shortcut=m|move>>';
return navlinkStringToHTML('if(talk){' +
'<<edit|shortcut=e>>|<<new|shortcut=+|+>>*' + hist + '*' +
'<<article|shortcut=a>>|<<editArticle|edit>>' + '*' + watch + '*' + move +
'}else{<<edit|shortcut=e>>*' + hist +
'*<<talk|shortcut=t|>>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>' +
'*' + watch + '*' + move+'}<br>', x.article, x.params);
};
pg.structures.fancy.popupOtherLinks=function(x) {
var admin='<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*<<undelete|undeleteShort>>|<<delete|shortcut=d|del>>';
var user='<<contribs|shortcut=c>>if(wikimedia){|<<count|shortcut=#|#>>}';
user+='if(ipuser){|<<arin>>}else{*<<email|shortcut=E|'+
popupString('email')+'>>}if(admin){*<<block|shortcut=b>>}';
var normal='<<whatLinksHere|shortcut=l|links here>>*<<relatedChanges|shortcut=r|related>>';
return navlinkStringToHTML('<br>if(user){' + user + '*}if(admin){'+admin+'if(user){<br>}else{*}}' + normal,
x.article, x.params);
};
pg.structures.fancy.popupRedirTitle=pg.structures.fancy.popupTitle;
pg.structures.fancy.popupRedirTopLinks=pg.structures.fancy.popupTopLinks;
pg.structures.fancy.popupRedirOtherLinks=pg.structures.fancy.popupOtherLinks;
/** -- fancy2 -- **/
// hack for User:MacGyverMagic
copyStructure('fancy', 'fancy2');
pg.structures.fancy2.popupTopLinks=function(x) { // hack out the <br> at the end and put one at the beginning
return '<br>'+pg.structures.fancy.popupTopLinks(x).replace(RegExp('<br>$','i'),'');
};
pg.structures.fancy2.popupLayout=function () { // move toplinks to after the title
return ['popupError', 'popupImage', 'popupTitle', 'popupUserData', 'popupData', 'popupTopLinks', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
/** -- menus -- **/
copyStructure('original', 'menus');
pg.structures.menus.popupLayout=function () {
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle', 'popupOtherLinks',
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],
'popupUserData', 'popupData', 'popupMiscTools', ['popupRedlink'],
'popupPrePreviewSep', 'popupPreview', 'popupSecondPreview', 'popupPreviewMore', 'popupPostPreview', 'popupFixDab'];
};
pg.structures.menus.popupTopLinks = function (x, shorter) {
// FIXME maybe this stuff should be cached
var s=[];
var dropdiv='<div class="popup_drop">';
var enddiv='</div>';
var hist='<<history|shortcut=h>>';
if (!shorter) { hist = '<menurow>' + hist +
'|<<historyfeed|rss>>|<<editors|shortcut=E>></menurow>'; }
var lastedit='<<lastEdit|shortcut=/|show last edit>>';
var thank='if(diff){<<thank|send thanks>>}';
var jsHistory='<<lastContrib|last set of edits>><<sinceMe|changes since mine>>';
var linkshere='<<whatLinksHere|shortcut=l|what links here>>';
var related='<<relatedChanges|shortcut=r|related changes>>';
var search='<menurow><<search|shortcut=s>>if(wikimedia){|<<globalsearch|shortcut=g|global>>}' +
'|<<google|shortcut=G|web>></menurow>';
var watch='<menurow><<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>></menurow>';
var protect='<menurow><<unprotect|unprotectShort>>|' +
'<<protect|shortcut=p>>|<<protectlog|log>></menurow>';
var del='<menurow><<undelete|undeleteShort>>|<<delete|shortcut=d>>|' +
'<<deletelog|log>></menurow>';
var move='<<move|shortcut=m|move page>>';
var nullPurge='<menurow><<nullEdit|shortcut=n|null edit>>|<<purge|shortcut=P>></menurow>';
var viewOptions='<menurow><<view|shortcut=v>>|<<render|shortcut=S>>|<<raw>></menurow>';
var editRow='if(oldid){' +
'<menurow><<edit|shortcut=e>>|<<editOld|shortcut=e|this revision>></menurow>' +
'<menurow><<revert|shortcut=v>>|<<undo>></menurow>' + '}else{<<edit|shortcut=e>>}';
var markPatrolled='if(rcid){<<markpatrolled|mark patrolled>>}';
var newTopic='if(talk){<<new|shortcut=+|new topic>>}';
var protectDelete='if(admin){' + protect + del + '}';
if (getValueOf('popupActionsMenu')) {
s.push( '<<mainlink>>*' + dropdiv + menuTitle('tác vụ'));
} else {
s.push( dropdiv + '<<mainlink>>');
}
s.push( '<menu>');
s.push( editRow + markPatrolled + newTopic + hist + lastedit + thank );
if (!shorter) { s.push(jsHistory); }
s.push( move + linkshere + related);
if (!shorter) { s.push(nullPurge + search); }
if (!shorter) { s.push(viewOptions); }
s.push('<hr />' + watch + protectDelete);
s.push('<hr />' +
'if(talk){<<article|shortcut=a|view article>><<editArticle|edit article>>}' +
'else{<<talk|shortcut=t|talk page>><<editTalk|edit talk>>' +
'<<newTalk|shortcut=+|new topic>>}</menu>' + enddiv);
// user menu starts here
var email='<<email|shortcut=E|email user>>';
var contribs= 'if(wikimedia){<menurow>}<<contribs|shortcut=c|contributions>>if(wikimedia){</menurow>}' +
'if(admin){<menurow><<deletedContribs>></menurow>}';
s.push('if(user){*' + dropdiv + menuTitle('user'));
s.push('<menu>');
s.push('<menurow><<userPage|shortcut=u|user page>>|<<userSpace|space>></menurow>');
s.push('<<userTalk|shortcut=t|user talk>><<editUserTalk|edit user talk>>' +
'<<newUserTalk|shortcut=+|leave comment>>');
if(!shorter) { s.push( 'if(ipuser){<<arin>>}else{' + email + '}' ); }
else { s.push( 'if(ipuser){}else{' + email + '}' ); }
s.push('<hr />' + contribs + '<<userlog|shortcut=L|user log>>');
s.push('if(wikimedia){<<count|shortcut=#|edit counter>>}');
s.push('if(admin){<menurow><<unblock|unblockShort>>|<<block|shortcut=b|block user>></menurow>}');
s.push('<<blocklog|shortcut=B|block log>>');
s.push('</menu>' + enddiv + '}');
// popups menu starts here
if (getValueOf('popupSetupMenu') && !x.navpop.hasPopupMenu /* FIXME: hack */) {
x.navpop.hasPopupMenu=true;
s.push('*' + dropdiv + menuTitle('popupsMenu') + '<menu>');
s.push('<<togglePreviews|toggle previews>>');
s.push('<<purgePopups|reset>>');
s.push('<<disablePopups|disable>>');
s.push('</menu>'+enddiv);
}
return navlinkStringToHTML(s.join(''), x.article, x.params);
};
function menuTitle(s) {
return '<a href="#" noPopup=1>' + popupString(s) + '</a>';
}
pg.structures.menus.popupRedirTitle=pg.structures.menus.popupTitle;
pg.structures.menus.popupRedirTopLinks=pg.structures.menus.popupTopLinks;
copyStructure('menus', 'shortmenus');
pg.structures.shortmenus.popupTopLinks=function(x) {
return pg.structures.menus.popupTopLinks(x,true);
};
pg.structures.shortmenus.popupRedirTopLinks=pg.structures.shortmenus.popupTopLinks;
//</NOLITE>
pg.structures.lite={};
pg.structures.lite.popupLayout=function () {
return ['popupTitle', 'popupPreview' ];
};
pg.structures.lite.popupTitle=function (x) {
log (x.article + ': structures.lite.popupTitle');
//return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.params);
return '<div><span class="popup_mainlink"><b>' + x.article.toString() + '</b></span></div>';
};
// ENDFILE: structures.js
// STARTFILE: autoedit.js
//<NOLITE>
function substitute(data,cmdBody) {
// alert('sub\nfrom: '+cmdBody.from+'\nto: '+cmdBody.to+'\nflags: '+cmdBody.flags);
var fromRe=RegExp(cmdBody.from, cmdBody.flags);
return data.replace(fromRe, cmdBody.to);
}
function execCmds(data, cmdList) {
for (var i=0; i<cmdList.length; ++i) {
data=cmdList[i].action(data, cmdList[i]);
}
return data;
}
function parseCmd(str) {
// returns a list of commands
if (!str.length) { return []; }
var p=false;
switch (str.charAt(0)) {
case 's':
p=parseSubstitute(str);
break;
default:
return false;
}
if (p) { return [p].concat(parseCmd(p.remainder)); }
return false;
}
function unEscape(str, sep) {
return str.split('\\\\').join('\\').split('\\'+sep).join(sep).split('\\n').join('\n');
}
function parseSubstitute(str) {
// takes a string like s/a/b/flags;othercmds and parses it
var from,to,flags,tmp;
if (str.length<4) { return false; }
var sep=str.charAt(1);
str=str.substring(2);
tmp=skipOver(str,sep);
if (tmp) { from=tmp.segment; str=tmp.remainder; }
else { return false; }
tmp=skipOver(str,sep);
if (tmp) { to=tmp.segment; str=tmp.remainder; }
else { return false; }
flags='';
if (str.length) {
tmp=skipOver(str,';') || skipToEnd(str, ';');
if (tmp) {flags=tmp.segment; str=tmp.remainder; }
}
return {action: substitute, from: from, to: to, flags: flags, remainder: str};
}
function skipOver(str,sep) {
var endSegment=findNext(str,sep);
if (endSegment<0) { return false; }
var segment=unEscape(str.substring(0,endSegment), sep);
return {segment: segment, remainder: str.substring(endSegment+1)};
}
/*eslint-disable*/
function skipToEnd(str,sep) {
return {segment: str, remainder: ''};
}
/*eslint-enable */
function findNext(str, ch) {
for (var i=0; i<str.length; ++i) {
if (str.charAt(i)=='\\') { i+=2; }
if (str.charAt(i)==ch) { return i; }
}
return -1;
}
function setCheckbox(param, box) {
var val=mw.util.getParamValue(param);
if (val) {
switch (val) {
case '1': case 'yes': case 'true':
box.checked=true;
break;
case '0': case 'no': case 'false':
box.checked=false;
}
}
}
function autoEdit() {
setupPopups( function () {
if (mw.util.getParamValue('autoimpl') !== popupString('autoedit_version') ) { return false; }
if (mw.util.getParamValue('autowatchlist') && mw.util.getParamValue('actoken')===autoClickToken()) {
pg.fn.modifyWatchlist(mw.util.getParamValue('title'), mw.util.getParamValue('action'));
}
if (!document.editform) { return false; }
if (autoEdit.alreadyRan) { return false; }
autoEdit.alreadyRan=true;
var cmdString=mw.util.getParamValue('autoedit');
if (cmdString) {
try {
var editbox=document.editform.wpTextbox1;
var cmdList=parseCmd(cmdString);
var input=editbox.value;
var output=execCmds(input, cmdList);
editbox.value=output;
} catch (dang) { return; }
// wikEd user script compatibility
if (typeof(wikEdUseWikEd) != 'undefined') {
if (wikEdUseWikEd === true) {
WikEdUpdateFrame();
}
}
}
setCheckbox('autominor', document.editform.wpMinoredit);
setCheckbox('autowatch', document.editform.wpWatchthis);
var rvid = mw.util.getParamValue('autorv');
if (rvid) {
var url=pg.wiki.apiwikibase + '?action=query&format=json&formatversion=2&prop=revisions&revids='+rvid;
startDownload(url, null, autoEdit2);
} else { autoEdit2(); }
} );
}
function autoEdit2(d) {
var summary=mw.util.getParamValue('autosummary');
var summaryprompt=mw.util.getParamValue('autosummaryprompt');
var summarynotice='';
if (d && d.data && mw.util.getParamValue('autorv')) {
var s = getRvSummary(summary, d.data);
if (s === false) {
summaryprompt=true;
summarynotice=popupString('Failed to get revision information, please edit manually.\n\n');
summary = simplePrintf(summary, [mw.util.getParamValue('autorv'), '(unknown)', '(unknown)']);
} else { summary = s; }
}
if (summaryprompt) {
var txt= summarynotice +
popupString('Enter a non-empty edit summary or press cancel to abort');
var response=prompt(txt, summary);
if (response) { summary=response; }
else { return; }
}
if (summary) { document.editform.wpSummary.value=summary; }
// Attempt to avoid possible premature clicking of the save button
// (maybe delays in updates to the DOM are to blame?? or a red herring)
setTimeout(autoEdit3, 100);
}
function autoClickToken() {
return mw.user.sessionId();
}
function autoEdit3() {
if( mw.util.getParamValue('actoken') != autoClickToken()) { return; }
var btn=mw.util.getParamValue('autoclick');
if (btn) {
if (document.editform && document.editform[btn]) {
var button=document.editform[btn];
var msg=tprintf('The %s button has been automatically clicked. Please wait for the next page to load.',
[ button.value ]);
bannerMessage(msg);
document.title='('+document.title+')';
button.click();
} else {
alert(tprintf('Could not find button %s. Please check the settings in your javascript file.',
[ btn ]));
}
}
}
function bannerMessage(s) {
var headings=document.getElementsByTagName('h1');
if (headings) {
var div=document.createElement('div');
div.innerHTML='<font size=+1><b>' + s + '</b></font>';
headings[0].parentNode.insertBefore(div, headings[0]);
}
}
function getRvSummary(template, json) {
try {
var o=getJsObj(json);
var edit = anyChild(o.query.pages).revisions[0];
var timestamp = edit.timestamp.split(/[A-Z]/g).join(' ').replace(/^ *| *$/g, '');
return simplePrintf(template, [edit.revid, timestamp, edit.userhidden ? '(hidden)' : edit.user ]);
} catch (badness) {
return false;
}
}
//</NOLITE>
// ENDFILE: autoedit.js
// STARTFILE: downloader.js
/**
@fileoverview
{@link Downloader}, a xmlhttprequest wrapper, and helper functions.
*/
/**
Creates a new Downloader
@constructor
@class The Downloader class. Create a new instance of this class to download stuff.
@param {String} url The url to download. This can be omitted and supplied later.
*/
function Downloader(url) {
if (typeof XMLHttpRequest!='undefined') { this.http = new XMLHttpRequest(); }
/**
The url to download
@type String
*/
this.url = url;
/**
A universally unique ID number
@type integer
*/
this.id=null;
/**
Modification date, to be culled from the incoming headers
@type Date
@private
*/
this.lastModified = null;
/**
What to do when the download completes successfully
@type Function
@private
*/
this.callbackFunction = null;
/**
What to do on failure
@type Function
@private
*/
this.onFailure = null;
/**
Flag set on <code>abort</code>
@type boolean
*/
this.aborted = false;
/**
HTTP method. See https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html for possibilities.
@type String
*/
this.method='GET';
/**
Async flag.
@type boolean
*/
this.async=true;
}
new Downloader();
/** Submits the http request. */
Downloader.prototype.send = function (x) {
if (!this.http) { return null; }
return this.http.send(x);
};
/** Aborts the download, setting the <code>aborted</code> field to true. */
Downloader.prototype.abort = function () {
if (!this.http) { return null; }
this.aborted=true;
return this.http.abort();
};
/** Returns the downloaded data. */
Downloader.prototype.getData = function () {if (!this.http) { return null; } return this.http.responseText;};
/** Prepares the download. */
Downloader.prototype.setTarget = function () {
if (!this.http) { return null; }
this.http.open(this.method, this.url, this.async);
this.http.setRequestHeader( 'Api-User-Agent', pg.misc.userAgent );
};
/** Gets the state of the download. */
Downloader.prototype.getReadyState=function () {if (!this.http) { return null; } return this.http.readyState;};
pg.misc.downloadsInProgress = { };
/** Starts the download.
Note that setTarget {@link Downloader#setTarget} must be run first
*/
Downloader.prototype.start=function () {
if (!this.http) { return; }
pg.misc.downloadsInProgress[this.id] = this;
this.http.send(null);
};
/** Gets the 'Last-Modified' date from the download headers.
Should be run after the download completes.
Returns <code>null</code> on failure.
@return {Date}
*/
Downloader.prototype.getLastModifiedDate=function () {
if(!this.http) { return null; }
var lastmod=null;
try {
lastmod=this.http.getResponseHeader('Last-Modified');
} catch (err) {}
if (lastmod) { return new Date(lastmod); }
return null;
};
/** Sets the callback function.
@param {Function} f callback function, called as <code>f(this)</code> on success
*/
Downloader.prototype.setCallback = function (f) {
if(!this.http) { return; }
this.http.onreadystatechange = f;
};
Downloader.prototype.getStatus = function() { if (!this.http) { return null; } return this.http.status; };
//////////////////////////////////////////////////
// helper functions
/** Creates a new {@link Downloader} and prepares it for action.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function newDownload(url, id, callback, onfailure) {
var d=new Downloader(url);
if (!d.http) { return 'ohdear'; }
d.id=id;
d.setTarget();
if (!onfailure) {
onfailure=2;
}
var f = function () {
if (d.getReadyState() == 4) {
delete pg.misc.downloadsInProgress[this.id];
try {
if ( d.getStatus() == 200 ) {
d.data=d.getData();
d.lastModified=d.getLastModifiedDate();
callback(d);
} else if (typeof onfailure == typeof 1) {
if (onfailure > 0) {
// retry
newDownload(url, id, callback, onfailure - 1);
}
} else if (typeof onfailure === 'function') {
onfailure(d,url,id,callback);
}
} catch (somerr) { /* ignore it */ }
}
};
d.setCallback(f);
return d;
}
/** Simulates a download from cached data.
The supplied data is put into a {@link Downloader} as if it had downloaded it.
@param {String} url The url.
@param {integer} id The ID.
@param {Function} callback The callback, which is invoked immediately as <code>callback(d)</code>,
where <code>d</code> is the new {@link Downloader}.
@param {String} data The (cached) data.
@param {Date} lastModified The (cached) last modified date.
*/
function fakeDownload(url, id, callback, data, lastModified, owner) {
var d=newDownload(url,callback);
d.owner=owner;
d.id=id; d.data=data;
d.lastModified=lastModified;
return callback(d);
}
/**
Starts a download.
@param {String} url The url to download
@param {integer} id The ID of the {@link Downloader} object
@param {Function} callback The callback function invoked on success
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser
*/
function startDownload(url, id, callback) {
var d=newDownload(url, id, callback);
if (typeof d == typeof '' ) { return d; }
d.start();
return d;
}
/**
Aborts all downloads which have been started.
*/
function abortAllDownloads() {
for ( var x in pg.misc.downloadsInProgress ) {
try {
pg.misc.downloadsInProgress[x].aborted=true;
pg.misc.downloadsInProgress[x].abort();
delete pg.misc.downloadsInProgress[x];
} catch (e) {}
}
}
// ENDFILE: downloader.js
// STARTFILE: livepreview.js
// TODO: location is often not correct (eg relative links in previews)
// NOTE: removed md5 and image and math parsing. was broken, lots of bytes.
/**
* InstaView - a Mediawiki to HTML converter in JavaScript
* Version 0.6.1
* Copyright (C) Pedro Fayolle 2005-2006
* https://en.wikipedia.org/wiki/User:Pilaf
* Distributed under the BSD license
*
* Changelog:
*
* 0.6.1
* - Fixed problem caused by \r characters
* - Improved inline formatting parser
*
* 0.6
* - Changed name to InstaView
* - Some major code reorganizations and factored out some common functions
* - Handled conversion of relative links (i.e. [[/foo]])
* - Fixed misrendering of adjacent definition list items
* - Fixed bug in table headings handling
* - Changed date format in signatures to reflect Mediawiki's
* - Fixed handling of [[:Image:...]]
* - Updated MD5 function (hopefully it will work with UTF-8)
* - Fixed bug in handling of links inside images
*
* To do:
* - Better support for math tags
* - Full support for <nowiki>
* - Parser-based (as opposed to RegExp-based) inline wikicode handling (make it one-pass and bullet-proof)
* - Support for templates (through AJAX)
* - Support for coloured links (AJAX)
*/
var Insta = {};
function setupLivePreview() {
// options
Insta.conf =
{
baseUrl: '',
user: {},
wiki: {
lang: pg.wiki.lang,
interwiki: pg.wiki.interwiki,
default_thumb_width: 180
},
paths: {
articles: pg.wiki.articlePath + '/',
// Only used for Insta previews with images. (not in popups)
math: '/math/',
images: '//upload.wikimedia.org/wikipedia/en/', // FIXME getImageUrlStart(pg.wiki.hostname),
images_fallback: '//upload.wikimedia.org/wikipedia/commons/',
},
locale: {
user: mw.config.get('wgFormattedNamespaces')[pg.nsUserId],
image: mw.config.get('wgFormattedNamespaces')[pg.nsImageId],
category: mw.config.get('wgFormattedNamespaces')[pg.nsCategoryId],
// shouldn't be used in popup previews, i think
months: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
}
};
// options with default values or backreferences
Insta.conf.user.name = Insta.conf.user.name || 'Wikipedian';
Insta.conf.user.signature = '[['+Insta.conf.locale.user+':'+Insta.conf.user.name+'|'+Insta.conf.user.name+']]';
//Insta.conf.paths.images = '//upload.wikimedia.org/wikipedia/' + Insta.conf.wiki.lang + '/';
// define constants
Insta.BLOCK_IMAGE = new RegExp('^\\[\\[(?:File|Image|'+Insta.conf.locale.image+
'):.*?\\|.*?(?:frame|thumbnail|thumb|none|right|left|center)', 'i');
}
Insta.dump = function(from, to)
{
if (typeof from == 'string') { from = document.getElementById(from); }
if (typeof to == 'string') { to = document.getElementById(to); }
to.innerHTML = this.convert(from.value);
};
Insta.convert = function(wiki)
{
var ll = (typeof wiki == 'string')? wiki.replace(/\r/g,'').split(/\n/): wiki, // lines of wikicode
o = '', // output
p = 0, // para flag
$r; // result of passing a regexp to $()
// some shorthands
function remain() { return ll.length; }
function sh() { return ll.shift(); } // shift
function ps(s) { o += s; } // push
// similar to C's printf, uses ? as placeholders, ?? to escape question marks
function f()
{
var i=1, a=arguments, f=a[0], o='', c, p;
for (; i<a.length; i++) {
if ((p=f.indexOf('?'))+1) {
// allow character escaping
i -= c = f.charAt(p+1)=='?' ? 1 : 0;
o += f.substring(0,p) + (c ? '?' : a[i]);
f = f.substr(p+1+c);
} else { break; }
}
return o+f;
}
function html_entities(s) {
return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
}
// Wiki text parsing to html is a nightmare.
// The below functions deliberately don't escape the ampersand since this would make it more difficult,
// and we don't absolutely need to for how we need it.
// This means that any unescaped ampersands in wikitext will remain unescaped and can cause invalid HTML.
// Browsers should all be able to handle it though.
// We also escape significant wikimarkup characters to prevent further matching on the processed text
function htmlescape_text(s) {
return s.replace(/</g,"<").replace(/>/g,">").replace(/:/g,":").replace(/\[/g,"[").replace(/]/g,"]");
}
function htmlescape_attr(s) {
return htmlescape_text(s).replace(/'/g,"'").replace(/"/g,""");
}
// return the first non matching character position between two strings
function str_imatch(a, b)
{
for (var i=0, l=Math.min(a.length, b.length); i<l; i++) {
if (a.charAt(i)!=b.charAt(i)) { break; }
}
return i;
}
// compare current line against a string or regexp
// if passed a string it will compare only the first string.length characters
// if passed a regexp the result is stored in $r
function $(c) { return (typeof c == 'string') ? (ll[0].substr(0,c.length)==c) : ($r = ll[0].match(c)); }
function $$(c) { return ll[0]==c; } // compare current line against a string
function _(p) { return ll[0].charAt(p); } // return char at pos p
function endl(s) { ps(s); sh(); }
function parse_list()
{
var prev='';
while (remain() && $(/^([*#:;]+)(.*)$/)) {
var l_match = $r;
sh();
var ipos = str_imatch(prev, l_match[1]);
// close uncontinued lists
for (var prevPos=prev.length-1; prevPos >= ipos; prevPos--) {
var pi = prev.charAt(prevPos);
if (pi=='*') { ps('</ul>'); }
else if (pi=='#') { ps('</ol>'); }
// close a dl only if the new item is not a dl item (:, ; or empty)
else if($.inArray(l_match[1].charAt(prevPos), ['','*','#'])) { ps('</dl>'); }
}
// open new lists
for (var matchPos=ipos; matchPos<l_match[1].length; matchPos++) {
var li = l_match[1].charAt(matchPos);
if (li=='*') { ps('<ul>'); }
else if (li=='#') { ps('<ol>'); }
// open a new dl only if the prev item is not a dl item (:, ; or empty)
else if ($.inArray(prev.charAt(matchPos), ['','*','#'])) { ps('<dl>'); }
}
switch (l_match[1].charAt(l_match[1].length-1)) {
case '*': case '#':
ps('<li>' + parse_inline_nowiki(l_match[2]));
break;
case ';':
ps('<dt>');
var dt_match = l_match[2].match(/(.*?)(:.*?)$/);
// handle ;dt :dd format
if (dt_match) {
ps(parse_inline_nowiki(dt_match[1]));
ll.unshift(dt_match[2]);
} else ps(parse_inline_nowiki(l_match[2]));
break;
case ':':
ps('<dd>' + parse_inline_nowiki(l_match[2]));
}
prev=l_match[1];
}
// close remaining lists
for (var i=prev.length-1; i>=0; i--) {
ps(f('</?>', (prev.charAt(i)=='*')? 'ul': ((prev.charAt(i)=='#')? 'ol': 'dl')));
}
}
function parse_table()
{
endl(f('<table>', $(/^\{\|( .*)$/)? $r[1]: ''));
for (;remain();) if ($('|')) switch (_(1)) {
case '}':
endl('</table>');
return;
case '-':
endl(f('<tr>', $(/\|-*(.*)/)[1]));
break;
default:
parse_table_data();
}
else if ($('!')) { parse_table_data(); }
else { sh(); }
}
function parse_table_data()
{
var td_line, match_i;
// 1: "|+", '|' or '+'
// 2: ??
// 3: attributes ??
// TODO: finish commenting this regexp
var td_match = sh().match(/^(\|\+|\||!)((?:([^[|]*?)\|(?!\|))?(.*))$/);
if (td_match[1] == '|+') ps('<caption');
else ps('<t' + ((td_match[1]=='|')?'d':'h'));
if (typeof td_match[3] != 'undefined') {
//ps(' ' + td_match[3])
match_i = 4;
} else match_i = 2;
ps('>');
if (td_match[1] != '|+') {
// use || or !! as a cell separator depending on context
// NOTE: when split() is passed a regexp make sure to use non-capturing brackets
td_line = td_match[match_i].split((td_match[1] == '|')? '||': /(?:\|\||!!)/);
ps(parse_inline_nowiki(td_line.shift()));
while (td_line.length) ll.unshift(td_match[1] + td_line.pop());
} else ps(td_match[match_i]);
var tc = 0, td = [];
while (remain()) {
td.push(sh());
if ($('|')) {
if (!tc) break; // we're at the outer-most level (no nested tables), skip to td parse
else if (_(1)=='}') tc--;
}
else if (!tc && $('!')) break;
else if ($('{|')) tc++;
}
if (td.length) ps(Insta.convert(td));
}
function parse_pre()
{
ps('<pre>');
do {
endl(parse_inline_nowiki(ll[0].substring(1)) + "\n");
} while (remain() && $(' '));
ps('</pre>');
}
function parse_block_image()
{
ps(parse_image(sh()));
}
function parse_image(str)
{
//<NOLITE>
// get what's in between "[[Image:" and "]]"
var tag = str.substring(str.indexOf(':') + 1, str.length - 2);
/* eslint-disable no-unused-vars */
var width;
var attr = [], filename, caption = '';
var thumb=0, frame=0, center=0;
var align='';
/* eslint-enable no-unused-vars */
if (tag.match(/\|/)) {
// manage nested links
var nesting = 0;
var last_attr;
for (var i = tag.length-1; i > 0; i--) {
if (tag.charAt(i) == '|' && !nesting) {
last_attr = tag.substr(i+1);
tag = tag.substring(0, i);
break;
} else switch (tag.substr(i-1, 2)) {
case ']]':
nesting++;
i--;
break;
case '[[':
nesting--;
i--;
}
}
attr = tag.split(/\s*\|\s*/);
attr.push(last_attr);
filename = attr.shift();
var w_match;
for (;attr.length; attr.shift()) {
w_match = attr[0].match(/^(\d*)(?:[px]*\d*)?px$/);
if (w_match) width = w_match[1];
else switch(attr[0]) {
case 'thumb':
case 'thumbnail':
thumb=true;
frame=true;
break;
case 'frame':
frame=true;
break;
case 'none':
case 'right':
case 'left':
center=false;
align=attr[0];
break;
case 'center':
center=true;
align='none';
break;
default:
if (attr.length == 1) caption = attr[0];
}
}
} else filename = tag;
return '';
//</NOLITE>
}
function parse_inline_nowiki(str)
{
var start, lastend=0;
var substart=0, nestlev=0, open, close, subloop;
var html='';
while (-1 != (start = str.indexOf('<nowiki>', substart))) {
html += parse_inline_wiki(str.substring(lastend, start));
start += 8;
substart = start;
subloop = true;
do {
open = str.indexOf('<nowiki>', substart);
close = str.indexOf('</nowiki>', substart);
if (close<=open || open==-1) {
if (close==-1) {
return html + html_entities(str.substr(start));
}
substart = close+9;
if (nestlev) {
nestlev--;
} else {
lastend = substart;
html += html_entities(str.substring(start, lastend-9));
subloop = false;
}
} else {
substart = open+8;
nestlev++;
}
} while (subloop);
}
return html + parse_inline_wiki(str.substr(lastend));
}
function parse_inline_images(str)
{
//<NOLITE>
var start, substart=0, nestlev=0;
var loop, close, open, wiki, html;
while (-1 != (start=str.indexOf('[[', substart))) {
if(str.substr(start+2).match(RegExp('^(Image|File|' + Insta.conf.locale.image + '):','i'))) {
loop=true;
substart=start;
do {
substart+=2;
close=str.indexOf(']]',substart);
open=str.indexOf('[[',substart);
if (close<=open||open==-1) {
if (close==-1) return str;
substart=close;
if (nestlev) {
nestlev--;
} else {
wiki=str.substring(start,close+2);
html=parse_image(wiki);
str=str.replace(wiki,html);
substart=start+html.length;
loop=false;
}
} else {
substart=open;
nestlev++;
}
} while (loop);
} else break;
}
//</NOLITE>
return str;
}
// the output of this function doesn't respect the FILO structure of HTML
// but since most browsers can handle it I'll save myself the hassle
function parse_inline_formatting(str)
{
var em,st,i,li,o='';
while ((i=str.indexOf("''",li))+1) {
o += str.substring(li,i);
li=i+2;
if (str.charAt(i+2)=="'") {
li++;
st=!st;
o+=st?'<strong>':'</strong>';
} else {
em=!em;
o+=em?'<em>':'</em>';
}
}
return o+str.substr(li);
}
function parse_inline_wiki(str)
{
str = parse_inline_images(str);
str = parse_inline_formatting(str);
// math
str = str.replace(/<(?:)math>(.*?)<\/math>/ig, '');
// Build a Mediawiki-formatted date string
var date = new Date();
var minutes = date.getUTCMinutes();
if (minutes < 10) minutes = '0' + minutes;
date = f("?:?, ? ? ? (UTC)", date.getUTCHours(), minutes, date.getUTCDate(), Insta.conf.locale.months[date.getUTCMonth()], date.getUTCFullYear());
// text formatting
return str.
// signatures
replace(/~{5}(?!~)/g, date).
replace(/~{4}(?!~)/g, Insta.conf.user.name+' '+date).
replace(/~{3}(?!~)/g, Insta.conf.user.name).
// [[:Category:...]], [[:Image:...]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):[^|]*?)\\]\\](\\w*)','gi'), function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2));}).
// remove straight category and interwiki tags
replace(RegExp('\\[\\[(?:'+Insta.conf.locale.category+'|'+Insta.conf.wiki.interwiki+'):.*?\\]\\]','gi'),'').
// [[:Category:...|Links]], [[:Image:...|Links]], etc...
replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|Image|File|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):.*?)\\|([^\\]]+?)\\]\\](\\w*)','gi'), function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3));}).
// [[/Relative links]]
replace(/\[\[(\/[^|]*?)\]\]/g, function($0,$1){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($1)); }).
// [[/Replaced|Relative links]]
replace(/\[\[(\/.*?)\|(.+?)\]\]/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.baseUrl + htmlescape_attr($1), htmlescape_text($2)); }).
// [[Common links]]
replace(/\[\[([^[|]*?)\]\](\w*)/g, function($0,$1,$2){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($1) + htmlescape_text($2)); }).
// [[Replaced|Links]]
replace(/\[\[([^[]*?)\|([^\]]+?)\]\](\w*)/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1), htmlescape_text($2) + htmlescape_text($3)); }).
// [[Stripped:Namespace|Namespace]]
replace(/\[\[([^\]]*?:)?(.*?)( *\(.*?\))?\|\]\]/g, function($0,$1,$2,$3){return f("<a href='?'>?</a>", Insta.conf.paths.articles + htmlescape_attr($1) + htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($2)); }).
// External links
replace(/\[(https?|news|ftp|mailto|gopher|irc):(\/*)([^\]]*?) (.*?)\]/g, function($0,$1,$2,$3,$4){return f("<a class='external' href='?:?'>?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($4)); }).
replace(/\[http:\/\/(.*?)\]/g, function($0,$1){return f("<a class='external' href='http://?'>[#]</a>", htmlescape_attr($1)); }).
replace(/\[(news|ftp|mailto|gopher|irc):(\/*)(.*?)\]/g, function($0,$1,$2,$3){return f("<a class='external' href='?:?'>?:?</a>", htmlescape_attr($1), htmlescape_attr($2) + htmlescape_attr($3), htmlescape_text($1), htmlescape_text($2) + htmlescape_text($3)); }).
replace(/(^| )(https?|news|ftp|mailto|gopher|irc):(\/*)([^ $]*[^.,!?;: $])/g, function($0,$1,$2,$3,$4){return f("?<a class='external' href='?:?'>?:?</a>", htmlescape_text($1), htmlescape_attr($2), htmlescape_attr($3) + htmlescape_attr($4), htmlescape_text($2), htmlescape_text($3) + htmlescape_text($4)); }).
replace('__NOTOC__','').
replace('__NOEDITSECTION__','');
}
// begin parsing
for (;remain();) if ($(/^(={1,6})(.*)\1(.*)$/)) {
p=0;
endl(f('<h?>?</h?>?', $r[1].length, parse_inline_nowiki($r[2]), $r[1].length, $r[3]));
} else if ($(/^[*#:;]/)) {
p=0;
parse_list();
} else if ($(' ')) {
p=0;
parse_pre();
} else if ($('{|')) {
p=0;
parse_table();
} else if ($(/^----+$/)) {
p=0;
endl('<hr />');
} else if ($(Insta.BLOCK_IMAGE)) {
p=0;
parse_block_image();
} else {
// handle paragraphs
if ($$('')) {
p = (remain()>1 && ll[1]===(''));
if (p) endl('<p><br>');
} else {
if(!p) {
ps('<p>');
p=1;
}
ps(parse_inline_nowiki(ll[0]) + ' ');
}
sh();
}
return o;
};
function wiki2html(txt,baseurl) {
Insta.conf.baseUrl=baseurl;
return Insta.convert(txt);
}
// ENDFILE: livepreview.js
// STARTFILE: pageinfo.js
//<NOLITE>
function popupFilterPageSize(data) {
return formatBytes(data.length);
}
function popupFilterCountLinks(data) {
var num=countLinks(data);
return String(num) + ' ' + ((num!=1)?popupString('wikiLinks'):popupString('wikiLink'));
}
function popupFilterCountImages(data) {
var num=countImages(data);
return String(num) + ' ' + ((num!=1)?popupString('images'):popupString('image'));
}
function popupFilterCountCategories(data) {
var num=countCategories(data);
return String(num) + ' ' + ((num!=1)?popupString('categories'):popupString('category'));
}
function popupFilterLastModified(data,download) {
var lastmod=download.lastModified;
var now=new Date();
var age=now-lastmod;
if (lastmod && getValueOf('popupLastModified')) {
return (tprintf('%s old', [formatAge(age)])).replace(RegExp(' ','g'), ' ');
}
return '';
}
function formatAge(age) {
// coerce into a number
var a=0+age, aa=a;
var seclen = 1000;
var minlen = 60*seclen;
var hourlen = 60*minlen;
var daylen = 24*hourlen;
var weeklen = 7*daylen;
var numweeks = (a-a%weeklen)/weeklen; a = a-numweeks*weeklen; var sweeks = addunit(numweeks, 'week');
var numdays = (a-a%daylen)/daylen; a = a-numdays*daylen; var sdays = addunit(numdays, 'day');
var numhours = (a-a%hourlen)/hourlen; a = a-numhours*hourlen; var shours = addunit(numhours,'hour');
var nummins = (a-a%minlen)/minlen; a = a-nummins*minlen; var smins = addunit(nummins, 'minute');
var numsecs = (a-a%seclen)/seclen; a = a-numsecs*seclen; var ssecs = addunit(numsecs, 'second');
if (aa > 4*weeklen) { return sweeks; }
if (aa > weeklen) { return sweeks + ' ' + sdays; }
if (aa > daylen) { return sdays + ' ' + shours; }
if (aa > 6*hourlen) { return shours; }
if (aa > hourlen) { return shours + ' ' + smins; }
if (aa > 10*minlen) { return smins; }
if (aa > minlen) { return smins + ' ' + ssecs; }
return ssecs;
}
function addunit(num,str) { return '' + num + ' ' + ((num!=1) ? popupString(str+'s') : popupString(str)) ;}
function runPopupFilters(list, data, download) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (list[i] && typeof list[i] == 'function') {
var s=list[i](data, download, download.owner.article);
if (s) { ret.push(s); }
}
}
return ret;
}
function getPageInfo(data, download) {
if (!data || data.length === 0) { return popupString('Empty page'); }
var popupFilters=getValueOf('popupFilters') || [];
var extraPopupFilters = getValueOf('extraPopupFilters') || [];
var pageInfoArray = runPopupFilters(popupFilters.concat(extraPopupFilters), data, download);
var pageInfo=pageInfoArray.join(', ');
if (pageInfo !== '' ) { pageInfo = upcaseFirst(pageInfo); }
return pageInfo;
}
// this could be improved!
function countLinks(wikiText) { return wikiText.split('[[').length - 1; }
// if N = # matches, n = # brackets, then
// String.parenSplit(regex) intersperses the N+1 split elements
// with Nn other elements. So total length is
// L= N+1 + Nn = N(n+1)+1. So N=(L-1)/(n+1).
function countImages(wikiText) {
return (wikiText.parenSplit(pg.re.image).length - 1) / (pg.re.imageBracketCount + 1);
}
function countCategories(wikiText) {
return (wikiText.parenSplit(pg.re.category).length - 1) / (pg.re.categoryBracketCount + 1);
}
function popupFilterStubDetect(data, download, article) {
var counts=stubCount(data, article);
if (counts.real) { return popupString('stub'); }
if (counts.sect) { return popupString('section stub'); }
return '';
}
function popupFilterDisambigDetect(data, download, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return ''; }
return (isDisambig(data, article)) ? popupString('disambig') : '';
}
function formatBytes(num) {
return (num > 949) ? (Math.round(num/100)/10+popupString('kB')) : (num +' ' + popupString('bytes')) ;
}
//</NOLITE>
// ENDFILE: pageinfo.js
// STARTFILE: titles.js
/**
@fileoverview Defines the {@link Title} class, and associated crufty functions.
<code>Title</code> deals with article titles and their various
forms. {@link Stringwrapper} is the parent class of
<code>Title</code>, which exists simply to make things a little
neater.
*/
/**
Creates a new Stringwrapper.
@constructor
@class the Stringwrapper class. This base class is not really
useful on its own; it just wraps various common string operations.
*/
function Stringwrapper() {
/**
Wrapper for this.toString().indexOf()
@param {String} x
@type integer
*/
this.indexOf=function(x){return this.toString().indexOf(x);};
/**
Returns this.value.
@type String
*/
this.toString=function(){return this.value;};
/**
Wrapper for {@link String#parenSplit} applied to this.toString()
@param {RegExp} x
@type Array
*/
this.parenSplit=function(x){return this.toString().parenSplit(x);};
/**
Wrapper for this.toString().substring()
@param {String} x
@param {String} y (optional)
@type String
*/
this.substring=function(x,y){
if (typeof y=='undefined') { return this.toString().substring(x); }
return this.toString().substring(x,y);
};
/**
Wrapper for this.toString().split()
@param {String} x
@type Array
*/
this.split=function(x){return this.toString().split(x);};
/**
Wrapper for this.toString().replace()
@param {String} x
@param {String} y
@type String
*/
this.replace=function(x,y){ return this.toString().replace(x,y); };
}
/**
Creates a new <code>Title</code>.
@constructor
@class The Title class. Holds article titles and converts them into
various forms. Also deals with anchors, by which we mean the bits
of the article URL after a # character, representing locations
within an article.
@param {String} value The initial value to assign to the
article. This must be the canonical title (see {@link
Title#value}. Omit this in the constructor and use another function
to set the title if this is unavailable.
*/
function Title(val) {
/**
The canonical article title. This must be in UTF-8 with no
entities, escaping or nasties. Also, underscores should be
replaced with spaces.
@type String
@private
*/
this.value=null;
/**
The canonical form of the anchor. This should be exactly as
it appears in the URL, i.e. with the .C3.0A bits in.
@type String
*/
this.anchor='';
this.setUtf(val);
}
Title.prototype=new Stringwrapper();
/**
Returns the canonical representation of the article title, optionally without anchor.
@param {boolean} omitAnchor
@fixme Decide specs for anchor
@return String The article title and the anchor.
*/
Title.prototype.toString=function(omitAnchor) {
return this.value + ( (!omitAnchor && this.anchor) ? '#' + this.anchorString() : '' );
};
Title.prototype.anchorString=function() {
if (!this.anchor) { return ''; }
var split=this.anchor.parenSplit(/((?:[.][0-9A-F]{2})+)/);
var len=split.length;
for (var j=1; j<len; j+=2) {
// FIXME s/decodeURI/decodeURIComponent/g ?
split[j]=decodeURIComponent(split[j].split('.').join('%')).split('_').join(' ');
}
return split.join('');
};
Title.prototype.urlAnchor=function() {
var split=this.anchor.parenSplit('/((?:[%][0-9A-F]{2})+)/');
var len=split.length;
for (var j=1; j<len; j+=2) {
split[j]=split[j].split('%').join('.');
}
return split.join('');
};
Title.prototype.anchorFromUtf=function(str) {
this.anchor=encodeURIComponent(str.split(' ').join('_'))
.split('%3A').join(':').split("'").join('%27').split('%').join('.');
};
Title.fromURL=function(h) {
return new Title().fromURL(h);
};
Title.prototype.fromURL=function(h) {
if (typeof h != 'string') {
this.value=null;
return this;
}
// NOTE : playing with decodeURI, encodeURI, escape, unescape,
// we seem to be able to replicate the IE borked encoding
// IE doesn't do this new-fangled utf-8 thing.
// and it's worse than that.
// IE seems to treat the query string differently to the rest of the url
// the query is treated as bona-fide utf8, but the first bit of the url is pissed around with
// we fix up & for all browsers, just in case.
var splitted=h.split('?');
splitted[0]=splitted[0].split('&').join('%26');
h=splitted.join('?');
var contribs=pg.re.contribs.exec(h);
if (contribs) {
if (contribs[1]=='title=') { contribs[3]=contribs[3].split('+').join(' '); }
var u=new Title(contribs[3]);
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + u.stripNamespace()));
return this;
}
var email=pg.re.email.exec(h);
if (email) {
this.setUtf(this.decodeNasties(mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(email[3]).stripNamespace()));
return this;
}
var backlinks=pg.re.backlinks.exec(h);
if (backlinks) {
this.setUtf(this.decodeNasties(new Title(backlinks[3])));
return this;
}
//A dummy title object for a Special:Diff link.
var specialdiff=pg.re.specialdiff.exec(h);
if (specialdiff) {
this.setUtf(this.decodeNasties(new Title(mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Diff')));
return this;
}
// no more special cases to check --
// hopefully it's not a disguised user-related or specially treated special page
var m=pg.re.main.exec(h);
if(m === null) { this.value=null; }
else {
var fromBotInterface = /[?](.+[&])?title=/.test(h);
if (fromBotInterface) {
m[2]=m[2].split('+').join('_');
}
var extracted = m[2] + (m[3] ? '#' + m[3] : '');
if (pg.flag.isSafari && /%25[0-9A-Fa-f]{2}/.test(extracted)) {
// Fix Safari issue
// Safari sometimes encodes % as %25 in UTF-8 encoded strings like %E5%A3 -> %25E5%25A3.
this.setUtf(decodeURIComponent(unescape(extracted)));
} else {
this.setUtf(this.decodeNasties(extracted));
}
}
return this;
};
Title.prototype.decodeNasties=function(txt) {
var ret= this.decodeEscapes(decodeURI(txt));
ret = ret.replace(/[_ ]*$/, '');
return ret;
};
Title.prototype.decodeEscapes=function(txt) {
var split=txt.parenSplit(/((?:[%][0-9A-Fa-f]{2})+)/);
var len=split.length;
for (var i=1; i<len; i=i+2) {
// FIXME is decodeURIComponent better?
split[i]=unescape(split[i]);
}
return split.join('');
};
Title.fromAnchor=function(a) {
return new Title().fromAnchor(a);
};
Title.prototype.fromAnchor=function(a) {
if (!a) { this.value=null; return this; }
return this.fromURL(a.href);
};
Title.fromWikiText=function(txt) {
return new Title().fromWikiText(txt);
};
Title.prototype.fromWikiText=function(txt) {
// FIXME - testing needed
txt=myDecodeURI(txt);
this.setUtf(txt);
return this;
};
Title.prototype.hintValue=function(){
if(!this.value) { return ''; }
return safeDecodeURI(this.value);
};
//<NOLITE>
Title.prototype.toUserName=function(withNs) {
if (this.namespaceId() != pg.nsUserId && this.namespaceId() != pg.nsUsertalkId) {
this.value=null;
return;
}
this.value = (withNs ? mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' : '') + this.stripNamespace().split('/')[0];
};
Title.prototype.userName=function(withNs) {
var t=(new Title(this.value));
t.toUserName(withNs);
if (t.value) { return t; }
return null;
};
Title.prototype.toTalkPage=function() {
// convert article to a talk page, or if we can't, return null
// In other words: return null if this ALREADY IS a talk page
// and return the corresponding talk page otherwise
//
// Per https://www.mediawiki.org/wiki/Manual:Namespace#Subject_and_talk_namespaces
// * All discussion namespaces have odd-integer indices
// * The discussion namespace index for a specific namespace with index n is n + 1
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId>=0 && namespaceId % 2 === 0) //non-special and subject namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId+1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
//</NOLITE>
// Return canonical, localized namespace
Title.prototype.namespace=function() {
return mw.config.get('wgFormattedNamespaces')[this.namespaceId()];
};
Title.prototype.namespaceId=function() {
var n=this.value.indexOf(':');
if (n<0) { return 0; } //mainspace
var namespaceId = mw.config.get('wgNamespaceIds')[this.value.substring(0,n).split(' ').join('_').toLowerCase()];
if (typeof namespaceId=='undefined') return 0; //mainspace
return namespaceId;
};
//<NOLITE>
Title.prototype.talkPage=function() {
var t=new Title(this.value);
t.toTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.isTalkPage=function() {
if (this.talkPage()===null) { return true; }
return false;
};
Title.prototype.toArticleFromTalkPage=function() {
//largely copy/paste from toTalkPage above.
if (this.value === null) { return null; }
var namespaceId = this.namespaceId();
if (namespaceId >= 0 && namespaceId % 2 == 1) //non-special and talk namespace
{
var localizedNamespace = mw.config.get('wgFormattedNamespaces')[namespaceId-1];
if (typeof localizedNamespace!=='undefined')
{
if (localizedNamespace === '') {
this.value = this.stripNamespace();
} else {
this.value = localizedNamespace.split(' ').join('_') + ':' + this.stripNamespace();
}
return this.value;
}
}
this.value=null;
return null;
};
Title.prototype.articleFromTalkPage=function() {
var t=new Title(this.value);
t.toArticleFromTalkPage();
if (t.value) { return t; }
return null;
};
Title.prototype.articleFromTalkOrArticle=function() {
var t=new Title(this.value);
if ( t.toArticleFromTalkPage() ) { return t; }
return this;
};
Title.prototype.isIpUser=function() {
return pg.re.ipUser.test(this.userName());
};
//</NOLITE>
Title.prototype.stripNamespace=function(){ // returns a string, not a Title
var n=this.value.indexOf(':');
if (n<0) { return this.value; }
var namespaceId = this.namespaceId();
if (namespaceId === pg.nsMainspaceId) return this.value;
return this.value.substring(n+1);
};
Title.prototype.setUtf=function(value){
if (!value) { this.value=''; return; }
var anch=value.indexOf('#');
if(anch < 0) { this.value=value.split('_').join(' '); this.anchor=''; return; }
this.value=value.substring(0,anch).split('_').join(' ');
this.anchor=value.substring(anch+1);
this.ns=null; // wait until namespace() is called
};
Title.prototype.setUrl=function(urlfrag) {
var anch=urlfrag.indexOf('#');
this.value=safeDecodeURI(urlfrag.substring(0,anch));
this.anchor=this.value.substring(anch+1);
};
Title.prototype.append=function(x){
this.setUtf(this.value + x);
};
Title.prototype.urlString=function(x) {
if(!x) { x={}; }
var v=this.toString(true);
if (!x.omitAnchor && this.anchor) { v+= '#' + this.urlAnchor(); }
if (!x.keepSpaces) { v=v.split(' ').join('_'); }
return encodeURI(v).split('&').join('%26').split('?').join('%3F').split('+').join('%2B');
};
Title.prototype.removeAnchor=function() {
return new Title(this.toString(true));
};
Title.prototype.toUrl=function() {
return pg.wiki.titlebase + this.urlString();
};
function parseParams(url) {
var specialDiff = pg.re.specialdiff.exec(url);
if (specialDiff)
{
var split= specialDiff[1].split('/');
if (split.length==1) return {oldid:split[0], diff: 'prev'};
else if (split.length==2) return {oldid: split[0], diff: split[1]};
}
var ret={};
if (url.indexOf('?')==-1) { return ret; }
url = url.split('#')[0];
var s=url.split('?').slice(1).join();
var t=s.split('&');
for (var i=0; i<t.length; ++i) {
var z=t[i].split('=');
z.push(null);
ret[z[0]]=z[1];
}
//Diff revision with no oldid is interpreted as a diff to the previous revision by MediaWiki
if (ret.diff && typeof(ret.oldid)==='undefined')
{
ret.oldid = "prev";
}
//Documentation seems to say something different, but oldid can also accept prev/next, and Echo is emitting such URLs. Simple fixup during parameter decoding:
if (ret.oldid && (ret.oldid==='prev' || ret.oldid==='next' || ret.oldid==='cur'))
{
var helper = ret.diff;
ret.diff = ret.oldid;
ret.oldid = helper;
}
return ret;
}
// (a) myDecodeURI (first standard decodeURI, then pg.re.urlNoPopup)
// (b) change spaces to underscores
// (c) encodeURI (just the straight one, no pg.re.urlNoPopup)
function myDecodeURI (str) {
var ret;
// FIXME decodeURIComponent??
try { ret=decodeURI(str.toString()); }
catch (summat) { return str; }
for (var i=0; i<pg.misc.decodeExtras.length; ++i) {
var from=pg.misc.decodeExtras[i].from;
var to=pg.misc.decodeExtras[i].to;
ret=ret.split(from).join(to);
}
return ret;
}
function safeDecodeURI(str) { var ret=myDecodeURI(str); return ret || str; }
///////////
// TESTS //
///////////
//<NOLITE>
function isDisambig(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
return ! article.isTalkPage() && pg.re.disambig.test(data);
}
function stubCount(data, article) {
if (!getValueOf('popupAllDabsStubs') && article.namespace()) { return false; }
var sectStub=0;
var realStub=0;
if (pg.re.stub.test(data)) {
var s=data.parenSplit(pg.re.stub);
for (var i=1; i<s.length; i=i+2) {
if (s[i]) { ++sectStub; }
else { ++realStub; }
}
}
return { real: realStub, sect: sectStub };
}
function isValidImageName(str){ // extend as needed...
return ( str.indexOf('{') == -1 );
}
function isInStrippableNamespace(article) {
// Does the namespace allow subpages
// Note, would be better if we had access to wgNamespacesWithSubpages
return ( article.namespaceId() !== 0 );
}
function isInMainNamespace(article) { return article.namespaceId() === 0; }
function anchorContainsImage(a) {
// iterate over children of anchor a
// see if any are images
if (a === null) { return false; }
var kids=a.childNodes;
for (var i=0; i<kids.length; ++i) { if (kids[i].nodeName=='IMG') { return true; } }
return false;
}
//</NOLITE>
function isPopupLink(a) {
// NB for performance reasons, TOC links generally return true
// they should be stripped out later
if (!markNopopupSpanLinks.done) { markNopopupSpanLinks(); }
if (a.inNopopupSpan) { return false; }
// FIXME is this faster inline?
if (a.onmousedown || a.getAttribute('nopopup')) { return false; }
var h=a.href;
if (h === document.location.href+'#') { return false; }
if (!pg.re.basenames.test(h)) { return false; }
if (!pg.re.urlNoPopup.test(h)) { return true; }
return (
(pg.re.email.test(h) || pg.re.contribs.test(h) || pg.re.backlinks.test(h) || pg.re.specialdiff.test(h)) &&
h.indexOf('&limit=') == -1 );
}
function markNopopupSpanLinks() {
if( !getValueOf('popupOnlyArticleLinks'))
fixVectorMenuPopups();
var s = $('.nopopups').toArray();
for (var i=0; i<s.length; ++i) {
var as=s[i].getElementsByTagName('a');
for (var j=0; j<as.length; ++j) {
as[j].inNopopupSpan=true;
}
}
markNopopupSpanLinks.done=true;
}
function fixVectorMenuPopups() {
$('div.vectorMenu h3:first a:first').prop('inNopopupSpan', true);
}
// ENDFILE: titles.js
// STARTFILE: getpage.js
//////////////////////////////////////////////////
// Wiki-specific downloading
//
// Schematic for a getWiki call
//
// getPageWithCaching
// |
// false | true
// getPage<-[findPictureInCache]->-onComplete(a fake download)
// \.
// (async)->addPageToCache(download)->-onComplete(download)
// check cache to see if page exists
function getPageWithCaching(url, onComplete, owner) {
log('getPageWithCaching, url='+url);
var i=findInPageCache(url);
var d;
if (i > -1) {
d=fakeDownload(url, owner.idNumber, onComplete,
pg.cache.pages[i].data, pg.cache.pages[i].lastModified,
owner);
} else {
d=getPage(url, onComplete, owner);
if (d && owner && owner.addDownload) {
owner.addDownload(d);
d.owner=owner;
}
}
}
function getPage(url, onComplete, owner) {
log('getPage');
var callback= function (d) { if (!d.aborted) {addPageToCache(d); onComplete(d);} };
return startDownload(url, owner.idNumber, callback);
}
function findInPageCache(url) {
for (var i=0; i<pg.cache.pages.length; ++i) {
if (url==pg.cache.pages[i].url) { return i; }
}
return -1;
}
function addPageToCache(download) {
log('addPageToCache '+download.url);
var page = {url: download.url, data: download.data, lastModified: download.lastModified};
return pg.cache.pages.push(page);
}
// ENDFILE: getpage.js
// STARTFILE: parensplit.js
//////////////////////////////////////////////////
// parenSplit
// String.prototype.parenSplit should do what ECMAscript says String.prototype.split does,
// interspersing paren matches (regex capturing groups) between the split elements.
// i.e. 'abc'.split(/(b)/)) should return ['a','b','c'], not ['a','c']
if (String('abc'.split(/(b)/))!='a,b,c') {
// broken String.split, e.g. konq, IE < 10
String.prototype.parenSplit=function (re) {
re=nonGlobalRegex(re);
var s=this;
var m=re.exec(s);
var ret=[];
while (m && s) {
// without the following loop, we have
// 'ab'.parenSplit(/a|(b)/) != 'ab'.split(/a|(b)/)
for(var i=0; i<m.length; ++i) {
if (typeof m[i]=='undefined') m[i]='';
}
ret.push(s.substring(0,m.index));
ret = ret.concat(m.slice(1));
s=s.substring(m.index + m[0].length);
m=re.exec(s);
}
ret.push(s);
return ret;
};
} else {
String.prototype.parenSplit=function (re) { return this.split(re); };
String.prototype.parenSplit.isNative=true;
}
function nonGlobalRegex(re) {
var s=re.toString();
var flags='';
for (var j=s.length; s.charAt(j) != '/'; --j) {
if (s.charAt(j) != 'g') { flags += s.charAt(j); }
}
var t=s.substring(1,j);
return RegExp(t,flags);
}
// ENDFILE: parensplit.js
// STARTFILE: tools.js
// IE madness with encoding
// ========================
//
// suppose throughout that the page is in utf8, like wikipedia
//
// if a is an anchor DOM element and a.href should consist of
//
// http://host.name.here/wiki/foo?bar=baz
//
// then IE gives foo as "latin1-encoded" utf8; we have foo = decode_utf8(decodeURI(foo_ie))
// but IE gives bar=baz correctly as plain utf8
//
// ---------------------------------
//
// IE's xmlhttp doesn't understand utf8 urls. Have to use encodeURI here.
//
// ---------------------------------
//
// summat else
// Source: http://aktuell.de.selfhtml.org/artikel/javascript/utf8b64/utf8.htm
//<NOLITE>
function getJsObj(json) {
try {
var json_ret = JSON.parse(json);
if( json_ret.warnings ) {
for( var w=0; w < json_ret.warnings.length; w++ ) {
if( json_ret.warnings[w]['*'] ) {
log( json_ret.warnings[w]['*'] );
} else {
log( json_ret.warnings[w]['warnings'] );
}
}
} else if ( json_ret.error ) {
errlog( json_ret.error.code + ': ' + json_ret.error.info );
}
return json_ret;
} catch (someError) {
errlog('Something went wrong with getJsObj, json='+json);
return 1;
}
}
function anyChild(obj) {
for (var p in obj) {
return obj[p];
}
return null;
}
//</NOLITE>
function upcaseFirst(str) {
if (typeof str != typeof '' || str === '') return '';
return str.charAt(0).toUpperCase() + str.substring(1);
}
function findInArray(arr, foo) {
if (!arr || !arr.length) { return -1; }
var len=arr.length;
for (var i=0; i<len; ++i) { if (arr[i]==foo) { return i; } }
return -1;
}
/* eslint-disable no-unused-vars */
function nextOne (array, value) {
// NB if the array has two consecutive entries equal
// then this will loop on successive calls
var i=findInArray(array, value);
if (i<0) { return null; }
return array[i+1];
}
/* eslint-enable no-unused-vars */
function literalizeRegex(str){
return mw.util.escapeRegExp(str);
}
String.prototype.entify=function() {
//var shy='­';
return this.split('&').join('&').split('<').join('<').split('>').join('>'/*+shy*/).split('"').join('"');
};
// Array filter function
function removeNulls(val) { return val !== null; }
function joinPath(list) {
return list.filter(removeNulls).join('/');
}
function simplePrintf(str, subs) {
if (!str || !subs) { return str; }
var ret=[];
var s=str.parenSplit(/(%s|\$[0-9]+)/);
var i=0;
do {
ret.push(s.shift());
if ( !s.length ) { break; }
var cmd=s.shift();
if (cmd == '%s') {
if ( i < subs.length ) { ret.push(subs[i]); } else { ret.push(cmd); }
++i;
} else {
var j=parseInt( cmd.replace('$', ''), 10 ) - 1;
if ( j > -1 && j < subs.length ) { ret.push(subs[j]); } else { ret.push(cmd); }
}
} while (s.length > 0);
return ret.join('');
}
/* eslint-disable no-unused-vars */
function isString(x) { return (typeof x === 'string' || x instanceof String); }
function isNumber(x) { return (typeof x === 'number' || x instanceof Number); }
function isRegExp(x) { return x instanceof RegExp; }
function isArray (x) { return x instanceof Array; }
function isObject(x) { return x instanceof Object; }
function isFunction(x) {
return !isRegExp(x) && (typeof x === 'function' || x instanceof Function);
}
/* eslint-enable no-unused-vars */
function repeatString(s,mult) {
var ret='';
for (var i=0; i<mult; ++i) { ret += s; }
return ret;
}
function zeroFill(s, min) {
min = min || 2;
var t=s.toString();
return repeatString('0', min - t.length) + t;
}
function map(f, o) {
if (isArray(o)) { return map_array(f,o); }
return map_object(f,o);
}
function map_array(f,o) {
var ret=[];
for (var i=0; i<o.length; ++i) {
ret.push(f(o[i]));
}
return ret;
}
function map_object(f,o) {
var ret={};
for (var i in o) { ret[o]=f(o[i]); }
return ret;
}
pg.escapeQuotesHTML = function ( text ) {
return text
.replace(/&/g, "&")
.replace(/"/g, """)
.replace(/</g, "<")
.replace(/>/g, ">");
};
// ENDFILE: tools.js
// STARTFILE: dab.js
//<NOLITE>
//////////////////////////////////////////////////
// Dab-fixing code
//
function retargetDab(newTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) {
log('retargetDab: newTarget='+newTarget + ' oldTarget=' + oldTarget);
return changeLinkTargetLink(
{newTarget: newTarget,
text: newTarget.split(' ').join(' '),
hint: tprintf('disambigHint', [newTarget]),
summary: simplePrintf(
getValueOf('popupFixDabsSummary'), [friendlyCurrentArticleName, newTarget ]),
clickButton: getValueOf('popupDabsAutoClick'), minor: true, oldTarget: oldTarget,
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit});
}
function listLinks(wikitext, oldTarget, titleToEdit) {
// mediawiki strips trailing spaces, so we do the same
// testcase: https://en.wikipedia.org/w/index.php?title=Radial&oldid=97365633
var reg=RegExp('\\[\\[([^|]*?) *(\\||\\]\\])', 'gi');
var ret=[];
var splitted=wikitext.parenSplit(reg);
// ^[a-z]+ should match interwiki links, hopefully (case-insensitive)
// and ^[a-z]* should match those and [[:Category...]] style links too
var omitRegex=RegExp('^[a-z]*:|^[Ss]pecial:|^[Ii]mage|^[Cc]ategory');
var friendlyCurrentArticleName= oldTarget.toString();
var wikPos = getValueOf('popupDabWiktionary');
for (var i=1; i<splitted.length; i=i+3) {
if (typeof splitted[i] == typeof 'string' && splitted[i].length>0 && !omitRegex.test(splitted[i])) {
ret.push( retargetDab(splitted[i], oldTarget, friendlyCurrentArticleName, titleToEdit) );
} /* if */
} /* for loop */
ret = rmDupesFromSortedList(ret.sort());
if (wikPos) {
var wikTarget='wiktionary:' +
friendlyCurrentArticleName.replace( RegExp('^(.+)\\s+[(][^)]+[)]\\s*$'), '$1' );
var meth;
if (wikPos.toLowerCase() == 'first') { meth = 'unshift'; }
else { meth = 'push'; }
ret[meth]( retargetDab(wikTarget, oldTarget, friendlyCurrentArticleName, titleToEdit) );
}
ret.push(changeLinkTargetLink(
{ newTarget: null,
text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this disambig page from this article"),
clickButton: getValueOf('popupDabsAutoClick'), oldTarget: oldTarget,
summary: simplePrintf(getValueOf('popupRmDabLinkSummary'), [friendlyCurrentArticleName]),
watch: getValueOf('popupWatchDisambiggedPages'),
title: titleToEdit
}));
return ret;
}
function rmDupesFromSortedList(list) {
var ret=[];
for (var i=0; i<list.length; ++i) {
if (ret.length === 0 || list[i]!=ret[ret.length-1]) { ret.push(list[i]); }
}
return ret;
}
function makeFixDab(data, navpop) {
// grab title from parent popup if there is one; default exists in changeLinkTargetLink
var titleToEdit=(navpop.parentPopup && navpop.parentPopup.article.toString());
var list=listLinks(data, navpop.originalArticle, titleToEdit);
if (list.length === 0) { log('listLinks returned empty list'); return null; }
var html='<hr />' + popupString('Click to disambiguate this link to:') + '<br>';
html+=list.join(', ');
return html;
}
function makeFixDabs(wikiText, navpop) {
if (getValueOf('popupFixDabs') && isDisambig(wikiText, navpop.article) &&
Title.fromURL(location.href).namespaceId() != pg.nsSpecialId &&
navpop.article.talkPage() ) {
setPopupHTML(makeFixDab(wikiText, navpop), 'popupFixDab', navpop.idNumber);
}
}
function popupRedlinkHTML(article) {
return changeLinkTargetLink(
{ newTarget: null, text: popupString('remove this link').split(' ').join(' '),
hint: popupString("remove all links to this page from this article"),
clickButton: getValueOf('popupRedlinkAutoClick'),
oldTarget: article.toString(),
summary: simplePrintf(getValueOf('popupRedlinkSummary'), [article.toString()])});
}
//</NOLITE>
// ENDFILE: dab.js
// STARTFILE: htmloutput.js
// this has to use a timer loop as we don't know if the DOM element exists when we want to set the text
function setPopupHTML (str, elementId, popupId, onSuccess, append) {
if (typeof popupId === 'undefined') {
//console.error('popupId is not defined in setPopupHTML, html='+str.substring(0,100));
popupId = pg.idNumber;
}
var popupElement=document.getElementById(elementId+popupId);
if (popupElement) {
if (!append) { popupElement.innerHTML=''; }
if (isString(str)) {
popupElement.innerHTML+=str;
} else {
popupElement.appendChild(str);
}
if (onSuccess) { onSuccess(); }
setTimeout(checkPopupPosition, 100);
return true;
} else {
// call this function again in a little while...
setTimeout(function(){
setPopupHTML(str,elementId,popupId,onSuccess);
}, 600);
}
return null;
}
//<NOLITE>
function setPopupTrailer(str,id) {return setPopupHTML(str, 'popupData', id);}
//</NOLITE>
// args.navpopup is mandatory
// optional: args.redir, args.redirTarget
// FIXME: ye gods, this is ugly stuff
function fillEmptySpans(args) {
// if redir is present and true then redirTarget is mandatory
var redir=true;
var rcid;
if (typeof args != 'object' || typeof args.redir == 'undefined' || !args.redir) { redir=false; }
var a=args.navpopup.parentAnchor;
var article, hint=null, oldid=null, params={};
if (redir && typeof args.redirTarget == typeof {}) {
article=args.redirTarget;
//hint=article.hintValue();
} else {
article=(new Title()).fromAnchor(a);
hint=a.originalTitle || article.hintValue();
params=parseParams(a.href);
oldid=(getValueOf('popupHistoricalLinks')) ? params.oldid : null;
rcid=params.rcid;
}
var x={ article:article, hint: hint, oldid: oldid, rcid: rcid, navpop:args.navpopup, params:params };
var structure=pg.structures[getValueOf('popupStructure')];
if (typeof structure != 'object') {
setPopupHTML('popupError', 'Unknown structure (this should never happen): '+
pg.option.popupStructure, args.navpopup.idNumber);
return;
}
var spans=flatten(pg.misc.layout);
var numspans = spans.length;
var redirs=pg.misc.redirSpans;
for (var i=0; i<numspans; ++i) {
var found = redirs && (redirs.indexOf( spans[i] ) !== -1);
//log('redir='+redir+', found='+found+', spans[i]='+spans[i]);
if ( (found && !redir) || (!found && redir) ) {
//log('skipping this set of the loop');
continue;
}
var structurefn=structure[spans[i]];
var setfn = setPopupHTML;
if (getValueOf('popupActiveNavlinks') &&
(spans[i].indexOf('popupTopLinks')===0 || spans[i].indexOf('popupRedirTopLinks')===0)
) {
setfn = setPopupTipsAndHTML;
}
switch (typeof structurefn) {
case 'function':
log('running '+spans[i]+'({article:'+x.article+', hint:'+x.hint+', oldid: '+x.oldid+'})');
setfn(structurefn(x), spans[i], args.navpopup.idNumber);
break;
case 'string':
setfn(structurefn, spans[i], args.navpopup.idNumber);
break;
default:
errlog('unknown thing with label '+spans[i] + ' (span index was ' + i + ')');
break;
}
}
}
// flatten an array
function flatten(list, start) {
var ret=[];
if (typeof start == 'undefined') { start=0; }
for (var i=start; i<list.length; ++i) {
if (typeof list[i] == typeof []) {
return ret.concat(flatten(list[i])).concat(flatten(list, i+1));
}
else { ret.push(list[i]); }
}
return ret;
}
// Generate html for whole popup
function popupHTML (a) {
getValueOf('popupStructure');
var structure=pg.structures[pg.option.popupStructure];
if (typeof structure != 'object') {
//return 'Unknown structure: '+pg.option.popupStructure;
// override user choice
pg.option.popupStructure=pg.optionDefault.popupStructure;
return popupHTML(a);
}
if (typeof structure.popupLayout != 'function') { return 'Bad layout'; }
pg.misc.layout=structure.popupLayout();
if (typeof structure.popupRedirSpans === 'function') { pg.misc.redirSpans=structure.popupRedirSpans(); }
else { pg.misc.redirSpans=[]; }
return makeEmptySpans(pg.misc.layout, a.navpopup);
}
function makeEmptySpans (list, navpop) {
var ret='';
for (var i=0; i<list.length; ++i) {
if (typeof list[i] == typeof '') {
ret += emptySpanHTML(list[i], navpop.idNumber, 'div');
} else if (typeof list[i] == typeof [] && list[i].length > 0 ) {
ret = ret.parenSplit(RegExp('(</[^>]*?>$)')).join(makeEmptySpans(list[i], navpop));
} else if (typeof list[i] == typeof {} && list[i].nodeType ) {
ret += emptySpanHTML(list[i].name, navpop.idNumber, list[i].nodeType);
}
}
return ret;
}
function emptySpanHTML(name, id, tag, classname) {
tag = tag || 'span';
if (!classname) { classname = emptySpanHTML.classAliases[name]; }
classname = classname || name;
if (name == getValueOf('popupDragHandle')) { classname += ' popupDragHandle'; }
return simplePrintf('<%s id="%s" class="%s"></%s>', [tag, name + id, classname, tag]);
}
emptySpanHTML.classAliases={ 'popupSecondPreview': 'popupPreview' };
// generate html for popup image
// <a id="popupImageLinkn"><img id="popupImagen">
// where n=idNumber
function imageHTML(article, idNumber) {
return simplePrintf('<a id="popupImageLink$1">' +
'<img align="right" valign="top" id="popupImg$1" style="display: none;"></img>' +
'</a>', [ idNumber ]);
}
function popTipsSoonFn(id, when, popData) {
if (!when) { when=250; }
var popTips=function(){ setupTooltips(document.getElementById(id), false, true, popData); };
return function() { setTimeout( popTips, when, popData ); };
}
function setPopupTipsAndHTML(html, divname, idnumber, popData) {
setPopupHTML(html, divname, idnumber,
getValueOf('popupSubpopups') ?
popTipsSoonFn(divname + idnumber, null, popData) :
null);
}
// ENDFILE: htmloutput.js
// STARTFILE: mouseout.js
//////////////////////////////////////////////////
// fuzzy checks
function fuzzyCursorOffMenus(x,y, fuzz, parent) {
if (!parent) { return null; }
var uls=parent.getElementsByTagName('ul');
for (var i=0; i<uls.length; ++i) {
if (uls[i].className=='popup_menu') {
if (uls[i].offsetWidth > 0) return false;
} // else {document.title+='.';}
}
return true;
}
function checkPopupPosition () { // stop the popup running off the right of the screen
// FIXME avoid pg.current.link
if (pg.current.link && pg.current.link.navpopup)
pg.current.link.navpopup.limitHorizontalPosition();
}
function mouseOutWikiLink () {
//console ('mouseOutWikiLink');
var a=this;
removeModifierKeyHandler(a);
if (a.navpopup === null || typeof a.navpopup === 'undefined') return;
if ( ! a.navpopup.isVisible() ) {
a.navpopup.banish();
return;
}
restoreTitle(a);
Navpopup.tracker.addHook(posCheckerHook(a.navpopup));
}
function posCheckerHook(navpop) {
return function() {
if (!navpop.isVisible()) { return true; /* remove this hook */ }
if (Navpopup.tracker.dirty) {
return false;
}
var x=Navpopup.tracker.x, y=Navpopup.tracker.y;
var mouseOverNavpop = navpop.isWithin(x,y,navpop.fuzz, navpop.mainDiv) ||
!fuzzyCursorOffMenus(x,y,navpop.fuzz, navpop.mainDiv);
// FIXME it'd be prettier to do this internal to the Navpopup objects
var t=getValueOf('popupHideDelay');
if (t) { t = t * 1000; }
if (!t) {
if(!mouseOverNavpop) {
if(navpop.parentAnchor) {
restoreTitle( navpop.parentAnchor );
}
navpop.banish();
return true; /* remove this hook */
}
return false;
}
// we have a hide delay set
var d=+(new Date());
if ( !navpop.mouseLeavingTime ) {
navpop.mouseLeavingTime = d;
return false;
}
if ( mouseOverNavpop ) {
navpop.mouseLeavingTime=null;
return false;
}
if (d - navpop.mouseLeavingTime > t) {
navpop.mouseLeavingTime=null;
navpop.banish(); return true; /* remove this hook */
}
return false;
};
}
function runStopPopupTimer(navpop) {
// at this point, we should have left the link but remain within the popup
// so we call this function again until we leave the popup.
if (!navpop.stopPopupTimer) {
navpop.stopPopupTimer=setInterval(posCheckerHook(navpop), 500);
navpop.addHook(function(){clearInterval(navpop.stopPopupTimer);},
'hide', 'before');
}
}
// ENDFILE: mouseout.js
// STARTFILE: previewmaker.js
/**
@fileoverview
Defines the {@link Previewmaker} object, which generates short previews from wiki markup.
*/
/**
Creates a new Previewmaker
@constructor
@class The Previewmaker class. Use an instance of this to generate short previews from Wikitext.
@param {String} wikiText The Wikitext source of the page we wish to preview.
@param {String} baseUrl The url we should prepend when creating relative urls.
@param {Navpopup} owner The navpop associated to this preview generator
*/
function Previewmaker(wikiText, baseUrl, owner) {
/** The wikitext which is manipulated to generate the preview. */
this.originalData=wikiText;
this.baseUrl=baseUrl;
this.owner=owner;
this.maxCharacters=getValueOf('popupMaxPreviewCharacters');
this.maxSentences=getValueOf('popupMaxPreviewSentences');
this.setData();
}
Previewmaker.prototype.setData=function() {
var maxSize=Math.max(10000, 2*this.maxCharacters);
this.data=this.originalData.substring(0,maxSize);
};
/** Remove HTML comments
@private
*/
Previewmaker.prototype.killComments = function () {
// this also kills one trailing newline, eg [[diamyo]]
this.data=this.data.replace(RegExp('^<!--[^$]*?-->\\n|\\n<!--[^$]*?-->(?=\\n)|<!--[^$]*?-->', 'g'), '');
};
/**
@private
*/
Previewmaker.prototype.killDivs = function () {
// say goodbye, divs (can be nested, so use * not *?)
this.data=this.data.replace(RegExp('< *div[^>]* *>[\\s\\S]*?< */ *div *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killGalleries = function () {
this.data=this.data.replace(RegExp('< *gallery[^>]* *>[\\s\\S]*?< */ *gallery *>',
'gi'), '');
};
/**
@private
*/
Previewmaker.prototype.kill = function(opening, closing, subopening, subclosing, repl) {
var oldk=this.data;
var k=this.killStuff(this.data, opening, closing, subopening, subclosing, repl);
while (k.length < oldk.length) {
oldk=k;
k=this.killStuff(k, opening, closing, subopening, subclosing, repl);
}
this.data=k;
};
/**
@private
*/
Previewmaker.prototype.killStuff = function (txt, opening, closing, subopening, subclosing, repl) {
var op=this.makeRegexp(opening);
var cl=this.makeRegexp(closing, '^');
var sb=subopening ? this.makeRegexp(subopening, '^') : null;
var sc=subclosing ? this.makeRegexp(subclosing, '^') : cl;
if (!op || !cl) {
alert('Navigation Popups error: op or cl is null! something is wrong.');
return;
}
if (!op.test(txt)) { return txt; }
var ret='';
var opResult = op.exec(txt);
ret = txt.substring(0,opResult.index);
txt=txt.substring(opResult.index+opResult[0].length);
var depth = 1;
while (txt.length > 0) {
var removal=0;
if (depth==1 && cl.test(txt)) {
depth--;
removal=cl.exec(txt)[0].length;
} else if (depth > 1 && sc.test(txt)) {
depth--;
removal=sc.exec(txt)[0].length;
}else if (sb && sb.test(txt)) {
depth++;
removal=sb.exec(txt)[0].length;
}
if ( !removal ) { removal = 1; }
txt=txt.substring(removal);
if (depth === 0) { break; }
}
return ret + (repl || '') + txt;
};
/**
@private
*/
Previewmaker.prototype.makeRegexp = function (x, prefix, suffix) {
prefix = prefix || '';
suffix = suffix || '';
var reStr='';
var flags='';
if (isString(x)) {
reStr=prefix + literalizeRegex(x) + suffix;
} else if (isRegExp(x)) {
var s=x.toString().substring(1);
var sp=s.split('/');
flags=sp[sp.length-1];
sp[sp.length-1]='';
s=sp.join('/');
s=s.substring(0,s.length-1);
reStr= prefix + s + suffix;
} else {
log ('makeRegexp failed');
}
log ('makeRegexp: got reStr=' + reStr + ', flags=' + flags);
return RegExp(reStr, flags);
};
/**
@private
*/
Previewmaker.prototype.killBoxTemplates = function () {
// taxobox removal... in fact, there's a saudiprincebox_begin, so let's be more general
// also, have float_begin, ... float_end
this.kill(RegExp('[{][{][^{}\\s|]*?(float|box)[_ ](begin|start)', 'i'), /[}][}]\s*/, '{{');
// infoboxes etc
// from User:Zyxw/popups.js: kill frames too
this.kill(RegExp('[{][{][^{}\\s|]*?(infobox|elementbox|frame)[_ ]', 'i'), /[}][}]\s*/, '{{');
};
/**
@private
*/
Previewmaker.prototype.killTemplates = function () {
// the plus sign is to avoid transcluding [[Template:', ']]
this.kill('{'+'{', '}'+'}', '{', '}', ' ');
};
/**
@private
*/
Previewmaker.prototype.killTables = function () {
// tables are bad, too
// this can be slow, but it's an inprovement over a browser hang
// torture test: [[Comparison_of_Intel_Central_Processing_Units]]
this.kill('{|', /[|]}\s*/, '{|');
this.kill(/<table.*?>/i, /<\/table.*?>/i, /<table.*?>/i);
// remove lines starting with a pipe for the hell of it (?)
this.data=this.data.replace(RegExp('^[|].*$', 'mg'), '');
};
/**
@private
*/
Previewmaker.prototype.killImages = function () {
var forbiddenNamespaceAliases = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=pg.nsImageId && _namespaceId!=pg.nsCategoryId) return;
forbiddenNamespaceAliases.push(_localizedNamespaceLc.split(' ').join('[ _]')); //todo: escape regexp fragments!
});
// images and categories are a nono
this.kill(RegExp('[[][[]\\s*(' + forbiddenNamespaceAliases.join('|') + ')\\s*:', 'i'),
/\]\]\s*/, '[', ']');
};
/**
@private
*/
Previewmaker.prototype.killHTML = function () {
// kill <ref ...>...</ref>
this.kill(/<ref\b[^/>]*?>/i, /<\/ref>/i);
// let's also delete entire lines starting with <. it's worth a try.
this.data=this.data.replace(RegExp('(^|\\n) *<.*', 'g'), '\n');
// and those pesky html tags, but not <nowiki> or <blockquote>
var splitted=this.data.parenSplit(/(<[\w\W]*?(?:>|$|(?=<)))/);
var len=splitted.length;
for (var i=1; i<len; i=i+2) {
switch (splitted[i]) {
case '<nowiki>':
case '</nowiki>':
case '<blockquote>':
case '</blockquote>':
break;
default:
splitted[i]='';
}
}
this.data=splitted.join('');
};
/**
@private
*/
Previewmaker.prototype.killChunks = function() { // heuristics alert
// chunks of italic text? you crazy, man?
var italicChunkRegex=new RegExp
("((^|\\n)\\s*:*\\s*''[^']([^']|'''|'[^']){20}(.|\\n[^\\n])*''[.!?\\s]*\\n)+", 'g');
// keep stuff separated, though, so stick in \n (fixes [[Union Jack]]?
this.data=this.data.replace(italicChunkRegex, '\n');
};
/**
@private
*/
Previewmaker.prototype.mopup = function () {
// we simply *can't* be doing with horizontal rules right now
this.data=this.data.replace(RegExp('^-{4,}','mg'),'');
// no indented lines
this.data=this.data.replace(RegExp('(^|\\n) *:[^\\n]*','g'), '');
// replace __TOC__, __NOTOC__ and whatever else there is
// this'll probably do
this.data=this.data.replace(RegExp('^__[A-Z_]*__ *$', 'gmi'),'');
};
/**
@private
*/
Previewmaker.prototype.firstBit = function () {
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d=this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data=this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data=this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data=this.data.replace(RegExp('^[\\s\\n]*'), '');
var stuff=(RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) { d = stuff[0]; }
if (!getValueOf('popupPreviewFirstParOnly')) { d = this.data; }
/// now put \n\n after sections so that bullets and numbered lists work
d=d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// Split sentences. Superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["'+"'"+']*\\s)','g'));
// leading space is bad, mmkay?
d[0]=d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds=RegExp('([^.][a-z][.] *[a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|op|cit|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
this.fullLength=d.join('').length;
var n=this.maxSentences;
var dd=this.firstSentences(d,n);
do {
dd=this.firstSentences(d,n); --n;
} while ( dd.length > this.maxCharacters && n !== 0 );
this.data = dd;
};
/**
@private
*/
Previewmaker.prototype.fixSentenceEnds = function(strs, reg) {
// take an array of strings, strs
// join strs[i] to strs[i+1] & strs[i+2] if strs[i] matches regex reg
for (var i=0; i<strs.length-2; ++i) {
if (reg.test(strs[i])) {
var a=[];
for (var j=0; j<strs.length; ++j) {
if (j<i) a[j]=strs[j];
if (j==i) a[i]=strs[i]+strs[i+1]+strs[i+2];
if (j>i+2) a[j-2]=strs[j];
}
return this.fixSentenceEnds(a,reg);
}
}
return strs;
};
/**
@private
*/
Previewmaker.prototype.firstSentences = function(strs, howmany) {
var t=strs.slice(0, 2*howmany);
return t.join('');
};
/**
@private
*/
Previewmaker.prototype.killBadWhitespace = function() {
// also cleans up isolated '''', eg [[Suntory Sungoliath]]
this.data=this.data.replace(RegExp('^ *\'+ *$', 'gm'), '');
};
/**
Runs the various methods to generate the preview.
The preview is stored in the <code>html</html> field.
@private
*/
Previewmaker.prototype.makePreview = function() {
if (this.owner.article.namespaceId()!=pg.nsTemplateId &&
this.owner.article.namespaceId()!=pg.nsImageId ) {
this.killComments();
this.killDivs();
this.killGalleries();
this.killBoxTemplates();
if (getValueOf('popupPreviewKillTemplates')) {
this.killTemplates();
} else {
this.killMultilineTemplates();
}
this.killTables();
this.killImages();
this.killHTML();
this.killChunks();
this.mopup();
this.firstBit();
this.killBadWhitespace();
}
else
{
this.killHTML();
}
this.html=wiki2html(this.data, this.baseUrl); // needs livepreview
this.fixHTML();
this.stripLongTemplates();
};
/**
@private
*/
Previewmaker.prototype.esWiki2HtmlPart = function(data) {
var reLinks = /(?:\[\[([^|\]]*)(?:\|([^|\]]*))*]]([a-z]*))/gi; //match a wikilink
reLinks.lastIndex = 0; //reset regex
var match;
var result = "";
var postfixIndex = 0;
while ((match = reLinks.exec(data))) //match all wikilinks
{
//FIXME: the way that link is built here isn't perfect. It is clickable, but popups preview won't recognize it in some cases.
result += pg.escapeQuotesHTML(data.substring(postfixIndex, match.index)) +
'<a href="'+Insta.conf.paths.articles+pg.escapeQuotesHTML(match[1])+'">'+pg.escapeQuotesHTML((match[2]?match[2]:match[1])+match[3])+"</a>";
postfixIndex = reLinks.lastIndex;
}
//append the rest
result += pg.escapeQuotesHTML(data.substring(postfixIndex));
return result;
};
Previewmaker.prototype.editSummaryPreview=function() {
var reAes = /\/\* *(.*?) *\*\//g; //match the first section marker
reAes.lastIndex = 0; //reset regex
var match;
match = reAes.exec(this.data);
if (match)
{
//we have a section link. Split it, process it, combine it.
var prefix = this.data.substring(0,match.index-1);
var section = match[1];
var postfix = this.data.substring(reAes.lastIndex);
var start = "<span class='autocomment'>";
var end = "</span>";
if (prefix.length>0) start = this.esWiki2HtmlPart(prefix) + " " + start + "- ";
if (postfix.length>0) end = ": " + end + this.esWiki2HtmlPart(postfix);
var t=new Title().fromURL(this.baseUrl);
t.anchorFromUtf(section);
var sectionLink = Insta.conf.paths.articles + pg.escapeQuotesHTML(t.toString(true)) + '#' + pg.escapeQuotesHTML(t.anchor);
return start + '<a href="'+sectionLink+'">→</a> '+pg.escapeQuotesHTML(section) + end;
}
//else there's no section link, htmlify the whole thing.
return this.esWiki2HtmlPart(this.data);
};
//<NOLITE>
/** Test function for debugging preview problems one step at a time.
*/
/*eslint-disable */
function previewSteps(txt) {
try {
txt=txt || document.editform.wpTextbox1.value;
} catch (err) {
if (pg.cache.pages.length > 0) {
txt=pg.cache.pages[pg.cache.pages.length-1].data;
} else {
alert('provide text or use an edit page');
}
}
txt=txt.substring(0,10000);
var base=pg.wiki.articlebase + Title.fromURL(document.location.href).urlString();
var p=new Previewmaker(txt, base, pg.current.link.navpopup);
if (this.owner.article.namespaceId() != pg.nsTemplateId) {
p.killComments(); if (!confirm('done killComments(). Continue?\n---\n' + p.data)) { return; }
p.killDivs(); if (!confirm('done killDivs(). Continue?\n---\n' + p.data)) { return; }
p.killGalleries(); if (!confirm('done killGalleries(). Continue?\n---\n' + p.data)) { return; }
p.killBoxTemplates(); if (!confirm('done killBoxTemplates(). Continue?\n---\n' + p.data)) { return; }
if (getValueOf('popupPreviewKillTemplates')) {
p.killTemplates(); if (!confirm('done killTemplates(). Continue?\n---\n' + p.data)) { return; }
} else {
p.killMultilineTemplates(); if (!confirm('done killMultilineTemplates(). Continue?\n---\n' + p.data)) { return; }
}
p.killTables(); if (!confirm('done killTables(). Continue?\n---\n' + p.data)) { return; }
p.killImages(); if (!confirm('done killImages(). Continue?\n---\n' + p.data)) { return; }
p.killHTML(); if (!confirm('done killHTML(). Continue?\n---\n' + p.data)) { return; }
p.killChunks(); if (!confirm('done killChunks(). Continue?\n---\n' + p.data)) { return; }
p.mopup(); if (!confirm('done mopup(). Continue?\n---\n' + p.data)) { return; }
p.firstBit(); if (!confirm('done firstBit(). Continue?\n---\n' + p.data)) { return; }
p.killBadWhitespace(); if (!confirm('done killBadWhitespace(). Continue?\n---\n' + p.data)) { return; }
}
p.html=wiki2html(p.data, base); // needs livepreview
p.fixHTML(); if (!confirm('done fixHTML(). Continue?\n---\n' + p.html)) { return; }
p.stripLongTemplates(); if (!confirm('done stripLongTemplates(). Continue?\n---\n' + p.html)) { return; }
alert('finished preview - end result follows.\n---\n' + p.html);
}
/*eslint-enable */
//</NOLITE>
/**
Works around livepreview bugs.
@private
*/
Previewmaker.prototype.fixHTML = function() {
if(!this.html) return;
var ret = this.html;
// fix question marks in wiki links
// maybe this'll break some stuff :-(
ret=ret.replace(RegExp('(<a href="' + pg.wiki.articlePath + '/[^"]*)[?](.*?")', 'g'), '$1%3F$2');
ret=ret.replace(RegExp('(<a href=\'' + pg.wiki.articlePath + '/[^\']*)[?](.*?\')', 'g'), '$1%3F$2');
// FIXME fix up % too
this.html=ret;
};
/**
Generates the preview and displays it in the current popup.
Does nothing if the generated preview is invalid or consists of whitespace only.
Also activates wikilinks in the preview for subpopups if the popupSubpopups option is true.
*/
Previewmaker.prototype.showPreview = function () {
this.makePreview();
if (typeof this.html != typeof '') return;
if (RegExp('^\\s*$').test(this.html)) return;
setPopupHTML('<hr />', 'popupPrePreviewSep', this.owner.idNumber);
setPopupTipsAndHTML(this.html, 'popupPreview', this.owner.idNumber, { owner: this.owner });
var more = (this.fullLength > this.data.length) ? this.moreLink() : '';
setPopupHTML(more, 'popupPreviewMore', this.owner.idNumber);
};
/**
@private
*/
Previewmaker.prototype.moreLink=function() {
var a=document.createElement('a');
a.className='popupMoreLink';
a.innerHTML=popupString('more...');
var savedThis=this;
a.onclick=function() {
savedThis.maxCharacters+=2000;
savedThis.maxSentences+=20;
savedThis.setData();
savedThis.showPreview();
};
return a;
};
/**
@private
*/
Previewmaker.prototype.stripLongTemplates = function() {
// operates on the HTML!
this.html=this.html.replace(RegExp('^.{0,1000}[{][{][^}]*?(<(p|br)( /)?>\\s*){2,}([^{}]*?[}][}])?', 'gi'), '');
this.html=this.html.split('\n').join(' '); // workaround for <pre> templates
this.html=this.html.replace(RegExp('[{][{][^}]*<pre>[^}]*[}][}]','gi'), '');
};
/**
@private
*/
Previewmaker.prototype.killMultilineTemplates = function() {
this.kill('{{{', '}}}');
this.kill(RegExp('\\s*[{][{][^{}]*\\n'), '}}', '{{');
};
// ENDFILE: previewmaker.js
// STARTFILE: querypreview.js
function loadAPIPreview(queryType, article, navpop) {
var art=new Title(article).urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
var htmlGenerator=function(/*a, d*/){alert('invalid html generator');};
var usernameart = '';
switch (queryType) {
case 'history':
url += 'titles=' + art + '&prop=revisions&rvlimit=' +
getValueOf('popupHistoryPreviewLimit');
htmlGenerator=APIhistoryPreviewHTML;
break;
case 'category':
url += 'list=categorymembers&cmtitle=' + art;
htmlGenerator=APIcategoryPreviewHTML;
break;
case 'userinfo':
var username = new Title( article ).userName();
usernameart = encodeURIComponent( username );
if (pg.re.ipUser.test(username)) {
url += 'list=blocks&bkprop=range|restrictions&bkip=' + usernameart;
} else {
url += 'list=users|usercontribs&usprop=blockinfo|groups|editcount|registration|gender&ususers=' + usernameart + "&meta=globaluserinfo&guiprop=groups|unattached&guiuser="+ usernameart + "&uclimit=1&ucprop=timestamp&ucuser=" + usernameart;
}
htmlGenerator=APIuserInfoPreviewHTML;
break;
case 'contribs':
usernameart = encodeURIComponent( new Title( article ).userName() );
url += 'list=usercontribs&ucuser=' + usernameart +
'&uclimit=' + getValueOf('popupContribsPreviewLimit');
htmlGenerator=APIcontribsPreviewHTML;
break;
case 'imagepagepreview':
var trail='';
if (getValueOf('popupImageLinks')) { trail = '&list=imageusage&iutitle=' + art; }
url += 'titles=' + art + '&prop=revisions|imageinfo&rvprop=content' + trail;
htmlGenerator=APIimagepagePreviewHTML;
break;
case 'backlinks':
url += 'list=backlinks&bltitle=' + art;
htmlGenerator=APIbacklinksPreviewHTML;
break;
case 'revision':
if (article.oldid) {
url += 'revids=' + article.oldid;
} else {
url += 'titles=' + article.removeAnchor().urlString();
}
url += '&prop=revisions|pageprops|info|images|categories&rvprop=ids|timestamp|flags|comment|user|content&cllimit=max&imlimit=max';
htmlGenerator=APIrevisionPreviewHTML;
break;
}
pendingNavpopTask(navpop);
var callback=function(d){
log( "callback of API functions was hit" );
showAPIPreview(queryType, htmlGenerator(article,d,navpop), navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_'+queryType+'_QUERY_DATA'); }
}
function linkList(list) {
list.sort(function(x,y) { return (x==y ? 0 : (x<y ? -1 : 1)); });
var buf=[];
for (var i=0; i<list.length; ++i) {
buf.push(wikiLink({article: new Title(list[i]),
text: list[i].split(' ').join(' '),
action: 'view'}));
}
return buf.join(', ');
}
function getTimeOffset() {
var tz = mw.user.options.get('timecorrection');
if(tz) {
if( tz.indexOf('|') > -1 ) {
// New format
return parseInt(tz.split('|')[1],10);
} else if ( tz.indexOf(':') > -1 ) {
// Old format
return( parseInt(tz,10)*60 + parseInt(tz.split(':')[1],10) );
}
}
return 0;
}
/*
* Creates a HTML table that's shown in the history and user-contribs popups.
* @param {Object[]} h - a list of revisions, returned from the API
* @param {boolean} reallyContribs - true only if we're displaying user contributions
*/
function editPreviewTable(article, h, reallyContribs, timeOffset) {
var html=['<table>'];
var day=null;
var curart=article;
var page=null;
var makeFirstColumnLinks;
if(reallyContribs) {
// We're showing user contributions, so make (diff | hist) links
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&diff=prev' +
'&oldid=' + currentRevision.revid + '">' + popupString('diff') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase +
new Title(currentRevision.title).urlString() + '&action=history">' +
popupString('hist') + '</a>';
result += ')';
return result;
};
} else {
// It's a regular history page, so make (cur | last) links
var firstRevid = h[0].revid;
makeFirstColumnLinks = function(currentRevision) {
var result = '(';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=' + firstRevid + '&oldid=' + currentRevision.revid + '">' + popupString('cur') + '</a>';
result += ' | ';
result += '<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&diff=prev&oldid=' + currentRevision.revid + '">' + popupString('last') + '</a>';
result += ')';
return result;
};
}
for (var i=0; i<h.length; ++i) {
if (reallyContribs) {
page = h[i].title;
curart = new Title(page);
}
var minor = h[i].minor ? '<b>m </b>' : '';
var editDate = adjustDate(getDateFromTimestamp(h[i].timestamp), timeOffset);
var thisDay = dayFormat(editDate);
var thisTime = timeFormat(editDate);
if (thisDay == day) {
thisDay = '';
} else {
day = thisDay;
}
if (thisDay) {
html.push( '<tr><td colspan=3><span class="popup_history_date">' +
thisDay+'</span></td></tr>' );
}
html.push('<tr class="popup_history_row_' + ( (i%2) ? 'odd' : 'even') + '">');
html.push('<td>' + makeFirstColumnLinks(h[i]) + '</td>');
html.push('<td>' +
'<a href="' + pg.wiki.titlebase + new Title(curart).urlString() +
'&oldid=' + h[i].revid + '">' + thisTime + '</a></td>');
var col3url='', col3txt='';
if (!reallyContribs) {
var user=h[i].user;
if( !h[i].userhidden ) {
if( pg.re.ipUser.test(user) ) {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId] + ':Contributions&target=' + new Title(user).urlString();
} else {
col3url=pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsUserId] + ':' + new Title(user).urlString();
}
col3txt=pg.escapeQuotesHTML(user);
} else {
col3url=getValueOf('popupRevDelUrl');
col3txt=pg.escapeQuotesHTML( popupString('revdel'));
}
} else {
col3url=pg.wiki.titlebase + curart.urlString();
col3txt=pg.escapeQuotesHTML(page);
}
html.push('<td>' + (reallyContribs ? minor : '') +
'<a href="' + col3url + '">' + col3txt + '</a></td>');
var comment='';
var c=h[i].comment || h[i].content;
if (c) {
comment=new Previewmaker(c, new Title(curart).toUrl()).editSummaryPreview();
} else if ( h[i].commenthidden ) {
comment=popupString('revdel');
}
html.push('<td>' + (!reallyContribs ? minor : '') + comment + '</td>');
html.push('</tr>');
html=[html.join('')];
}
html.push('</table>');
return html.join('');
}
function getDateFromTimestamp(t) {
var s=t.split(/[^0-9]/);
switch(s.length) {
case 0: return null;
case 1: return new Date(s[0]);
case 2: return new Date(s[0], s[1]-1);
case 3: return new Date(s[0], s[1]-1, s[2]);
case 4: return new Date(s[0], s[1]-1, s[2], s[3]);
case 5: return new Date(s[0], s[1]-1, s[2], s[3], s[4]);
case 6: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5]);
default: return new Date(s[0], s[1]-1, s[2], s[3], s[4], s[5], s[6]);
}
}
function adjustDate(d, offset) {
// offset is in minutes
var o=offset * 60 * 1000;
return new Date( +d + o);
}
function dayFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCFullYear(), editDate.getUTCMonth()+1, editDate.getUTCDate()]).join('-'); }
return map(zeroFill, [editDate.getFullYear(), editDate.getMonth()+1, editDate.getDate()]).join('-');
}
function timeFormat(editDate, utc) {
if (utc) { return map(zeroFill, [editDate.getUTCHours(), editDate.getUTCMinutes(), editDate.getUTCSeconds()]).join(':'); }
return map(zeroFill, [editDate.getHours(), editDate.getMinutes(), editDate.getSeconds()]).join(':');
}
function showAPIPreview(queryType, html, id, navpop, download) {
// DJ: done
var target='popupPreview';
completedNavpopTask(navpop);
switch (queryType) {
case 'imagelinks':
case 'category':
target='popupPostPreview'; break;
case 'userinfo':
target='popupUserData'; break;
case 'revision':
insertPreview(download);
return;
}
setPopupTipsAndHTML(html, target, id);
}
function APIrevisionPreviewHTML(article, download) {
try{
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
if( page.missing ) {
// TODO we need to fix this proper later on
download.owner = null;
return;
}
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
download.data = content;
download.lastModified = new Date(page.revisions[0].timestamp);
}
} catch(someError) {
return 'Revision preview failed :(';
}
}
function APIbacklinksPreviewHTML(article, download/*, navpop*/ ) {
try {
var jsObj=getJsObj(download.data);
var list=jsObj.query.backlinks;
var html=[];
if (!list) { return popupString('No backlinks found'); }
for ( var i=0; i < list.length; i++ ) {
var t=new Title(list[i].title);
html.push('<a href="' + pg.wiki.titlebase + t.urlString() + '">' + t + '</a>');
}
html=html.join(', ');
if (jsObj['continue'] && jsObj['continue'].blcontinue) {
html += popupString(' and more');
}
return html;
} catch (someError) {
return 'backlinksPreviewHTML went wonky';
}
}
pg.fn.APIsharedImagePagePreviewHTML = function APIsharedImagePagePreviewHTML(obj) {
log( "APIsharedImagePagePreviewHTML" );
var popupid = obj.requestid;
if( obj.query && obj.query.pages )
{
var page=anyChild(obj.query.pages );
var content = (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
if( typeof content === 'string' )
{
/* Not entirely safe, but the best we can do */
var p=new Previewmaker(content, pg.current.link.navpopup.article, pg.current.link.navpopup);
p.makePreview();
setPopupHTML( p.html, "popupSecondPreview", popupid );
}
}
};
function APIimagepagePreviewHTML(article, download, navpop) {
try {
var jsObj=getJsObj(download.data);
var page=anyChild(jsObj.query.pages);
var content= (
page &&
page.revisions &&
page.revisions[0].contentmodel === 'wikitext'
) ? page.revisions[0].content : null;
var ret='';
var alt='';
try{alt=navpop.parentAnchor.childNodes[0].alt;} catch(e){}
if (alt) {
ret = ret + '<hr /><b>' + popupString('Alt text:') + '</b> ' + pg.escapeQuotesHTML(alt);
}
if (typeof content === 'string') {
var p=prepPreviewmaker(content, article, navpop);
p.makePreview();
if (p.html) { ret += '<hr />' + p.html; }
if (getValueOf('popupSummaryData')) {
var info=getPageInfo(content, download);
log(info);
setPopupTrailer(info, navpop.idNumber);
}
}
if (page && page.imagerepository == "shared" ) {
var art=new Title(article);
var encart = encodeURIComponent( "File:" + art.stripNamespace() );
var shared_url = pg.wiki.apicommonsbase + '?format=json&formatversion=2' +
'&callback=pg.fn.APIsharedImagePagePreviewHTML' +
'&requestid=' + navpop.idNumber +
'&action=query&prop=revisions&rvprop=content&titles=' + encart;
ret = ret +'<hr />' + popupString( 'Image from Commons') +
': <a href="' + pg.wiki.commonsbase + '?title=' + encart + '">' +
popupString( 'Description page') + '</a>';
mw.loader.load( shared_url );
}
showAPIPreview('imagelinks', APIimagelinksPreviewHTML(article,download), navpop.idNumber, download);
return ret;
} catch (someError) {
return 'API imagepage preview failed :(';
}
}
function APIimagelinksPreviewHTML(article, download) {
try {
var jsobj=getJsObj(download.data);
var list=jsobj.query.imageusage;
if (list) {
var ret=[];
for (var i=0; i < list.length; i++) {
ret.push(list[i].title);
}
if (ret.length === 0) { return popupString('No image links found'); }
return '<h2>' + popupString('File links') + '</h2>' + linkList(ret);
} else {
return popupString('No image links found');
}
} catch(someError) {
return 'Image links preview generation failed :(';
}
}
function APIcategoryPreviewHTML(article, download) {
try{
var jsobj=getJsObj(download.data);
var list=jsobj.query.categorymembers;
var ret=[];
for (var p=0; p < list.length; p++) {
ret.push(list[p].title);
}
if (ret.length === 0) { return popupString('Empty category'); }
ret = '<h2>' + tprintf('Category members (%s shown)', [ret.length]) + '</h2>' +linkList(ret);
if (jsobj['continue'] && jsobj['continue'].cmcontinue) {
ret += popupString(' and more');
}
return ret;
} catch(someError) {
return 'Category preview failed :(';
}
}
function APIuserInfoPreviewHTML(article, download) {
var ret=[];
var queryobj = {};
try{
queryobj=getJsObj(download.data).query;
} catch(someError) { return 'Userinfo preview failed :('; }
var user=anyChild(queryobj.users);
if (user) {
var globaluserinfo=queryobj.globaluserinfo;
if (user.invalid === '') {
ret.push( popupString( 'Invalid user') );
} else if (user.missing === '') {
ret.push( popupString( 'Not a registered username') );
}
if( user.blockedby ) {
if( user.blockpartial ) {
ret.push('<b>' + popupString('Has blocks') + '</b>');
} else {
ret.push('<b>' + popupString('CẤM') + '</b>');
}
}
if( globaluserinfo && ( 'locked' in globaluserinfo || 'hidden' in globaluserinfo ) ) {
var lockedSulAccountIsAttachedToThis = true;
for( var i=0; globaluserinfo.unattached && i < globaluserinfo.unattached.length; i++) {
if ( globaluserinfo.unattached[i].wiki === mw.config.get('wgDBname') ) {
lockedSulAccountIsAttachedToThis=false;
break;
}
}
if (lockedSulAccountIsAttachedToThis) {
if ( 'locked' in globaluserinfo ) ret.push('<b><i>' + popupString('LOCKED') + '</i></b>');
if ( 'hidden' in globaluserinfo ) ret.push('<b><i>' + popupString('HIDDEN') + '</i></b>');
}
}
if( getValueOf('popupShowGender') && user.gender ) {
switch( user.gender ) {
case "male": ret.push( popupString( "\u2642" ) ); break;
case "female": ret.push( popupString( "\u2640" ) ); break;
}
}
if( user.groups ) {
for( var j=0; j < user.groups.length; j++) {
var currentGroup = user.groups[j];
if( ["*", "user", "autoconfirmed", "extendedconfirmed"].indexOf( currentGroup ) === -1 ) {
ret.push( pg.escapeQuotesHTML(user.groups[j]) );
}
}
}
if( globaluserinfo && globaluserinfo.groups ) {
for( var k=0; k < globaluserinfo.groups.length; k++) {
ret.push( '<i>'+pg.escapeQuotesHTML(globaluserinfo.groups[k])+'</i>' );
}
}
if( user.registration )
ret.push( pg.escapeQuotesHTML((user.editcount?user.editcount:'0') + popupString(' sửa đổi từ: ') + (user.registration?dayFormat(getDateFromTimestamp(user.registration)):'')) );
}
if (queryobj.usercontribs && queryobj.usercontribs.length) {
ret.push( popupString('sửa đổi lần cuối vào ') + dayFormat(getDateFromTimestamp(queryobj.usercontribs[0].timestamp)) );
}
if (queryobj.blocks) {
ret.push( popupString( 'IP user') ); //we only request list=blocks for IPs
for (var l=0; l<queryobj.blocks.length; l++) {
console.log(queryobj);
var rbstr = queryobj.blocks[l].rangestart === queryobj.blocks[l].rangeend ? 'BLOCK' : 'RANGEBLOCK';
rbstr = (!Array.isArray(queryobj.blocks[l].restrictions) ? 'Has ' + rbstr.toLowerCase() + 's' : rbstr + 'ED');
ret.push('<b>' + popupString(rbstr) + '</b>' );
}
}
ret = '<hr />' + ret.join( ', ' );
return ret;
}
function APIcontribsPreviewHTML(article, download, navpop) {
return APIhistoryPreviewHTML(article, download, navpop, true);
}
function APIhistoryPreviewHTML(article, download, navpop, reallyContribs) {
try {
var jsobj=getJsObj(download.data);
var edits = [];
if( reallyContribs ) {
edits=jsobj.query.usercontribs;
} else {
edits=anyChild(jsobj.query.pages).revisions;
}
var ret=editPreviewTable(article, edits, reallyContribs, getTimeOffset());
return ret;
} catch (someError) {
return 'History preview failed :-(';
}
}
//</NOLITE>
// ENDFILE: querypreview.js
// STARTFILE: debug.js
////////////////////////////////////////////////////////////////////
// Debugging functions
////////////////////////////////////////////////////////////////////
function setupDebugging() {
//<NOLITE>
if (window.popupDebug) { // popupDebug is set from .version
window.log=function(x) { //if(gMsg!='')gMsg += '\n'; gMsg+=time() + ' ' + x; };
window.console.log(x);
};
window.errlog=function(x) {
window.console.error(x);
};
log('Initializing logger');
} else {
//</NOLITE>
window.log = function() {};
window.errlog = function() {};
//<NOLITE>
}
//</NOLITE>
}
// ENDFILE: debug.js
// STARTFILE: images.js
// load image of type Title.
function loadImage(image, navpop) {
if (typeof image.stripNamespace != 'function') { alert('loadImages bad'); }
// API call to retrieve image info.
if ( !getValueOf('popupImages') ) return;
if ( !isValidImageName(image) ) return false;
var art=image.urlString();
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query';
url += '&prop=imageinfo&iiprop=url|mime&iiurlwidth=' + getValueOf('popupImageSizeLarge');
url += '&titles=' + art;
pendingNavpopTask(navpop);
var callback=function(d){
popupsInsertImage(navpop.idNumber, navpop, d);
};
var go = function(){
getPageWithCaching(url, callback, navpop);
return true;
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'after', 'DOWNLOAD_IMAGE_QUERY_DATA'); }
}
function popupsInsertImage(id, navpop, download) {
log( "popupsInsertImage");
var imageinfo;
try {
var jsObj=getJsObj(download.data);
var imagepage=anyChild(jsObj.query.pages);
if (typeof imagepage.imageinfo === 'undefined') return;
imageinfo = imagepage.imageinfo[0];
} catch (someError) {
log( "popupsInsertImage failed :(" );
return;
}
var popupImage = document.getElementById("popupImg"+id);
if (!popupImage) {
log( "could not find insertion point for image");
return;
}
popupImage.width=getValueOf('popupImageSize');
popupImage.style.display='inline';
// Set the source for the image.
if( imageinfo.thumburl )
popupImage.src=imageinfo.thumburl;
else if( imageinfo.mime.indexOf("image") === 0 ){
popupImage.src=imageinfo.url;
log( "a thumb could not be found, using original image" );
} else log( "fullsize imagethumb, but not sure if it's an image");
var a=document.getElementById("popupImageLink"+id);
if (a === null) { return null; }
// Determine the action of the surrouding imagelink.
switch (getValueOf('popupThumbAction')) {
case 'imagepage':
if (pg.current.article.namespaceId()!=pg.nsImageId) {
a.href=imageinfo.descriptionurl;
// FIXME: unreliable pg.idNumber
popTipsSoonFn('popupImage' + id)();
break;
}
/* falls through */
case 'sizetoggle':
a.onclick=toggleSize;
a.title=popupString('Toggle image size');
return;
case 'linkfull':
a.href = imageinfo.url;
a.title=popupString('Open full-size image');
return;
}
}
// Toggles the image between inline small and navpop fullwidth.
// It's the same image, no actual sizechange occurs, only display width.
function toggleSize() {
var imgContainer=this;
if (!imgContainer) {
alert('imgContainer is null :/');
return;
}
var img=imgContainer.firstChild;
if (!img) {
alert('img is null :/');
return;
}
if (!img.style.width || img.style.width==='') {
img.style.width='100%';
} else {
img.style.width='';
}
}
// Returns one title of an image from wikiText.
function getValidImageFromWikiText(wikiText) {
// nb in pg.re.image we're interested in the second bracketed expression
// this may change if the regex changes :-(
//var match=pg.re.image.exec(wikiText);
var matched=null;
var match;
// strip html comments, used by evil bots :-(
var t = removeMatchesUnless(wikiText, RegExp('(<!--[\\s\\S]*?-->)'), 1,
RegExp('^<!--[^[]*popup', 'i'));
while ( ( match = pg.re.image.exec(t) ) ) {
// now find a sane image name - exclude templates by seeking {
var m = match[2] || match[6];
if ( isValidImageName(m) ) {
matched=m;
break;
}
}
pg.re.image.lastIndex=0;
if (!matched) { return null; }
return mw.config.get('wgFormattedNamespaces')[pg.nsImageId]+':'+upcaseFirst(matched);
}
function removeMatchesUnless(str, re1, parencount, re2) {
var split=str.parenSplit(re1);
var c=parencount + 1;
for (var i=0; i<split.length; ++i) {
if ( i%c === 0 || re2.test(split[i]) ) { continue; }
split[i]='';
}
return split.join('');
}
//</NOLITE>
// ENDFILE: images.js
// STARTFILE: namespaces.js
// Set up namespaces and other non-strings.js localization
// (currently that means redirs too)
function setNamespaces() {
pg.nsSpecialId = -1;
pg.nsMainspaceId = 0;
pg.nsImageId = 6;
pg.nsUserId = 2;
pg.nsUsertalkId = 3;
pg.nsCategoryId = 14;
pg.nsTemplateId = 10;
}
function setRedirs() {
var r='redirect';
var R='REDIRECT';
var redirLists={
//<NOLITE>
'ar': [ R, 'تحويل' ],
'be': [ r, 'перанакіраваньне' ],
'bg': [ r, 'пренасочване', 'виж' ],
'bs': [ r, 'Preusmjeri', 'preusmjeri', 'PREUSMJERI' ],
'cs': [ R, 'PŘESMĚRUJ' ],
'cy': [ r, 'ail-cyfeirio' ],
'de': [ R, 'WEITERLEITUNG' ],
'el': [ R, 'ΑΝΑΚΑΤΕΥΘΥΝΣΗ'],
'eo': [ R, 'ALIDIREKTU', 'ALIDIREKTI' ],
'es': [ R, 'REDIRECCIÓN' ],
'et': [ r, 'suuna' ],
'ga': [ r, 'athsheoladh' ],
'gl': [ r, 'REDIRECCIÓN', 'REDIRECIONAMENTO'],
'he': [ R, 'הפניה' ],
'hu': [ R, 'ÁTIRÁNYÍTÁS' ],
'is': [ r, 'tilvísun', 'TILVÍSUN' ],
'it': [ R, 'RINVIA', 'Rinvia'],
'ja': [ R, '転送' ],
'mk': [ r, 'пренасочување', 'види' ],
'nds': [ r, 'wiederleiden' ],
'nl': [ R, 'DOORVERWIJZING' ],
'nn': [ r, 'omdiriger' ],
'pl': [ R, 'PATRZ', 'PRZEKIERUJ', 'TAM' ],
'pt': [ R, 'redir' ],
'ru': [ R, 'ПЕРЕНАПРАВЛЕНИЕ', 'ПЕРЕНАПР' ],
'sk': [ r, 'presmeruj' ],
'sr': [ r, 'Преусмери', 'преусмери', 'ПРЕУСМЕРИ', 'Preusmeri', 'preusmeri', 'PREUSMERI' ],
'tt': [ R, 'yünältü', 'перенаправление', 'перенапр' ],
'uk': [ R, 'ПЕРЕНАПРАВЛЕННЯ', 'ПЕРЕНАПР' ],
'vi': [ r, 'đổi' ],
'zh': [ R, '重定向'] // no comma
//</NOLITE>
};
var redirList=redirLists[ pg.wiki.lang ] || [r, R];
// Mediawiki is very tolerant about what comes after the #redirect at the start
pg.re.redirect=RegExp('^\\s*[#](' + redirList.join('|') + ').*?\\[{2}([^\\|\\]]*)(|[^\\]]*)?\\]{2}\\s*(.*)', 'i');
}
function setInterwiki() {
if (pg.wiki.wikimedia) {
// From https://meta.wikimedia.org/wiki/List_of_Wikipedias
pg.wiki.interwiki='aa|ab|ace|af|ak|als|am|an|ang|ar|arc|arz|as|ast|av|ay|az|ba|bar|bat-smg|bcl|be|be-x-old|bg|bh|bi|bjn|bm|bn|bo|bpy|br|bs|bug|bxr|ca|cbk-zam|cdo|ce|ceb|ch|cho|chr|chy|ckb|co|cr|crh|cs|csb|cu|cv|cy|da|de|diq|dsb|dv|dz|ee|el|eml|en|eo|es|et|eu|ext|fa|ff|fi|fiu-vro|fj|fo|fr|frp|frr|fur|fy|ga|gag|gan|gd|gl|glk|gn|got|gu|gv|ha|hak|haw|he|hi|hif|ho|hr|hsb|ht|hu|hy|hz|ia|id|ie|ig|ii|ik|ilo|io|is|it|iu|ja|jbo|jv|ka|kaa|kab|kbd|kg|ki|kj|kk|kl|km|kn|ko|koi|kr|krc|ks|ksh|ku|kv|kw|ky|la|lad|lb|lbe|lg|li|lij|lmo|ln|lo|lt|ltg|lv|map-bms|mdf|mg|mh|mhr|mi|mk|ml|mn|mo|mr|mrj|ms|mt|mus|mwl|my|myv|mzn|na|nah|nap|nds|nds-nl|ne|new|ng|nl|nn|no|nov|nrm|nv|ny|oc|om|or|os|pa|pag|pam|pap|pcd|pdc|pfl|pi|pih|pl|pms|pnb|pnt|ps|pt|qu|rm|rmy|rn|ro|roa-rup|roa-tara|ru|rue|rw|sa|sah|sc|scn|sco|sd|se|sg|sh|si|simple|sk|sl|sm|sn|so|sq|sr|srn|ss|st|stq|su|sv|sw|szl|ta|te|tet|tg|th|ti|tk|tl|tn|to|tpi|tr|ts|tt|tum|tw|ty|udm|ug|uk|ur|uz|ve|vec|vi|vls|vo|wa|war|wo|wuu|xal|xh|yi|yo|za|zea|zh|zh-classical|zh-min-nan|zh-yue|zu';
pg.re.interwiki=RegExp('^'+pg.wiki.interwiki+':');
} else {
pg.wiki.interwiki=null;
pg.re.interwiki=RegExp('^$');
}
}
// return a regexp pattern matching all variants to write the given namespace
function nsRe(namespaceId) {
var imageNamespaceVariants = [];
jQuery.each(mw.config.get('wgNamespaceIds'), function(_localizedNamespaceLc, _namespaceId) {
if (_namespaceId!=namespaceId) return;
_localizedNamespaceLc = upcaseFirst(_localizedNamespaceLc);
imageNamespaceVariants.push(mw.util.escapeRegExp(_localizedNamespaceLc).split(' ').join('[ _]'));
imageNamespaceVariants.push(mw.util.escapeRegExp(encodeURI(_localizedNamespaceLc)));
});
return '(?:' + imageNamespaceVariants.join('|') + ')';
}
function nsReImage() {
return nsRe(pg.nsImageId);
}
// ENDFILE: namespaces.js
// STARTFILE: selpop.js
//<NOLITE>
function getEditboxSelection() {
// see http://www.webgurusforum.com/8/12/0
var editbox;
try {
editbox=document.editform.wpTextbox1;
} catch (dang) { return; }
// IE, Opera
if (document.selection) { return document.selection.createRange().text; }
// Mozilla
var selStart = editbox.selectionStart;
var selEnd = editbox.selectionEnd;
return (editbox.value).substring(selStart, selEnd);
}
function doSelectionPopup() {
// popup if the selection looks like [[foo|anything afterwards at all
// or [[foo|bar]]text without ']]'
// or [[foo|bar]]
var sel=getEditboxSelection();
var open=sel.indexOf('[[');
var pipe=sel.indexOf('|');
var close=sel.indexOf(']]');
if (open == -1 || ( pipe == -1 && close == -1) ) { return; }
if (pipe != -1 && open > pipe || close != -1 && open > close) { return; }
if (getValueOf('popupOnEditSelection')=='boxpreview') {
return doSeparateSelectionPopup(sel);
}
var article=new Title(sel.substring(open+2, (pipe < 0) ? close : pipe)).urlString();
if (close > 0 && sel.substring(close+2).indexOf('[[') >= 0) {
return;
}
var a=document.createElement('a');
a.href=pg.wiki.titlebase + article;
mouseOverWikiLink2(a);
if (a.navpopup) {
a.navpopup.addHook(function(){runStopPopupTimer(a.navpopup);}, 'unhide', 'after');
}
}
function doSeparateSelectionPopup(str) {
var div=document.getElementById('selectionPreview');
if (!div) {
div = document.createElement('div');
div.id='selectionPreview';
try {
var box=document.editform.wpTextbox1;
box.parentNode.insertBefore(div, box);
} catch (error) {
return;
}
}
div.innerHTML=wiki2html(str);
div.ranSetupTooltipsAlready = false;
popTipsSoonFn('selectionPreview')();
}
//</NOLITE>
// ENDFILE: selpop.js
// STARTFILE: navpopup.js
/**
@fileoverview Defines two classes: {@link Navpopup} and {@link Mousetracker}.
<code>Navpopup</code> describes popups: when they appear, where, what
they look like and so on.
<code>Mousetracker</code> "captures" the mouse using
<code>document.onmousemove</code>.
*/
/**
Creates a new Mousetracker.
@constructor
@class The Mousetracker class. This monitors mouse movements and manages associated hooks.
*/
function Mousetracker() {
/**
Interval to regularly run the hooks anyway, in milliseconds.
@type Integer
*/
this.loopDelay=400;
/**
Timer for the loop.
@type Timer
*/
this.timer=null;
/**
Flag - are we switched on?
@type Boolean
*/
this.active=false;
/**
Flag - are we probably inaccurate, i.e. not reflecting the actual mouse position?
*/
this.dirty=true;
/**
Array of hook functions.
@private
@type Array
*/
this.hooks=[];
}
/**
Adds a hook, to be called when we get events.
@param {Function} f A function which is called as
<code>f(x,y)</code>. It should return <code>true</code> when it
wants to be removed, and <code>false</code> otherwise.
*/
Mousetracker.prototype.addHook = function (f) {
this.hooks.push(f);
};
/**
Runs hooks, passing them the x
and y coords of the mouse. Hook functions that return true are
passed to {@link Mousetracker#removeHooks} for removal.
@private
*/
Mousetracker.prototype.runHooks = function () {
if (!this.hooks || !this.hooks.length) { return; }
//log('Mousetracker.runHooks; we got some hooks to run');
var remove=false;
var removeObj={};
// this method gets called a LOT -
// pre-cache some variables
var x=this.x, y=this.y, len = this.hooks.length;
for (var i=0; i<len; ++i) {
//~ run the hook function, and remove it if it returns true
if (this.hooks[i](x, y)===true) {
remove=true;
removeObj[i]=true;
}
}
if (remove) { this.removeHooks(removeObj); }
};
/**
Removes hooks.
@private
@param {Object} removeObj An object whose keys are the index
numbers of functions for removal, with values that evaluate to true
*/
Mousetracker.prototype.removeHooks = function(removeObj) {
var newHooks=[];
var len = this.hooks.length;
for (var i=0; i<len; ++i) {
if (! removeObj[i]) { newHooks.push(this.hooks[i]); }
}
this.hooks=newHooks;
};
/**
Event handler for mouse wiggles.
We simply grab the event, set x and y and run the hooks.
This makes the cpu all hot and bothered :-(
@private
@param {Event} e Mousemove event
*/
Mousetracker.prototype.track=function (e) {
//~ Apparently this is needed in IE.
e = e || window.event;
var x, y;
if (e) {
if (e.pageX) { x=e.pageX; y=e.pageY; }
else if (typeof e.clientX!='undefined') {
var left, top, docElt = document.documentElement;
if (docElt) { left=docElt.scrollLeft; }
left = left || document.body.scrollLeft || document.scrollLeft || 0;
if (docElt) { top=docElt.scrollTop; }
top = top || document.body.scrollTop || document.scrollTop || 0;
x=e.clientX + left;
y=e.clientY + top;
} else { return; }
this.setPosition(x,y);
}
};
/**
Sets the x and y coordinates stored and takes appropriate action,
running hooks as appropriate.
@param {Integer} x, y Screen coordinates to set
*/
Mousetracker.prototype.setPosition=function(x,y) {
this.x = x;
this.y = y;
if (this.dirty || this.hooks.length === 0) { this.dirty=false; return; }
if (typeof this.lastHook_x != 'number') { this.lastHook_x = -100; this.lastHook_y=-100; }
var diff = (this.lastHook_x - x)*(this.lastHook_y - y);
diff = (diff >= 0) ? diff : -diff;
if ( diff > 1 ) {
this.lastHook_x=x;
this.lastHook_y=y;
if (this.dirty) { this.dirty = false; }
else { this.runHooks(); }
}
};
/**
Sets things in motion, unless they are already that is, registering an event handler on <code>document.onmousemove</code>.
A half-hearted attempt is made to preserve the old event handler if there is one.
*/
Mousetracker.prototype.enable = function () {
if (this.active) { return; }
this.active=true;
//~ Save the current handler for mousemove events. This isn't too
//~ robust, of course.
this.savedHandler=document.onmousemove;
//~ Gotta save @tt{this} again for the closure, and use apply for
//~ the member function.
var savedThis=this;
document.onmousemove=function (e) {savedThis.track.apply(savedThis, [e]);};
if (this.loopDelay) { this.timer = setInterval(function() { //log('loop delay in mousetracker is working');
savedThis.runHooks();}, this.loopDelay); }
};
/**
Disables the tracker, removing the event handler.
*/
Mousetracker.prototype.disable = function () {
if (!this.active) { return; }
if (typeof this.savedHandler === 'function') {
document.onmousemove=this.savedHandler;
} else { delete document.onmousemove; }
if (this.timer) { clearInterval(this.timer); }
this.active=false;
};
/**
Creates a new Navpopup.
Gets a UID for the popup and
@param init Contructor object. If <code>init.draggable</code> is true or absent, the popup becomes draggable.
@constructor
@class The Navpopup class. This generates popup hints, and does some management of them.
*/
function Navpopup(/*init*/) {
//alert('new Navpopup(init)');
/** UID for each Navpopup instance.
Read-only.
@type integer
*/
this.uid=Navpopup.uid++;
/**
Read-only flag for current visibility of the popup.
@type boolean
@private
*/
this.visible=false;
/** Flag to be set when we want to cancel a previous request to
show the popup in a little while.
@private
@type boolean
*/
this.noshow=false;
/** Categorised list of hooks.
@see #runHooks
@see #addHook
@private
@type Object
*/
this.hooks={
'create': [],
'unhide': [],
'hide': []
};
/** list of unique IDs of hook functions, to avoid duplicates
@private
*/
this.hookIds={};
/** List of downloads associated with the popup.
@private
@type Array
*/
this.downloads=[];
/** Number of uncompleted downloads.
@type integer
*/
this.pending=null;
/** Tolerance in pixels when detecting whether the mouse has left the popup.
@type integer
*/
this.fuzz=5;
/** Flag to toggle running {@link #limitHorizontalPosition} to regulate the popup's position.
@type boolean
*/
this.constrained=true;
/** The popup width in pixels.
@private
@type integer
*/
this.width=0;
/** The popup width in pixels.
@private
@type integer
*/
this.height=0;
/** The main content DIV element.
@type HTMLDivElement
*/
this.mainDiv=null;
this.createMainDiv();
// if (!init || typeof init.popups_draggable=='undefined' || init.popups_draggable) {
// this.makeDraggable(true);
// }
}
/**
A UID for each Navpopup. This constructor property is just a counter.
@type integer
@private
*/
Navpopup.uid=0;
/**
Retrieves the {@link #visible} attribute, indicating whether the popup is currently visible.
@type boolean
*/
Navpopup.prototype.isVisible=function() {
return this.visible;
};
/**
Repositions popup using CSS style.
@private
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@param {boolean} noLimitHor Don't call {@link #limitHorizontalPosition}
*/
Navpopup.prototype.reposition= function (x,y, noLimitHor) {
log ('reposition('+x+','+y+','+noLimitHor+')');
if (typeof x != 'undefined' && x !== null) { this.left=x; }
if (typeof y != 'undefined' && y !== null) { this.top=y; }
if (typeof this.left != 'undefined' && typeof this.top != 'undefined') {
this.mainDiv.style.left=this.left + 'px';
this.mainDiv.style.top=this.top + 'px';
}
if (!noLimitHor) { this.limitHorizontalPosition(); }
//console.log('navpop'+this.uid+' - (left,top)=(' + this.left + ',' + this.top + '), css=('
//+ this.mainDiv.style.left + ',' + this.mainDiv.style.top + ')');
};
/**
Prevents popups from being in silly locations. Hopefully.
Should not be run if {@link #constrained} is true.
@private
*/
Navpopup.prototype.limitHorizontalPosition=function() {
if (!this.constrained || this.tooWide) { return; }
this.updateDimensions();
var x=this.left;
var w=this.width;
var cWidth=document.body.clientWidth;
// log('limitHorizontalPosition: x='+x+
// ', this.left=' + this.left +
// ', this.width=' + this.width +
// ', cWidth=' + cWidth);
if ( (x+w) >= cWidth ||
( x > 0 &&
this.maxWidth &&
this.width < this.maxWidth &&
this.height > this.width &&
x > cWidth - this.maxWidth ) ) {
// This is a very nasty hack. There has to be a better way!
// We find the "natural" width of the div by positioning it at the far left
// then reset it so that it should be flush right (well, nearly)
this.mainDiv.style.left='-10000px';
this.mainDiv.style.width = this.maxWidth + 'px';
var naturalWidth=parseInt(this.mainDiv.offsetWidth, 10);
var newLeft=cWidth - naturalWidth - 1;
if (newLeft < 0) { newLeft = 0; this.tooWide=true; } // still unstable for really wide popups?
log ('limitHorizontalPosition: moving to ('+newLeft + ','+ this.top+');' + ' naturalWidth=' + naturalWidth + ', clientWidth=' + cWidth);
this.reposition(newLeft, null, true);
}
};
/**
Counter indicating the z-order of the "highest" popup.
We start the z-index at 1000 so that popups are above everything
else on the screen.
@private
@type integer
*/
Navpopup.highest=1000;
/**
Brings popup to the top of the z-order.
We increment the {@link #highest} property of the contructor here.
@private
*/
Navpopup.prototype.raise = function () {
this.mainDiv.style.zIndex=Navpopup.highest + 1;
++Navpopup.highest;
};
/**
Shows the popup provided {@link #noshow} is not true.
Updates the position, brings the popup to the top of the z-order and unhides it.
*/
Navpopup.prototype.show = function () {
//document.title+='s';
if (this.noshow) { return; }
//document.title+='t';
this.reposition();
this.raise();
this.unhide();
};
/**
Checks to see if the mouse pointer has
stabilised (checking every <code>time</code>/2 milliseconds) and runs the
{@link #show} method if it has.
@param {integer} time The minimum time (ms) before the popup may be shown.
*/
Navpopup.prototype.showSoonIfStable = function (time) {
log ('showSoonIfStable, time='+time);
if (this.visible) { return; }
this.noshow = false;
//~ initialize these variables so that we never run @tt{show} after
//~ just half the time
this.stable_x = -10000; this.stable_y = -10000;
var stableShow = function() {
log('stableShow called');
var new_x = Navpopup.tracker.x, new_y = Navpopup.tracker.y;
var dx = savedThis.stable_x - new_x, dy = savedThis.stable_y - new_y;
var fuzz2 = 0; // savedThis.fuzz * savedThis.fuzz;
//document.title += '[' + [savedThis.stable_x,new_x, savedThis.stable_y,new_y, dx, dy, fuzz2].join(',') + '] ';
if ( dx * dx <= fuzz2 && dy * dy <= fuzz2 ) {
log ('mouse is stable');
clearInterval(savedThis.showSoonStableTimer);
savedThis.reposition.apply(savedThis, [new_x + 2, new_y + 2]);
savedThis.show.apply(savedThis, []);
savedThis.limitHorizontalPosition.apply(savedThis, []);
return;
}
savedThis.stable_x = new_x; savedThis.stable_y = new_y;
};
var savedThis = this;
this.showSoonStableTimer = setInterval(stableShow, time/2);
};
/**
Sets the {@link #noshow} flag and hides the popup. This should be called
when the mouse leaves the link before
(or after) it's actually been displayed.
*/
Navpopup.prototype.banish = function () {
log ('banish called');
// hide and prevent showing with showSoon in the future
this.noshow=true;
if (this.showSoonStableTimer) {
log('clearing showSoonStableTimer');
clearInterval(this.showSoonStableTimer);
}
this.hide();
};
/**
Runs hooks added with {@link #addHook}.
@private
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
*/
Navpopup.prototype.runHooks = function (key, when) {
if (!this.hooks[key]) { return; }
var keyHooks=this.hooks[key];
var len=keyHooks.length;
for (var i=0; i< len; ++i) {
if (keyHooks[i] && keyHooks[i].when == when) {
if (keyHooks[i].hook.apply(this, [])) {
// remove the hook
if (keyHooks[i].hookId) {
delete this.hookIds[keyHooks[i].hookId];
}
keyHooks[i]=null;
}
}
}
};
/**
Adds a hook to the popup. Hook functions are run with <code>this</code> set to refer to the Navpopup instance, and no arguments.
@param {Function} hook The hook function. Functions that return true are deleted.
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide'
@param {String} when Controls exactly when the hook is run: either 'before' or 'after'
@param {String} uid A truthy string identifying the hook function; if it matches another hook in this position, it won't be added again.
*/
Navpopup.prototype.addHook = function ( hook, key, when, uid ) {
when = when || 'after';
if (!this.hooks[key]) { return; }
// if uid is specified, don't add duplicates
var hookId=null;
if (uid) {
hookId=[key,when,uid].join('|');
if (this.hookIds[hookId]) {
return;
}
this.hookIds[hookId]=true;
}
this.hooks[key].push( {hook: hook, when: when, hookId: hookId} );
};
/**
Creates the main DIV element, which contains all the actual popup content.
Runs hooks with key 'create'.
@private
*/
Navpopup.prototype.createMainDiv = function () {
if (this.mainDiv) { return; }
this.runHooks('create', 'before');
var mainDiv=document.createElement('div');
var savedThis=this;
mainDiv.onclick=function(e) {savedThis.onclickHandler(e);};
mainDiv.className=(this.className) ? this.className : 'navpopup_maindiv';
mainDiv.id=mainDiv.className + this.uid;
mainDiv.style.position='absolute';
mainDiv.style.minWidth = '350px';
mainDiv.style.display='none';
mainDiv.className='navpopup';
// easy access to javascript object through DOM functions
mainDiv.navpopup=this;
this.mainDiv=mainDiv;
document.body.appendChild(mainDiv);
this.runHooks('create', 'after');
};
/**
Calls the {@link #raise} method.
@private
*/
Navpopup.prototype.onclickHandler=function(/*e*/) {
this.raise();
};
/**
Makes the popup draggable, using a {@link Drag} object.
@private
*/
Navpopup.prototype.makeDraggable=function(handleName) {
if (!this.mainDiv) { this.createMainDiv(); }
var drag=new Drag();
if (!handleName) {
drag.startCondition=function(e) {
try { if (!e.shiftKey) { return false; } } catch (err) { return false; }
return true;
};
}
var dragHandle;
if (handleName) dragHandle = document.getElementById(handleName);
if (!dragHandle) dragHandle = this.mainDiv;
var np=this;
drag.endHook=function(x,y) {
Navpopup.tracker.dirty=true;
np.reposition(x,y);
};
drag.init(dragHandle,this.mainDiv);
};
/** Hides the popup using CSS. Runs hooks with key 'hide'.
Sets {@link #visible} appropriately. {@link #banish} should be called externally instead of this method.
@private
*/
Navpopup.prototype.hide = function () {
this.runHooks('hide', 'before');
this.abortDownloads();
if (typeof this.visible != 'undefined' && this.visible) {
this.mainDiv.style.display='none';
this.visible=false;
}
this.runHooks('hide', 'after');
};
/** Shows the popup using CSS. Runs hooks with key 'unhide'.
Sets {@link #visible} appropriately. {@link #show} should be called externally instead of this method.
@private
*/
Navpopup.prototype.unhide = function () {
this.runHooks('unhide', 'before');
if (typeof this.visible != 'undefined' && !this.visible) {
this.mainDiv.style.display='inline';
this.visible=true;
}
this.runHooks('unhide', 'after');
};
/**
Sets the <code>innerHTML</code> attribute of the main div containing the popup content.
@param {String} html The HTML to set.
*/
Navpopup.prototype.setInnerHTML = function (html) {
this.mainDiv.innerHTML = html;
};
/**
Updates the {@link #width} and {@link #height} attributes with the CSS properties.
@private
*/
Navpopup.prototype.updateDimensions = function () {
this.width=parseInt(this.mainDiv.offsetWidth, 10);
this.height=parseInt(this.mainDiv.offsetHeight, 10);
};
/**
Checks if the point (x,y) is within {@link #fuzz} of the
{@link #mainDiv}.
@param {integer} x x-coordinate (px)
@param {integer} y y-coordinate (px)
@type boolean
*/
Navpopup.prototype.isWithin = function(x,y) {
//~ If we're not even visible, no point should be considered as
//~ being within the popup.
if (!this.visible) { return false; }
this.updateDimensions();
var fuzz=this.fuzz || 0;
//~ Use a simple box metric here.
return (x+fuzz >= this.left && x-fuzz <= this.left + this.width &&
y+fuzz >= this.top && y-fuzz <= this.top + this.height);
};
/**
Adds a download to {@link #downloads}.
@param {Downloader} download
*/
Navpopup.prototype.addDownload=function(download) {
if (!download) { return; }
this.downloads.push(download);
};
/**
Aborts the downloads listed in {@link #downloads}.
@see Downloader#abort
*/
Navpopup.prototype.abortDownloads=function() {
for(var i=0; i<this.downloads.length; ++i) {
var d=this.downloads[i];
if (d && d.abort) { d.abort(); }
}
this.downloads=[];
};
/**
A {@link Mousetracker} instance which is a property of the constructor (pseudo-global).
*/
Navpopup.tracker=new Mousetracker();
// ENDFILE: navpopup.js
// STARTFILE: diff.js
//<NOLITE>
/*
* Javascript Diff Algorithm
* By John Resig (http://ejohn.org/) and [[:en:User:Lupin]]
*
* More Info:
* http://ejohn.org/projects/javascript-diff-algorithm/
*/
function delFmt(x) {
if (!x.length) { return ''; }
return "<del class='popupDiff'>" + x.join('') +"</del>";
}
function insFmt(x) {
if (!x.length) { return ''; }
return "<ins class='popupDiff'>" + x.join('') +"</ins>";
}
function countCrossings(a, b, i, eject) {
// count the crossings on the edge starting at b[i]
if (!b[i].row && b[i].row !== 0) { return -1; }
var count=0;
for (var j=0; j<a.length; ++j) {
if (!a[j].row && a[j].row !== 0) { continue; }
if ( (j-b[i].row)*(i-a[j].row) > 0) {
if(eject) { return true; }
count++;
}
}
return count;
}
function shortenDiffString(str, context) {
var re=RegExp('(<del[\\s\\S]*?</del>|<ins[\\s\\S]*?</ins>)');
var splitted=str.parenSplit(re);
var ret=[''];
for (var i=0; i<splitted.length; i+=2) {
if (splitted[i].length < 2*context) {
ret[ret.length-1] += splitted[i];
if (i+1<splitted.length) { ret[ret.length-1] += splitted[i+1]; }
continue;
}
else {
if (i > 0) { ret[ret.length-1] += splitted[i].substring(0,context); }
if (i+1 < splitted.length) {
ret.push(splitted[i].substring(splitted[i].length-context) +
splitted[i+1]);
}
}
}
while (ret.length > 0 && !ret[0]) { ret = ret.slice(1); }
return ret;
}
function diffString( o, n, simpleSplit ) {
var splitRe=RegExp('([[]{2}|[\\]]{2}|[{]{2,3}|[}]{2,3}|[|]|=|<|>|[*:]+|\\s|\\b)');
// We need to split the strings o and n first, and entify() the parts
// individually, so that the HTML entities are never cut apart. (AxelBoldt)
var out, i, oSplitted, nSplitted;
if (simpleSplit) {
oSplitted=o.split(/\b/);
nSplitted=n.split(/\b/);
} else {
oSplitted=o.parenSplit(splitRe);
nSplitted=n.parenSplit(splitRe);
}
for (i=0; i<oSplitted.length; ++i) {oSplitted[i]=oSplitted[i].entify();}
for (i=0; i<nSplitted.length; ++i) {nSplitted[i]=nSplitted[i].entify();}
out = diff (oSplitted, nSplitted);
var str = "";
var acc=[]; // accumulator for prettier output
// crossing pairings -- eg 'A B' vs 'B A' -- cause problems, so let's iron them out
// this doesn't always do things optimally but it should be fast enough
var maxOutputPair=0;
for (i=0; i<out.n.length; ++i) {
if ( out.n[i].paired ) {
if( maxOutputPair > out.n[i].row ) {
// tangle - delete pairing
out.o[ out.n[i].row ]=out.o[ out.n[i].row ].text;
out.n[i]=out.n[i].text;
}
if (maxOutputPair < out.n[i].row) { maxOutputPair = out.n[i].row; }
}
}
// output the stuff preceding the first paired old line
for (i=0; i<out.o.length && !out.o[i].paired; ++i) { acc.push( out.o[i] ); }
str += delFmt(acc); acc=[];
// main loop
for ( i = 0; i < out.n.length; ++i ) {
// output unpaired new "lines"
while ( i < out.n.length && !out.n[i].paired ) { acc.push( out.n[i++] ); }
str += insFmt(acc); acc=[];
if ( i < out.n.length ) { // this new "line" is paired with the (out.n[i].row)th old "line"
str += out.n[i].text;
// output unpaired old rows starting after this new line's partner
var m = out.n[i].row + 1;
while ( m < out.o.length && !out.o[m].paired ) { acc.push ( out.o[m++] ); }
str += delFmt(acc); acc=[];
}
}
return str;
}
// see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Object
// FIXME: use obj.hasOwnProperty instead of this kludge!
var jsReservedProperties=RegExp('^(constructor|prototype|__((define|lookup)[GS]etter)__' +
'|eval|hasOwnProperty|propertyIsEnumerable' +
'|to(Source|String|LocaleString)|(un)?watch|valueOf)$');
function diffBugAlert(word) {
if (!diffBugAlert.list[word]) {
diffBugAlert.list[word]=1;
alert('Bad word: '+word+'\n\nPlease report this bug.');
}
}
diffBugAlert.list={};
function makeDiffHashtable(src) {
var ret={};
for ( var i = 0; i < src.length; i++ ) {
if ( jsReservedProperties.test(src[i]) ) { src[i] += '<!-- -->'; }
if ( !ret[ src[i] ] ) { ret[ src[i] ] = []; }
try { ret[ src[i] ].push( i ); } catch (err) { diffBugAlert(src[i]); }
}
return ret;
}
function diff( o, n ) {
// pass 1: make hashtable ns with new rows as keys
var ns = makeDiffHashtable(n);
// pass 2: make hashtable os with old rows as keys
var os = makeDiffHashtable(o);
// pass 3: pair unique new rows and matching unique old rows
var i;
for ( i in ns ) {
if ( ns[i].length == 1 && os[i] && os[i].length == 1 ) {
n[ ns[i][0] ] = { text: n[ ns[i][0] ], row: os[i][0], paired: true };
o[ os[i][0] ] = { text: o[ os[i][0] ], row: ns[i][0], paired: true };
}
}
// pass 4: pair matching rows immediately following paired rows (not necessarily unique)
for ( i = 0; i < n.length - 1; i++ ) {
if ( n[i].paired && ! n[i+1].paired && n[i].row + 1 < o.length && ! o[ n[i].row + 1 ].paired &&
n[i+1] == o[ n[i].row + 1 ] ) {
n[i+1] = { text: n[i+1], row: n[i].row + 1, paired: true };
o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1, paired: true };
}
}
// pass 5: pair matching rows immediately preceding paired rows (not necessarily unique)
for ( i = n.length - 1; i > 0; i-- ) {
if ( n[i].paired && ! n[i-1].paired && n[i].row > 0 && ! o[ n[i].row - 1 ].paired &&
n[i-1] == o[ n[i].row - 1 ] ) {
n[i-1] = { text: n[i-1], row: n[i].row - 1, paired: true };
o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1, paired: true };
}
}
return { o: o, n: n };
}
//</NOLITE>
// ENDFILE: diff.js
// STARTFILE: init.js
function setSiteInfo() {
if (window.popupLocalDebug) {
pg.wiki.hostname = 'en.wikipedia.org';
} else {
pg.wiki.hostname = location.hostname; // use in preference to location.hostname for flexibility (?)
}
pg.wiki.wikimedia=RegExp('(wiki([pm]edia|source|books|news|quote|versity)|wiktionary|mediawiki)[.]org').test(pg.wiki.hostname);
pg.wiki.wikia=RegExp('[.]wikia[.]com$', 'i').test(pg.wiki.hostname);
pg.wiki.isLocal=RegExp('^localhost').test(pg.wiki.hostname);
pg.wiki.commons=( pg.wiki.wikimedia && pg.wiki.hostname != 'commons.wikimedia.org') ? 'commons.wikimedia.org' : null;
pg.wiki.lang = mw.config.get('wgContentLanguage');
var port = location.port ? ':' + location.port : '';
pg.wiki.sitebase = pg.wiki.hostname + port;
}
function setUserInfo() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
list: 'users',
ususers: mw.config.get('wgUserName'),
usprop: 'rights'
};
pg.user.canReview = false;
if (getValueOf('popupReview')) {
api.get(params).done(function(data){
var rights = data.query.users[0].rights;
pg.user.canReview = rights.indexOf('review') !== -1; // TODO: Should it be a getValueOf('ReviewRight') ?
});
}
}
function setTitleBase() {
var protocol = ( window.popupLocalDebug ? 'http:' : location.protocol );
pg.wiki.articlePath = mw.config.get('wgArticlePath').replace(/\/\$1/, ""); // as in http://some.thing.com/wiki/Article
pg.wiki.botInterfacePath = mw.config.get('wgScript');
pg.wiki.APIPath = mw.config.get('wgScriptPath') +"/api.php";
// default mediawiki setting is paths like http://some.thing.com/articlePath/index.php?title=foo
var titletail = pg.wiki.botInterfacePath + '?title=';
//var titletail2 = joinPath([pg.wiki.botInterfacePath, 'wiki.phtml?title=']);
// other sites may need to add code here to set titletail depending on how their urls work
pg.wiki.titlebase = protocol + '//' + pg.wiki.sitebase + titletail;
//pg.wiki.titlebase2 = protocol + '//' + joinPath([pg.wiki.sitebase, titletail2]);
pg.wiki.wikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.botInterfacePath;
pg.wiki.apiwikibase = protocol + '//' + pg.wiki.sitebase + pg.wiki.APIPath;
pg.wiki.articlebase = protocol + '//' + pg.wiki.sitebase + pg.wiki.articlePath;
pg.wiki.commonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.botInterfacePath;
pg.wiki.apicommonsbase = protocol + '//' + pg.wiki.commons + pg.wiki.APIPath;
pg.re.basenames = RegExp( '^(' +
map( literalizeRegex, [ pg.wiki.titlebase, //pg.wiki.titlebase2,
pg.wiki.articlebase ]).join('|') + ')' );
}
//////////////////////////////////////////////////
// Global regexps
function setMainRegex() {
var reStart='[^:]*://';
var preTitles = literalizeRegex( mw.config.get('wgScriptPath') ) + '/(?:index[.]php|wiki[.]phtml)[?]title=';
preTitles += '|' + literalizeRegex( pg.wiki.articlePath + '/' );
var reEnd='(' + preTitles + ')([^&?#]*)[^#]*(?:#(.+))?';
pg.re.main = RegExp(reStart + literalizeRegex(pg.wiki.sitebase) + reEnd);
}
function setRegexps() {
// TODO: We shoud use an api call to get the aliases for special pages, now it does not work for non-English wikipedias:
// E.g., https://ru.wikipedia.org/w/api.php?action=query&meta=siteinfo&siprop=specialpagealiases&formatversion=2
setMainRegex();
var sp=nsRe(pg.nsSpecialId);
pg.re.urlNoPopup=RegExp('((title=|/)' + sp + '(?:%3A|:)|section=[0-9]|^#$)') ;
pg.re.contribs =RegExp('(title=|/)' + sp + '(?:%3A|:)Contributions' + '(&target=|/|/' + nsRe(pg.nsUserId)+':)(.*)') ;
pg.re.email =RegExp('(title=|/)' + sp + '(?:%3A|:)EmailUser' + '(&target=|/|/(?:' + nsRe(pg.nsUserId)+':)?)(.*)') ;
pg.re.backlinks =RegExp('(title=|/)' + sp + '(?:%3A|:)WhatLinksHere' + '(&target=|/)([^&]*)');
pg.re.specialdiff=RegExp('/' + sp + '(?:%3A|:)Diff/([^?#]*)');
//<NOLITE>
var im=nsReImage();
// note: tries to get images in infobox templates too, e.g. movie pages, album pages etc
// (^|\[\[)image: *([^|\]]*[^|\] ]) *
// (^|\[\[)image: *([^|\]]*[^|\] ])([^0-9\]]*([0-9]+) *px)?
// $4 = 120 as in 120px
pg.re.image = RegExp('(^|\\[\\[)' + im + ': *([^|\\]]*[^|\\] ])' +
'([^0-9\\]]*([0-9]+) *px)?|(?:\\n *[|]?|[|]) *' +
'(' + getValueOf('popupImageVarsRegexp') + ')' +
' *= *(?:\\[\\[ *)?(?:' + im + ':)?' +
'([^|]*?)(?:\\]\\])? *[|]? *\\n', 'img') ;
pg.re.imageBracketCount = 6;
pg.re.category = RegExp('\\[\\[' +nsRe(pg.nsCategoryId) +
': *([^|\\]]*[^|\\] ]) *', 'i');
pg.re.categoryBracketCount = 1;
pg.re.ipUser=RegExp('^' +
// IPv6
'(?::(?::|(?::[0-9A-Fa-f]{1,4}){1,7})|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){0,6}::|[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4}){7})' +
// IPv4
'|(((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}' +
'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9]))$');
pg.re.stub= RegExp(getValueOf('popupStubRegexp'), 'im');
pg.re.disambig=RegExp(getValueOf('popupDabRegexp'), 'im');
//</NOLITE>
// FIXME replace with general parameter parsing function, this is daft
pg.re.oldid=RegExp('[?&]oldid=([^&]*)');
pg.re.diff=RegExp('[?&]diff=([^&]*)');
}
//////////////////////////////////////////////////
// miscellany
function setupCache() {
// page caching
pg.cache.pages = [];
}
function setMisc() {
pg.current.link=null;
pg.current.links=[];
pg.current.linksHash={};
setupCache();
pg.timer.checkPopupPosition=null;
pg.counter.loop=0;
// ids change with each popup: popupImage0, popupImage1 etc
pg.idNumber=0;
// for myDecodeURI
pg.misc.decodeExtras = [
{from: '%2C', to: ',' },
{from: '_', to: ' ' },
{from: '%24', to: '$'},
{from: '%26', to: '&' } // no ,
];
pg.misc.userAgent = 'Navigation popups/1.0 (' + mw.config.get( 'wgServerName' ) +')';
}
// We need a callback since this might end up asynchronous because of
// the mw.loader.using() call.
function setupPopups( callback ) {
if ( setupPopups.completed ) {
if ( typeof callback === 'function' ) {
callback();
}
return;
}
// These dependencies are also enforced from the gadget,
// but not everyone loads this as a gadget, so double check
mw.loader.using( ['mediawiki.util', 'mediawiki.user', 'user.options'] ).then( function() {
// NB translatable strings should be set up first (strings.js)
// basics
setupDebugging();
setSiteInfo();
setTitleBase();
setOptions(); // see options.js
setUserInfo();
// namespaces etc
setNamespaces();
setInterwiki();
// regexps
setRegexps();
setRedirs();
// other stuff
setMisc();
setupLivePreview();
// main deal here
setupTooltips();
log('In setupPopups(), just called setupTooltips()');
Navpopup.tracker.enable();
setupPopups.completed = true;
if ( typeof callback === 'function' ) {
callback();
}
});
}
// ENDFILE: init.js
// STARTFILE: navlinks.js
//<NOLITE>
//////////////////////////////////////////////////
// navlinks... let the fun begin
//
function defaultNavlinkSpec() {
var str='';
str += '<b><<mainlink|shortcut= >></b>';
if (getValueOf('popupLastEditLink')) {
str += '*<<lastEdit|shortcut=/>>|<<lastContrib>>|<<sinceMe>>if(oldid){|<<oldEdit>>|<<diffCur>>}';
}
// user links
// contribs - log - count - email - block
// count only if applicable; block only if popupAdminLinks
str += 'if(user){<br><<contribs|shortcut=c>>*<<userlog|shortcut=L|log>>';
str+='if(ipuser){*<<arin>>}if(wikimedia){*<<count|shortcut=#>>}';
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>|<<blocklog|log>>}}';
// editing links
// talkpage -> edit|new - history - un|watch - article|edit
// other page -> edit - history - un|watch - talk|edit|new
var editstr='<<edit|shortcut=e>>';
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' + editstr + '}';
var historystr='<<history|shortcut=h>>|<<editors|shortcut=E|>>';
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>';
str+='<br>if(talk){' +
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' +
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' +
'}else{' + // not a talk page
editOldidStr + '*' + historystr + '*' + watchstr + '*' +
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>}';
// misc links
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>*<<move|shortcut=m>>';
// admin links
str += 'if(admin){<br><<unprotect|unprotectShort>>|<<protect|shortcut=p>>|<<protectlog|log>>*' +
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>|<<deletelog|log>>}';
return str;
}
function navLinksHTML (article, hint, params) { //oldid, rcid) {
var str = '<span class="popupNavLinks">' + defaultNavlinkSpec() + '</span>';
// BAM
return navlinkStringToHTML(str, article, params);
}
function expandConditionalNavlinkString(s,article,z,recursionCount) {
var oldid=z.oldid, rcid=z.rcid, diff=z.diff;
// nested conditionals (up to 10 deep) are ok, hopefully! (work from the inside out)
if (typeof recursionCount!=typeof 0) { recursionCount=0; }
var conditionalSplitRegex=RegExp(
//(1 if \\( (2 2) \\) {(3 3)} (4 else {(5 5)} 4)1)
'(;?\\s*if\\s*\\(\\s*([\\w]*)\\s*\\)\\s*\\{([^{}]*)\\}(\\s*else\\s*\\{([^{}]*?)\\}|))', 'i');
var splitted=s.parenSplit(conditionalSplitRegex);
// $1: whole conditional
// $2: test condition
// $3: true expansion
// $4: else clause (possibly empty)
// $5: false expansion (possibly null)
var numParens=5;
var ret = splitted[0];
for (var i=1; i<splitted.length; i=i+numParens+1) {
var testString=splitted[i+2-1];
var trueString=splitted[i+3-1];
var falseString=splitted[i+5-1];
if (typeof falseString=='undefined' || !falseString) { falseString=''; }
var testResult=null;
switch (testString) {
case 'user':
testResult=(article.userName())?true:false;
break;
case 'talk':
testResult=(article.talkPage())?false:true; // talkPage converts _articles_ to talkPages
break;
case 'admin':
testResult=getValueOf('popupAdminLinks')?true:false;
break;
case 'oldid':
testResult=(typeof oldid != 'undefined' && oldid)?true:false;
break;
case 'rcid':
testResult=(typeof rcid != 'undefined' && rcid)?true:false;
break;
case 'ipuser':
testResult=(article.isIpUser())?true:false;
break;
case 'mainspace_en':
testResult=isInMainNamespace(article) &&
pg.wiki.hostname=='en.wikipedia.org';
break;
case 'wikimedia':
testResult=(pg.wiki.wikimedia) ? true : false;
break;
case 'diff':
testResult=(typeof diff != 'undefined' && diff)?true:false;
break;
}
switch(testResult) {
case null: ret+=splitted[i]; break;
case true: ret+=trueString; break;
case false: ret+=falseString; break;
}
// append non-conditional string
ret += splitted[i+numParens];
}
if (conditionalSplitRegex.test(ret) && recursionCount < 10) {
return expandConditionalNavlinkString(ret,article,z,recursionCount+1);
}
return ret;
}
function navlinkStringToArray(s, article, params) {
s=expandConditionalNavlinkString(s,article,params);
var splitted=s.parenSplit(RegExp('<<(.*?)>>'));
var ret=[];
for (var i=0; i<splitted.length; ++i) {
if (i%2) { // i odd, so s is a tag
var t=new navlinkTag();
var ss=splitted[i].split('|');
t.id=ss[0];
for (var j=1; j<ss.length; ++j) {
var sss=ss[j].split('=');
if (sss.length>1) {
t[sss[0]]=sss[1];
}
else { // no assignment (no "="), so treat this as a title (overwriting the last one)
t.text=popupString(sss[0]);
}
}
t.article=article;
var oldid=params.oldid, rcid=params.rcid, diff=params.diff;
if (typeof oldid !== 'undefined' && oldid !== null) { t.oldid=oldid; }
if (typeof rcid !== 'undefined' && rcid !== null) { t.rcid=rcid; }
if (typeof diff !== 'undefined' && diff !== null) { t.diff=diff; }
if (!t.text && t.id !== 'mainlink') { t.text=popupString(t.id); }
ret.push(t);
}
else { // plain HTML
ret.push(splitted[i]);
}
}
return ret;
}
function navlinkSubstituteHTML(s) {
return s.split('*').join(getValueOf('popupNavLinkSeparator'))
.split('<menurow>').join('<li class="popup_menu_row">')
.split('</menurow>').join('</li>')
.split('<menu>').join('<ul class="popup_menu">')
.split('</menu>').join('</ul>');
}
function navlinkDepth(magic,s) {
return s.split('<' + magic + '>').length - s.split('</' + magic + '>').length;
}
// navlinkString: * becomes the separator
// <<foo|bar=baz|fubar>> becomes a foo-link with attribute bar='baz'
// and visible text 'fubar'
// if(test){...} and if(test){...}else{...} work too (nested ok)
function navlinkStringToHTML(s,article,params) {
//limitAlert(navlinkStringToHTML, 5, 'navlinkStringToHTML\n' + article + '\n' + (typeof article));
var p=navlinkStringToArray(s,article,params);
var html='';
var menudepth = 0; // nested menus not currently allowed, but doesn't do any harm to code for it
var menurowdepth = 0;
for (var i=0; i<p.length; ++i) {
if (typeof p[i] == typeof '') {
html+=navlinkSubstituteHTML(p[i]);
menudepth += navlinkDepth('menu', p[i]);
menurowdepth += navlinkDepth('menurow', p[i]);
// if (menudepth === 0) {
// tagType='span';
// } else if (menurowdepth === 0) {
// tagType='li';
// } else {
// tagType = null;
// }
} else if (typeof p[i].type != 'undefined' && p[i].type=='navlinkTag') {
if (menudepth > 0 && menurowdepth === 0) {
html += '<li class="popup_menu_item">' + p[i].html() + '</li>';
} else {
html+=p[i].html();
}
}
}
return html;
}
function navlinkTag() {
this.type='navlinkTag';
}
navlinkTag.prototype.html=function () {
this.getNewWin();
this.getPrintFunction();
var html='';
var opening, closing;
var tagType='span';
if (!tagType) {
opening = ''; closing = '';
} else {
opening = '<' + tagType + ' class="popup_' + this.id + '">';
closing = '</' + tagType + '>';
}
if (typeof this.print!='function') {
errlog ('Oh dear - invalid print function for a navlinkTag, id='+this.id);
} else {
html=this.print(this);
if (typeof html != typeof '') {html='';}
else if (typeof this.shortcut!='undefined') html=addPopupShortcut(html, this.shortcut);
}
return opening + html + closing;
};
navlinkTag.prototype.getNewWin=function() {
getValueOf('popupLinksNewWindow');
if (typeof pg.option.popupLinksNewWindow[this.id] === 'undefined') { this.newWin=null; }
this.newWin=pg.option.popupLinksNewWindow[this.id];
};
navlinkTag.prototype.getPrintFunction=function() { //think about this some more
// this.id and this.article should already be defined
if (typeof this.id!=typeof '' || typeof this.article!=typeof {} ) { return; }
this.noPopup=1;
switch (this.id) {
case 'contribs': case 'history': case 'whatLinksHere':
case 'userPage': case 'monobook': case 'userTalk':
case 'talk': case 'article': case 'lastEdit':
this.noPopup=null;
}
switch (this.id) {
case 'email': case 'contribs': case 'block': case 'unblock':
case 'userlog': case 'userSpace': case 'deletedContribs':
this.article=this.article.userName();
}
switch (this.id) {
case 'userTalk': case 'newUserTalk': case 'editUserTalk':
case 'userPage': case 'monobook': case 'editMonobook': case 'blocklog':
this.article=this.article.userName(true);
/* fall through */
case 'pagelog': case 'deletelog': case 'protectlog':
delete this.oldid;
}
if (this.id=='editMonobook' || this.id=='monobook') { this.article.append('/monobook.js'); }
if (this.id != 'mainlink') {
// FIXME anchor handling should be done differently with Title object
this.article=this.article.removeAnchor();
// if (typeof this.text=='undefined') this.text=popupString(this.id);
}
switch (this.id) {
case 'undelete': this.print=specialLink; this.specialpage='Undelete'; this.sep='/'; break;
case 'whatLinksHere': this.print=specialLink; this.specialpage='Whatlinkshere'; break;
case 'relatedChanges': this.print=specialLink; this.specialpage='Recentchangeslinked'; break;
case 'move': this.print=specialLink; this.specialpage='Movepage'; break;
case 'contribs': this.print=specialLink; this.specialpage='Contributions'; break;
case 'deletedContribs':this.print=specialLink; this.specialpage='Deletedcontributions'; break;
case 'email': this.print=specialLink; this.specialpage='EmailUser'; this.sep='/'; break;
case 'block': this.print=specialLink; this.specialpage='Blockip'; this.sep='&ip='; break;
case 'unblock': this.print=specialLink; this.specialpage='Ipblocklist'; this.sep='&action=unblock&ip='; break;
case 'userlog': this.print=specialLink; this.specialpage='Log'; this.sep='&user='; break;
case 'blocklog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=block&page='; break;
case 'pagelog': this.print=specialLink; this.specialpage='Log'; this.sep='&page='; break;
case 'protectlog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=protect&page='; break;
case 'deletelog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=delete&page='; break;
case 'userSpace': this.print=specialLink; this.specialpage='PrefixIndex'; this.sep='&namespace=2&prefix='; break;
case 'search': this.print=specialLink; this.specialpage='Search'; this.sep='&fulltext=Search&search='; break;
case 'thank': this.print=specialLink; this.specialpage='Thanks'; this.sep='/'; this.article.value = this.diff; break;
case 'unwatch': case 'watch':
this.print=magicWatchLink; this.action=this.id+'&autowatchlist=1&autoimpl=' + popupString('autoedit_version') + '&actoken='+autoClickToken(); break;
case 'history': case 'historyfeed':
case 'unprotect': case 'protect':
this.print=wikiLink; this.action=this.id; break;
case 'delete':
this.print=wikiLink; this.action='delete';
if (this.article.namespaceId()==pg.nsImageId) {
var img=this.article.stripNamespace();
this.action+='&image='+img;
}
break;
case 'markpatrolled':
case 'edit': // editOld should keep the oldid, but edit should not.
delete this.oldid;
/* fall through */
case 'view': case 'purge': case 'render':
this.print=wikiLink;
this.action=this.id; break;
case 'raw':
this.print=wikiLink; this.action='raw'; break;
case 'new':
this.print=wikiLink; this.action='edit§ion=new'; break;
case 'mainlink':
if (typeof this.text=='undefined') { this.text=this.article.toString().entify(); }
if (getValueOf('popupSimplifyMainLink') && isInStrippableNamespace(this.article)) {
// only show the /subpage part of the title text
var s=this.text.split('/'); this.text=s[s.length-1];
if (this.text==='' && s.length > 1) { this.text=s[s.length-2]; }
}
this.print=titledWikiLink;
if (typeof this.title==='undefined' && pg.current.link && typeof pg.current.link.href !== 'undefined') {
this.title=safeDecodeURI((pg.current.link.originalTitle)?pg.current.link.originalTitle:this.article);
if (typeof this.oldid !== 'undefined' && this.oldid) {
this.title=tprintf('Revision %s of %s', [this.oldid, this.title]);
}
}
this.action='view'; break;
case 'userPage':
case 'article':
case 'monobook':
case 'editMonobook':
case 'editArticle':
delete this.oldid;
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.article=this.article.articleFromTalkOrArticle();
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article);
this.print=wikiLink;
if (this.id.indexOf('edit')===0) {
this.action='edit';
} else { this.action='view';}
break;
case 'userTalk':
case 'talk':
this.article=this.article.talkPage();
delete this.oldid;
this.print=wikiLink;
this.action='view'; break;
case 'arin':
this.print=arinLink; break;
case 'count':
this.print=editCounterLink; break;
case 'google':
this.print=googleLink; break;
case 'editors':
this.print=editorListLink; break;
case 'globalsearch':
this.print=globalSearchLink; break;
case 'lastEdit':
this.print=titledDiffLink;
this.title=popupString('Show the last edit');
this.from='prev'; this.to='cur'; break;
case 'oldEdit':
this.print=titledDiffLink;
this.title=popupString('Show the edit made to get revision') + ' ' + this.oldid;
this.from='prev'; this.to=this.oldid; break;
case 'editOld':
this.print=wikiLink; this.action='edit'; break;
case 'undo':
this.print=wikiLink; this.action='edit&undo='; break;
case 'revert':
this.print=wikiLink; this.action='revert'; break;
case 'nullEdit':
this.print=wikiLink; this.action='nullEdit'; break;
case 'diffCur':
this.print=titledDiffLink;
this.title=tprintf('Show changes since revision %s', [this.oldid]);
this.from=this.oldid; this.to='cur'; break;
case 'editUserTalk':
case 'editTalk':
delete this.oldid;
this.article=this.article.talkPage();
this.action='edit'; this.print=wikiLink; break;
case 'newUserTalk':
case 'newTalk':
this.article=this.article.talkPage();
this.action='edit§ion=new'; this.print=wikiLink; break;
case 'lastContrib':
case 'sinceMe':
this.print=magicHistoryLink;
break;
case 'togglePreviews':
this.text=popupString(pg.option.simplePopups ? 'enable previews' : 'disable previews');
/* fall through */
case 'disablePopups': case 'purgePopups':
this.print=popupMenuLink;
break;
default:
this.print=function () {return 'Unknown navlink type: '+this.id+'';};
}
};
//
// end navlinks
//////////////////////////////////////////////////
//</NOLITE>
// ENDFILE: navlinks.js
// STARTFILE: shortcutkeys.js
//<NOLITE>
function popupHandleKeypress(evt) {
var keyCode = window.event ? window.event.keyCode : ( evt.keyCode ? evt.keyCode : evt.which);
if (!keyCode || !pg.current.link || !pg.current.link.navpopup) { return; }
if (keyCode==27) { // escape
killPopup();
return false; // swallow keypress
}
var letter=String.fromCharCode(keyCode);
var links=pg.current.link.navpopup.mainDiv.getElementsByTagName('A');
var startLink=0;
var i,j;
if (popupHandleKeypress.lastPopupLinkSelected) {
for (i=0; i<links.length; ++i) {
if (links[i]==popupHandleKeypress.lastPopupLinkSelected) { startLink=i; }
}
}
for (j=0; j<links.length; ++j) {
i=(startLink + j + 1) % links.length;
if (links[i].getAttribute('popupkey')==letter) {
if (evt && evt.preventDefault) evt.preventDefault();
links[i].focus();
popupHandleKeypress.lastPopupLinkSelected=links[i];
return false; // swallow keypress
}
}
// pass keypress on
if (document.oldPopupOnkeypress) { return document.oldPopupOnkeypress(evt); }
return true;
}
function addPopupShortcuts() {
if (document.onkeypress!=popupHandleKeypress) {
document.oldPopupOnkeypress=document.onkeypress;
}
document.onkeypress=popupHandleKeypress;
}
function rmPopupShortcuts() {
popupHandleKeypress.lastPopupLinkSelected=null;
try {
if (document.oldPopupOnkeypress && document.oldPopupOnkeypress==popupHandleKeypress) {
// panic
document.onkeypress=null; //function () {};
return;
}
document.onkeypress=document.oldPopupOnkeypress;
} catch (nasties) { /* IE goes here */ }
}
function addLinkProperty(html, property) {
// take "<a href=...>...</a> and add a property
// not sophisticated at all, easily broken
var i=html.indexOf('>');
if (i<0) { return html; }
return html.substring(0,i) + ' ' + property + html.substring(i);
}
function addPopupShortcut(html, key) {
if (!getValueOf('popupShortcutKeys')) { return html; }
var ret= addLinkProperty(html, 'popupkey="'+key+'"');
if (key==' ') { key=popupString('spacebar'); }
return ret.replace(RegExp('^(.*?)(title=")(.*?)(".*)$', 'i'),'$1$2$3 ['+key+']$4');
}
//</NOLITE>
// ENDFILE: shortcutkeys.js
// STARTFILE: diffpreview.js
//<NOLITE>
//lets jump through hoops to find the rev ids we need to retrieve
function loadDiff(article, oldid, diff, navpop) {
navpop.diffData={ oldRev: {}, newRev: {} };
mw.loader.using( 'mediawiki.api' ).then( function() {
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'compare',
prop: 'ids|title'
};
if(article.title){
params.fromtitle = article.title;
}
switch (diff) {
case 'cur':
switch ( oldid ) {
case null:
case '':
case 'prev':
// this can only work if we have the title
// cur -> prev
params.torelative = 'prev';
break;
default:
params.fromrev = oldid;
params.torelative = 'cur';
break;
}
break;
case 'prev':
if( oldid ) {
params.fromrev = oldid;
} else {
params.fromtitle;
}
params.torelative = 'prev';
break;
case 'next':
params.fromrev = oldid || 0;
params.torelative = 'next';
break;
default:
params.fromrev = oldid || 0;
params.torev = diff || 0;
break;
}
api.get( params ).then( function( data ) {
navpop.diffData.oldRev.revid = data.compare.fromrevid;
navpop.diffData.newRev.revid = data.compare.torevid;
addReviewLink(navpop, 'popupMiscTools');
var go = function() {
pendingNavpopTask(navpop);
var url=pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&';
url += 'revids=' + navpop.diffData.oldRev.revid + '|' + navpop.diffData.newRev.revid;
url += '&prop=revisions&rvprop=ids|timestamp|content';
getPageWithCaching(url, doneDiff, navpop);
return true; // remove hook once run
};
if (navpop.visible || !getValueOf('popupLazyDownloads')) { go(); }
else { navpop.addHook(go, 'unhide', 'before', 'DOWNLOAD_DIFFS'); }
} );
} );
}
// Put a "mark patrolled" link to an element target
// TODO: Allow patrol a revision, as well as a diff
function addReviewLink (navpop, target) {
if (! pg.user.canReview) return;
// If 'newRev' is older than 'oldRev' than it could be confusing, so we do not show the review link.
if (navpop.diffData.newRev.revid <= navpop.diffData.oldRev.revid) return;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
var params = {
action: 'query',
prop: 'info|flagged',
revids: navpop.diffData.oldRev.revid,
formatversion : 2
};
api.get (params).then(function(data){
var stable_revid = data.query.pages[0].flagged && data.query.pages[0].flagged.stable_revid || 0;
// The diff can be reviewed if the old version is the last reviewed version
// TODO: Other possible conditions that we may want to implement instead of this one:
// * old version is patrolled and the new version is not patrolled
// * old version is patrolled and the new version is more recent than the last reviewed version
if (stable_revid == navpop.diffData.oldRev.revid) {
var a = document.createElement('a');
a.innerHTML = popupString('mark patrolled');
a.title=popupString('markpatrolledHint');
a.onclick = function() {
var params = {
action: 'review',
revid: navpop.diffData.newRev.revid,
comment: tprintf('defaultpopupReviewedSummary', [navpop.diffData.oldRev.revid, navpop.diffData.newRev.revid])
};
api.postWithToken('csrf',params).done(function(){
a.style.display = "none";
// TODO: Update current page and other already constructed popups
} ).fail(function(){
alert(popupString('Could not marked this edit as patrolled'));
});
};
setPopupHTML(a, target, navpop.idNumber,null,true);
}
});
}
function doneDiff(download) {
if (!download.owner || !download.owner.diffData) { return; }
var navpop=download.owner;
completedNavpopTask(navpop);
var pages, revisions=[];
try{
// Process the downloads
pages = getJsObj(download.data).query.pages;
for(var i=0; i < pages.length; i++ ) {
revisions = revisions.concat(pages[i].revisions);
}
for(i=0; i< revisions.length; i++){
if(revisions[i].revid == navpop.diffData.oldRev.revid) {
navpop.diffData.oldRev.revision = revisions[i];
} else if (revisions[i].revid == navpop.diffData.newRev.revid) {
navpop.diffData.newRev.revision = revisions[i];
}
}
} catch(someError) {
errlog( 'Could not get diff' );
}
insertDiff(navpop);
}
function rmBoringLines(a,b,context) {
if (typeof context == 'undefined') { context=2; }
// this is fairly slow... i think it's quicker than doing a word-based diff from the off, though
var aa=[], aaa=[];
var bb=[], bbb=[];
var i, j;
// first, gather all disconnected nodes in a and all crossing nodes in a and b
for (i=0; i<a.length; ++i ) {
if(!a[i].paired) { aa[i]=1; }
else if (countCrossings(b,a,i, true)) {
aa[i]=1;
bb[ a[i].row ] = 1;
}
}
// pick up remaining disconnected nodes in b
for (i=0; i<b.length; ++i ) {
if (bb[i]==1) { continue; }
if(!b[i].paired) { bb[i]=1; }
}
// another pass to gather context: we want the neighbours of included nodes which are not yet included
// we have to add in partners of these nodes, but we don't want to add context for *those* nodes in the next pass
for (i=0; i<b.length; ++i) {
if ( bb[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(b.length, i+context); ++j) {
if ( !bb[j] ) { bb[j] = 1; aa[ b[j].row ] = 0.5; }
}
}
}
for (i=0; i<a.length; ++i) {
if ( aa[i] == 1 ) {
for (j=Math.max(0,i-context); j < Math.min(a.length, i+context); ++j) {
if ( !aa[j] ) { aa[j] = 1; bb[ a[j].row ] = 0.5; }
}
}
}
for (i=0; i<bb.length; ++i) {
if (bb[i] > 0) { // it's a row we need
if (b[i].paired) { bbb.push(b[i].text); } // joined; partner should be in aa
else {
bbb.push(b[i]);
}
}
}
for (i=0; i<aa.length; ++i) {
if (aa[i] > 0) { // it's a row we need
if (a[i].paired) { aaa.push(a[i].text); } // joined; partner should be in aa
else {
aaa.push(a[i]);
}
}
}
return { a: aaa, b: bbb};
}
function stripOuterCommonLines(a,b,context) {
var i=0;
while (i<a.length && i < b.length && a[i]==b[i]) { ++i; }
var j=a.length-1; var k=b.length-1;
while ( j>=0 && k>=0 && a[j]==b[k] ) { --j; --k; }
return { a: a.slice(Math.max(0,i - 1 - context), Math.min(a.length+1, j + context+1)),
b: b.slice(Math.max(0,i - 1 - context), Math.min(b.length+1, k + context+1)) };
}
function insertDiff(navpop) {
// for speed reasons, we first do a line-based diff, discard stuff that seems boring, then do a word-based diff
// FIXME: sometimes this gives misleading diffs as distant chunks are squashed together
var oldlines = navpop.diffData.oldRev.revision.content.split('\n');
var newlines = navpop.diffData.newRev.revision.content.split('\n');
var inner=stripOuterCommonLines(oldlines,newlines,getValueOf('popupDiffContextLines'));
oldlines=inner.a; newlines=inner.b;
var truncated=false;
getValueOf('popupDiffMaxLines');
if (oldlines.length > pg.option.popupDiffMaxLines || newlines.length > pg.option.popupDiffMaxLines) {
// truncate
truncated=true;
inner=stripOuterCommonLines(oldlines.slice(0,pg.option.popupDiffMaxLines),
newlines.slice(0,pg.option.popupDiffMaxLines),
pg.option.popupDiffContextLines);
oldlines=inner.a; newlines=inner.b;
}
var lineDiff=diff(oldlines, newlines);
var lines2=rmBoringLines(lineDiff.o, lineDiff.n);
var oldlines2=lines2.a; var newlines2=lines2.b;
var simpleSplit = !String.prototype.parenSplit.isNative;
var html='<hr />';
if (getValueOf('popupDiffDates')) {
html += diffDatesTable(navpop);
html += '<hr />';
}
html += shortenDiffString(
diffString(oldlines2.join('\n'), newlines2.join('\n'), simpleSplit),
getValueOf('popupDiffContextCharacters') ).join('<hr />');
setPopupTipsAndHTML(html.split('\n').join('<br>') +
(truncated ? '<hr /><b>'+popupString('Diff truncated for performance reasons')+'</b>' : '') ,
'popupPreview', navpop.idNumber);
}
function diffDatesTable( navpop ) {
var html='<table class="popup_diff_dates">';
html += diffDatesTableRow( navpop.diffData.newRev.revision, tprintf('New revision'));
html += diffDatesTableRow( navpop.diffData.oldRev.revision, tprintf('Old revision'));
html += '</table>';
return html;
}
function diffDatesTableRow( revision, label ) {
var txt='';
var lastModifiedDate = new Date(revision.timestamp);
var datePrint=getValueOf('popupDiffDatePrinter');
if (typeof lastModifiedDate[datePrint] == 'function') {
var d2 = adjustDate(lastModifiedDate, getTimeOffset());
txt = dayFormat(d2, true) + ' ' + timeFormat(d2, true);
} else {
txt = tprintf('Invalid %s %s', ['popupDiffDatePrinter', datePrint]);
}
var revlink = generalLink({url: mw.config.get('wgScript') + '?oldid='+revision.revid,
text: label, title: label});
return simplePrintf('<tr><td>%s</td><td>%s</td></tr>', [ revlink, txt ]);
}
//</NOLITE>
// ENDFILE: diffpreview.js
// STARTFILE: links.js
//<NOLITE>
/////////////////////
// LINK GENERATION //
/////////////////////
// titledDiffLink --> titledWikiLink --> generalLink
// wikiLink --> titledWikiLink --> generalLink
// editCounterLink --> generalLink
// TODO Make these functions return Element objects, not just raw HTML strings.
function titledDiffLink(l) { // article, text, title, from, to) {
return titledWikiLink({article: l.article, action: l.to + '&oldid=' + l.from,
newWin: l.newWin,
noPopup: l.noPopup,
text: l.text, title: l.title,
/* hack: no oldid here */
actionName: 'diff'});
}
function wikiLink(l) {
//{article:article, action:action, text:text, oldid, newid}) {
if (! (typeof l.article == typeof {} &&
typeof l.action == typeof '' &&
typeof l.text==typeof '')) return null;
if (typeof l.oldid == 'undefined') { l.oldid=null; }
var savedOldid = l.oldid;
if (!/^(edit|view|revert|render)$|^raw/.test(l.action)) { l.oldid=null; }
var hint=popupString(l.action + 'Hint'); // revertHint etc etc etc
var oldidData=[l.oldid, safeDecodeURI(l.article)];
var revisionString = tprintf('revision %s of %s', oldidData);
log('revisionString='+revisionString);
switch (l.action) {
case 'edit§ion=new': hint = popupString('newSectionHint'); break;
case 'edit&undo=':
if (l.diff && l.diff != 'prev' && savedOldid ) {
l.action += l.diff + '&undoafter=' + savedOldid;
} else if (savedOldid) {
l.action += savedOldid;
}
hint = popupString('undoHint');
break;
case 'raw&ctype=text/css': hint=popupString('rawHint'); break;
case 'revert':
var p=parseParams(pg.current.link.href);
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=' + revertSummary(l.oldid, p.diff);
if (p.diff=='prev') {
l.action += '&direction=prev';
revisionString = tprintf('the revision prior to revision %s of %s', oldidData);
}
if (getValueOf('popupRevertSummaryPrompt')) { l.action += '&autosummaryprompt=true'; }
if (getValueOf('popupMinorReverts')) { l.action += '&autominor=true'; }
log('revisionString is now '+revisionString);
break;
case 'nullEdit':
l.action='edit&autoclick=wpSave&actoken=' + autoClickToken() + '&autoimpl=' + popupString('autoedit_version') + '&autosummary=null';
break;
case 'historyfeed':
l.action='history&feed=rss';
break;
case 'markpatrolled':
l.action='markpatrolled&rcid='+l.rcid;
}
if (hint) {
if (l.oldid) {
hint = simplePrintf(hint, [revisionString]);
}
else {
hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
}
}
else {
hint = safeDecodeURI(l.article + '&action=' + l.action) + (l.oldid) ? '&oldid='+l.oldid : '';
}
return titledWikiLink({article: l.article, action: l.action, text: l.text, newWin:l.newWin,
title: hint, oldid: l.oldid, noPopup: l.noPopup, onclick: l.onclick});
}
function revertSummary(oldid, diff) {
var ret='';
if (diff == 'prev') {
ret=getValueOf('popupQueriedRevertToPreviousSummary');
} else { ret = getValueOf('popupQueriedRevertSummary'); }
return ret + '&autorv=' + oldid;
}
function titledWikiLink(l) {
// possible properties of argument:
// article, action, text, title, oldid, actionName, className, noPopup
// oldid = null is fine here
// article and action are mandatory args
if (typeof l.article == 'undefined' || typeof l.action=='undefined') {
errlog('got undefined article or action in titledWikiLink');
return null;
}
var base = pg.wiki.titlebase + l.article.urlString();
var url=base;
if (typeof l.actionName=='undefined' || !l.actionName) { l.actionName='action'; }
// no need to add &action=view, and this confuses anchors
if (l.action != 'view') { url = base + '&' + l.actionName + '=' + l.action; }
if (typeof l.oldid!='undefined' && l.oldid) { url+='&oldid='+l.oldid; }
var cssClass=pg.misc.defaultNavlinkClassname;
if (typeof l.className!='undefined' && l.className) { cssClass=l.className; }
return generalNavLink({url: url, newWin: l.newWin,
title: (typeof l.title != 'undefined') ? l.title : null,
text: (typeof l.text!='undefined')?l.text:null,
className: cssClass, noPopup:l.noPopup, onclick:l.onclick});
}
pg.fn.getLastContrib = function getLastContrib(wikipage, newWin) {
getHistoryInfo(wikipage, function(x) {
processLastContribInfo(x, {page: wikipage, newWin: newWin});
});
};
function processLastContribInfo(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: an odd thing happened. Please retry.'); return; }
if(!info.firstNewEditor) {
alert(tprintf('Only found one editor: %s made %s edits', [info.edits[0].editor,info.edits.length]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+info.firstNewEditor.oldid;
displayUrl(newUrl, stuff.newWin);
}
pg.fn.getDiffSinceMyEdit = function getDiffSinceMyEdit(wikipage, newWin) {
getHistoryInfo(wikipage, function(x){
processDiffSinceMyEdit(x, {page: wikipage, newWin: newWin});
});
};
function processDiffSinceMyEdit(info, stuff) {
if(!info.edits || !info.edits.length) { alert('Popups: something fishy happened. Please try again.'); return; }
var friendlyName=stuff.page.split('_').join(' ');
if(!info.myLastEdit) {
alert(tprintf('Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
[info.userName, getValueOf('popupHistoryLimit'), friendlyName]));
return;
}
if(info.myLastEdit.index === 0) {
alert(tprintf("%s seems to be the last editor to the page %s", [info.userName, friendlyName]));
return;
}
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+ info.myLastEdit.oldid;
displayUrl(newUrl, stuff.newWin);
}
function displayUrl(url, newWin){
if(newWin) { window.open(url); }
else { document.location=url; }
}
pg.fn.purgePopups = function purgePopups() {
processAllPopups(true);
setupCache(); // deletes all cached items (not browser cached, though...)
pg.option={};
abortAllDownloads();
};
function processAllPopups(nullify, banish) {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
if (!pg.current.links[i].navpopup) { continue; }
if (nullify || banish) pg.current.links[i].navpopup.banish();
pg.current.links[i].simpleNoMore=false;
if (nullify) pg.current.links[i].navpopup=null;
}
}
pg.fn.disablePopups = function disablePopups(){
processAllPopups(false, true);
setupTooltips(null, true);
};
pg.fn.togglePreviews = function togglePreviews() {
processAllPopups(true, true);
pg.option.simplePopups=!pg.option.simplePopups;
abortAllDownloads();
};
function magicWatchLink(l) {
//Yuck!! Would require a thorough redesign to add this as a click event though ...
l.onclick = simplePrintf( 'pg.fn.modifyWatchlist(\'%s\',\'%s\');return false;', [l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), this.id] );
return wikiLink(l);
}
pg.fn.modifyWatchlist = function modifyWatchlist(title, action) {
var reqData = {
'action': 'watch',
'formatversion': 2,
'titles': title,
'uselang': mw.config.get('wgUserLanguage')
};
if ( action === 'unwatch' ) reqData.unwatch = true;
var api = new mw.Api( {
ajax: {
headers: { 'Api-User-Agent': pg.misc.userAgent }
}
} );
// Load the Addedwatchtext or Removedwatchtext message and show it
var mwTitle = mw.Title.newFromText( title );
var messageName;
if ( mwTitle && mwTitle.getNamespaceId() > 0 && mwTitle.getNamespaceId() % 2 === 1 ) {
messageName = action === 'watch' ? 'addedwatchtext-talk' : 'removedwatchtext-talk';
} else {
messageName = action === 'watch' ? 'addedwatchtext' : 'removedwatchtext';
}
$.when(
api.postWithToken( 'watch', reqData ),
mw.loader.using( [ 'mediawiki.api', 'mediawiki.jqueryMsg' ] ).then( function () {
return api.loadMessagesIfMissing( [ messageName ] );
} )
).done( function () {
mw.notify( mw.message( messageName, title ).parseDom() );
} );
};
function magicHistoryLink(l) {
// FIXME use onclick change href trick to sort this out instead of window.open
var jsUrl='', title='', onClick='';
switch(l.id) {
case 'lastContrib':
onClick=simplePrintf('pg.fn.getLastContrib(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('lastContribHint');
break;
case 'sinceMe':
onClick=simplePrintf('pg.fn.getDiffSinceMyEdit(\'%s\',%s)',
[l.article.toString(true).split("\\").join("\\\\").split("'").join("\\'"), l.newWin]);
title=popupString('sinceMeHint');
break;
}
jsUrl = 'javascript:' + onClick; // jshint ignore:line
onClick += ';return false;';
return generalNavLink({url: jsUrl, newWin: false, // can't have new windows with JS links, I think
title: title, text: l.text, noPopup: l.noPopup, onclick: onClick });
}
function popupMenuLink(l) {
var jsUrl=simplePrintf('javascript:pg.fn.%s()', [l.id]); // jshint ignore:line
var title=popupString(simplePrintf('%sHint', [l.id]));
var onClick=simplePrintf('pg.fn.%s();return false;', [l.id]);
return generalNavLink({url: jsUrl, newWin:false, title:title, text:l.text, noPopup:l.noPopup, onclick: onClick});
}
function specialLink(l) {
// properties: article, specialpage, text, sep
if (typeof l.specialpage=='undefined'||!l.specialpage) return null;
var base = pg.wiki.titlebase + mw.config.get('wgFormattedNamespaces')[pg.nsSpecialId]+':'+l.specialpage;
if (typeof l.sep == 'undefined' || l.sep === null) l.sep='&target=';
var article=l.article.urlString({keepSpaces: l.specialpage=='Search'});
var hint=popupString(l.specialpage+'Hint');
switch (l.specialpage) {
case 'Log':
switch (l.sep) {
case '&user=': hint=popupString('userLogHint'); break;
case '&type=block&page=': hint=popupString('blockLogHint'); break;
case '&page=': hint=popupString('pageLogHint'); break;
case '&type=protect&page=': hint=popupString('protectLogHint'); break;
case '&type=delete&page=': hint=popupString('deleteLogHint'); break;
default: log('Unknown log type, sep=' + l.sep); hint='Missing hint (FIXME)';
}
break;
case 'PrefixIndex': article += '/'; break;
}
if (hint) hint = simplePrintf(hint, [safeDecodeURI(l.article)]);
else hint = safeDecodeURI(l.specialpage+':'+l.article) ;
var url = base + l.sep + article;
return generalNavLink({url: url, title: hint, text: l.text, newWin:l.newWin, noPopup:l.noPopup});
}
function generalLink(l) {
// l.url, l.text, l.title, l.newWin, l.className, l.noPopup, l.onclick
if (typeof l.url=='undefined') return null;
// only quotation marks in the url can screw us up now... I think
var url=l.url.split('"').join('%22');
var ret='<a href="' + url + '"';
if (typeof l.title!='undefined' && l.title) { ret += ' title="' + pg.escapeQuotesHTML(l.title) + '"'; }
if (typeof l.onclick!='undefined' && l.onclick) { ret += ' onclick="' + pg.escapeQuotesHTML(l.onclick) + '"'; }
if (l.noPopup) { ret += ' noPopup=1'; }
var newWin;
if (typeof l.newWin=='undefined' || l.newWin === null) { newWin=getValueOf('popupNewWindows'); }
else { newWin=l.newWin; }
if (newWin) { ret += ' target="_blank"'; }
if (typeof l.className!='undefined'&&l.className) { ret+=' class="'+l.className+'"'; }
ret += '>';
if (typeof l.text==typeof '') { ret+= l.text; }
ret +='</a>';
return ret;
}
function appendParamsToLink(linkstr, params) {
var sp=linkstr.parenSplit(RegExp('(href="[^"]+?)"', 'i'));
if (sp.length<2) return null;
var ret=sp.shift() + sp.shift();
ret += '&' + params + '"';
ret += sp.join('');
return ret;
}
function changeLinkTargetLink(x) { // newTarget, text, hint, summary, clickButton, minor, title (optional), alsoChangeLabel {
if (x.newTarget) {
log ('changeLinkTargetLink: newTarget=' + x.newTarget);
}
if (x.oldTarget !== decodeURIComponent( x.oldTarget ) ) {
log ('This might be an input problem: ' + x.oldTarget );
}
// FIXME: first character of page title as well as namespace should be case insensitive
// eg [[category:X1]] and [[Category:X1]] are equivalent
// this'll break if charAt(0) is nasty
var cA = mw.util.escapeRegExp(x.oldTarget);
var chs = cA.charAt(0).toUpperCase();
chs='['+chs + chs.toLowerCase()+']';
var currentArticleRegexBit=chs+cA.substring(1);
currentArticleRegexBit=currentArticleRegexBit
.split(RegExp('(?:[_ ]+|%20)', 'g')).join('(?:[_ ]+|%20)')
.split('\\(').join('(?:%28|\\()')
.split('\\)').join('(?:%29|\\))'); // why does this need to match encoded strings ? links in the document ?
// leading and trailing space should be ignored, and anchor bits optional:
currentArticleRegexBit = '\\s*(' + currentArticleRegexBit + '(?:#[^\\[\\|]*)?)\\s*';
// e.g. Computer (archaic) -> \s*([Cc]omputer[_ ](?:%2528|\()archaic(?:%2528|\)))\s*
// autoedit=s~\[\[([Cc]ad)\]\]~[[Computer-aided%20design|$1]]~g;s~\[\[([Cc]AD)[|]~[[Computer-aided%20design|~g
var title=x.title || mw.config.get('wgPageName').split('_').join(' ');
var lk=titledWikiLink({article: new Title(title), newWin:x.newWin,
action: 'edit',
text: x.text,
title: x.hint,
className: 'popup_change_title_link'
});
var cmd='';
if (x.newTarget) {
// escape '&' and other nasties
var t = x.newTarget;
var s = mw.util.escapeRegExp(x.newTarget);
if (x.alsoChangeLabel) {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + ']]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
} else {
cmd += 's~\\[\\[' + currentArticleRegexBit + '\\]\\]~[[' + t + '|$1]]~g;';
cmd += 's~\\[\\[' + currentArticleRegexBit + '[|]~[[' + t + '|~g;';
cmd += 's~\\[\\[' + s + '\\|' + s + '\\]\\]~[[' + t + ']]~g';
}
} else {
cmd += 's~\\[\\['+currentArticleRegexBit+'\\]\\]~$1~g;';
cmd += 's~\\[\\['+currentArticleRegexBit+'[|](.*?)\\]\\]~$2~g';
}
// Build query
cmd = 'autoedit=' + encodeURIComponent ( cmd );
cmd += '&autoclick='+ encodeURIComponent( x.clickButton ) + '&actoken=' + encodeURIComponent( autoClickToken() );
cmd += ( x.minor === null ) ? '' : '&autominor='+ encodeURIComponent( x.minor );
cmd += ( x.watch === null ) ? '' : '&autowatch='+ encodeURIComponent( x.watch );
cmd += '&autosummary='+encodeURIComponent(x.summary);
cmd += '&autoimpl='+encodeURIComponent( popupString('autoedit_version') );
return appendParamsToLink(lk, cmd);
}
function redirLink(redirMatch, article) {
// NB redirMatch is in wikiText
var ret='';
if (getValueOf('popupAppendRedirNavLinks') && getValueOf('popupNavLinks')) {
ret += '<hr />';
if (getValueOf('popupFixRedirs') && typeof autoEdit != 'undefined' && autoEdit) {
ret += popupString('Redirects to: (Fix ');
log('redirLink: newTarget=' + redirMatch);
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target'),
hint: popupString('Fix this redirect, changing just the link target'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages')
}), 'R');
ret += popupString(' or ');
ret += addPopupShortcut(changeLinkTargetLink({
newTarget: redirMatch,
text: popupString('target & label'),
hint: popupString('Fix this redirect, changing the link target and label'),
summary: simplePrintf(getValueOf('popupFixRedirsSummary'),[article.toString(), redirMatch]),
oldTarget: article.toString(),
clickButton: getValueOf('popupRedirAutoClick'),
minor: true,
watch: getValueOf('popupWatchRedirredPages'),
alsoChangeLabel: true
}), 'R');
ret += popupString(')');
}
else ret += popupString('Redirects') + popupString(' to ');
return ret;
}
else return '<br> ' + popupString('Redirects') + popupString(' to ') +
titledWikiLink({article: new Title().fromWikiText(redirMatch), action: 'view', /* FIXME: newWin */
text: safeDecodeURI(redirMatch), title: popupString('Bypass redirect')});
}
function arinLink(l) {
if (!saneLinkCheck(l)) { return null; }
if ( ! l.article.isIpUser() || ! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
return generalNavLink({url:'http://ws.arin.net/cgi-bin/whois.pl?queryinput=' + encodeURIComponent(uN), newWin:l.newWin,
title: tprintf('Look up %s in ARIN whois database', [uN]),
text: l.text, noPopup:1});
}
function toolDbName(cookieStyle) {
var ret = mw.config.get('wgDBname');
if (!cookieStyle) { ret+= '_p'; }
return ret;
}
function saneLinkCheck(l) {
if (typeof l.article != typeof {} || typeof l.text != typeof '') { return false; }
return true;
}
function editCounterLink(l) {
if(!saneLinkCheck(l)) return null;
if (! pg.wiki.wikimedia) return null;
var uN=l.article.userName();
var tool=getValueOf('popupEditCounterTool');
var url;
var defaultToolUrl='//tools.wmflabs.org/supercount/index.php?user=$1&project=$2.$3';
switch(tool) {
case 'custom':
url=simplePrintf(getValueOf('popupEditCounterUrl'), [ encodeURIComponent(uN), toolDbName() ]);
break;
case 'soxred': // no longer available
case 'kate': // no longer available
case 'interiot':// no longer available
/* fall through */
case 'supercount':
default:
var theWiki=pg.wiki.hostname.split('.');
url=simplePrintf(defaultToolUrl, [ encodeURIComponent(uN), theWiki[0], theWiki[1] ]);
}
return generalNavLink({url:url, title: tprintf('editCounterLinkHint', [uN]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function globalSearchLink(l) {
if(!saneLinkCheck(l)) return null;
var base='http://vs.aka-online.de/cgi-bin/globalwpsearch.pl?timeout=120&search=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + article, newWin:l.newWin,
title: tprintf('globalSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function googleLink(l) {
if(!saneLinkCheck(l)) return null;
var base='https://www.google.com/search?q=';
var article=l.article.urlString({keepSpaces:true});
return generalNavLink({url:base + '%22' + article + '%22', newWin:l.newWin,
title: tprintf('googleSearchHint', [safeDecodeURI(l.article)]),
text: l.text, noPopup:1});
}
function editorListLink(l) {
if(!saneLinkCheck(l)) return null;
var article= l.article.articleFromTalkPage() || l.article;
var url='https://xtools.wmflabs.org/articleinfo/' +
encodeURI( pg.wiki.hostname ) + '/' +
article.urlString() +
'?uselang=' + mw.config.get('wgUserLanguage');
return generalNavLink({url:url,
title: tprintf('editorListHint', [article]),
newWin:l.newWin, text: l.text, noPopup:1});
}
function generalNavLink(l) {
l.className = (l.className === null) ? 'popupNavLink' : l.className;
return generalLink(l);
}
//////////////////////////////////////////////////
// magic history links
//
function getHistoryInfo(wikipage, whatNext) {
log('getHistoryInfo');
getHistory(wikipage, whatNext ? function(d){whatNext(processHistory(d));} : processHistory);
}
// FIXME eliminate pg.idNumber ... how? :-(
function getHistory(wikipage, onComplete) {
log('getHistory');
var url = pg.wiki.apiwikibase + '?format=json&formatversion=2&action=query&prop=revisions&titles=' +
new Title(wikipage).urlString() + '&rvlimit=' + getValueOf('popupHistoryLimit');
log('getHistory: url='+url);
return startDownload(url, pg.idNumber+'history', onComplete);
}
function processHistory(download) {
var jsobj = getJsObj(download.data);
try {
var revisions = anyChild(jsobj.query.pages).revisions;
var edits=[];
for (var i=0; i<revisions.length; ++i) {
edits.push({ oldid: revisions[i].revid, editor: revisions[i].user });
}
log('processed ' + edits.length + ' edits');
return finishProcessHistory( edits, mw.config.get('wgUserName') );
} catch (someError) {
log('Something went wrong with JSON business');
return finishProcessHistory([]);
}
}
function finishProcessHistory(edits, userName) {
var histInfo={};
histInfo.edits=edits;
histInfo.userName=userName;
for (var i=0; i<edits.length; ++i) {
if (typeof histInfo.myLastEdit === 'undefined' && userName && edits[i].editor==userName) {
histInfo.myLastEdit={index: i, oldid: edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
if (typeof histInfo.firstNewEditor === 'undefined' && edits[i].editor != edits[0].editor) {
histInfo.firstNewEditor={index:i, oldid:edits[i].oldid, previd: (i === 0 ? null : edits[i-1].oldid)};
}
}
//pg.misc.historyInfo=histInfo;
return histInfo;
}
//</NOLITE>
// ENDFILE: links.js
// STARTFILE: options.js
//////////////////////////////////////////////////
// options
// check for existing value, else use default
function defaultize(x) {
if (pg.option[x]===null || typeof pg.option[x]=='undefined') {
if (typeof window[x] != 'undefined' ) pg.option[x]=window[x];
else pg.option[x]=pg.optionDefault[x];
}
}
function newOption(x, def) {
pg.optionDefault[x]=def;
}
function setDefault(x, def) {
return newOption(x, def);
}
function getValueOf(varName) {
defaultize(varName);
return pg.option[varName];
}
/*eslint-disable */
function useDefaultOptions() { // for testing
for (var p in pg.optionDefault) {
pg.option[p]=pg.optionDefault[p];
if (typeof window[p]!='undefined') { delete window[p]; }
}
}
/*eslint-enable */
function setOptions() {
// user-settable parameters and defaults
var userIsSysop = false;
if ( mw.config.get('wgUserGroups') ) {
for ( var g = 0; g < mw.config.get('wgUserGroups').length; ++g ) {
if ( mw.config.get('wgUserGroups')[g] == "sysop" )
userIsSysop = true;
}
}
// Basic options
newOption('popupDelay', 0.5);
newOption('popupHideDelay', 0.5);
newOption('simplePopups', false);
newOption('popupStructure', 'shortmenus'); // see later - default for popupStructure is 'original' if simplePopups is true
newOption('popupActionsMenu', true);
newOption('popupSetupMenu', true);
newOption('popupAdminLinks', userIsSysop);
newOption('popupShortcutKeys', false);
newOption('popupHistoricalLinks', true);
newOption('popupOnlyArticleLinks', true);
newOption('removeTitles', true);
newOption('popupMaxWidth', 350);
newOption('popupSimplifyMainLink', true);
newOption('popupAppendRedirNavLinks', true);
newOption('popupTocLinks', false);
newOption('popupSubpopups', true);
newOption('popupDragHandle', false /* 'popupTopLinks'*/);
newOption('popupLazyPreviews', true);
newOption('popupLazyDownloads', true);
newOption('popupAllDabsStubs', false);
newOption('popupDebugging', false);
newOption('popupActiveNavlinks', true);
newOption('popupModifier', false); // ctrl, shift, alt or meta
newOption('popupModifierAction', 'enable'); // or 'disable'
newOption('popupDraggable', true);
newOption('popupReview', false);
//<NOLITE>
// images
newOption('popupImages', true);
newOption('imagePopupsForImages', true);
newOption('popupNeverGetThumbs', false);
//newOption('popupImagesToggleSize', true);
newOption('popupThumbAction', 'imagepage'); //'sizetoggle');
newOption('popupImageSize', 60);
newOption('popupImageSizeLarge', 200);
// redirs, dabs, reversion
newOption('popupFixRedirs', false);
newOption('popupRedirAutoClick', 'wpDiff');
newOption('popupFixDabs', false);
newOption('popupDabsAutoClick', 'wpDiff');
newOption('popupRevertSummaryPrompt', false);
newOption('popupMinorReverts', false);
newOption('popupRedlinkRemoval', false);
newOption('popupRedlinkAutoClick', 'wpDiff');
newOption('popupWatchDisambiggedPages', null);
newOption('popupWatchRedirredPages', null);
newOption('popupDabWiktionary', 'last');
// navlinks
newOption('popupNavLinks', true);
newOption('popupNavLinkSeparator', ' ⋅ ');
newOption('popupLastEditLink', true);
newOption('popupEditCounterTool', 'supercount');
newOption('popupEditCounterUrl', '');
//</NOLITE>
// previews etc
newOption('popupPreviews', true);
newOption('popupSummaryData', true);
newOption('popupMaxPreviewSentences', 9);
newOption('popupMaxPreviewCharacters', 900);
newOption('popupLastModified', true);
newOption('popupPreviewKillTemplates', true);
newOption('popupPreviewRawTemplates', true);
newOption('popupPreviewFirstParOnly', false);
newOption('popupPreviewCutHeadings', false);
newOption('popupPreviewButton', false);
newOption('popupPreviewButtonEvent', 'click');
//<NOLITE>
// diffs
newOption('popupPreviewDiffs', true);
newOption('popupDiffMaxLines', 100);
newOption('popupDiffContextLines', 2);
newOption('popupDiffContextCharacters', 40);
newOption('popupDiffDates', true);
newOption('popupDiffDatePrinter', 'toLocaleString');
// edit summaries. God, these are ugly.
newOption('popupReviewedSummary', popupString('defaultpopupReviewedSummary') );
newOption('popupFixDabsSummary', popupString('defaultpopupFixDabsSummary') );
newOption('popupExtendedRevertSummary', popupString('defaultpopupExtendedRevertSummary') );
newOption('popupRevertSummary', popupString('defaultpopupRevertSummary') );
newOption('popupRevertToPreviousSummary', popupString('defaultpopupRevertToPreviousSummary') );
newOption('popupQueriedRevertSummary', popupString('defaultpopupQueriedRevertSummary') );
newOption('popupQueriedRevertToPreviousSummary', popupString('defaultpopupQueriedRevertToPreviousSummary') );
newOption('popupFixRedirsSummary', popupString('defaultpopupFixRedirsSummary') );
newOption('popupRedlinkSummary', popupString('defaultpopupRedlinkSummary') );
newOption('popupRmDabLinkSummary', popupString('defaultpopupRmDabLinkSummary') );
//</NOLITE>
// misc
newOption('popupHistoryLimit', 50);
//<NOLITE>
newOption('popupFilters', [popupFilterStubDetect, popupFilterDisambigDetect,
popupFilterPageSize, popupFilterCountLinks,
popupFilterCountImages, popupFilterCountCategories,
popupFilterLastModified]);
newOption('extraPopupFilters', []);
newOption('popupOnEditSelection', 'cursor');
newOption('popupPreviewHistory', true);
newOption('popupImageLinks', true);
newOption('popupCategoryMembers', true);
newOption('popupUserInfo', true);
newOption('popupHistoryPreviewLimit', 25);
newOption('popupContribsPreviewLimit',25);
newOption('popupRevDelUrl', '//en.wikipedia.org/wiki/Wikipedia:Revision_deletion');
newOption('popupShowGender', true);
//</NOLITE>
// new windows
newOption('popupNewWindows', false);
newOption('popupLinksNewWindow', {'lastContrib': true, 'sinceMe': true});
// regexps
newOption('popupDabRegexp', '(\\{\\{\\s*disambig(?!uation needed)|disambig(uation|)\\s*\\}\\}|disamb\\s*\\}\\}|dab\\s*\\}\\})|\\{\\{\\s*(((geo|hn|road?|school|number)dis)|[234][lc][acw]|(road|ship)index)(\\s*[|][^}]*)?\\s*[}][}]|is a .*disambiguation.*page');
newOption('popupAnchorRegexp', 'anchors?'); //how to identify an anchors template
newOption('popupStubRegexp', '(sect)?stub[}][}]|This .*-related article is a .*stub');
newOption('popupImageVarsRegexp', 'image|image_(?:file|skyline|name|flag|seal)|cover|badge|logo');
}
// ENDFILE: options.js
// STARTFILE: strings.js
//<NOLITE>
//////////////////////////////////////////////////
// Translatable strings
//////////////////////////////////////////////////
//
// See instructions at
// https://en.wikipedia.org/wiki/Wikipedia:Tools/Navigation_popups/Translation
pg.string = {
/////////////////////////////////////
// summary data, searching etc.
/////////////////////////////////////
'article': 'bài viết',
'category': 'thể loại',
'categories': 'thể loại',
'image': 'image',
'images': 'tập tin',
'stub': 'stub',
'section stub': 'section stub',
'Empty page': 'Trang trống',
'kB': 'kB',
'bytes': 'byte',
'day': 'ngày',
'days': 'ngày',
'hour': 'giờ',
'hours': 'giờ',
'minute': 'minute',
'minutes': 'minutes',
'second': 'second',
'seconds': 'seconds',
'week': 'tuần',
'weeks': 'weeks',
'search': 'search',
'SearchHint': 'Find English Wikipedia articles containing %s',
'web': 'web',
'global': 'global',
'globalSearchHint': 'Search across Wikipedias in different languages for %s',
'googleSearchHint': 'Google for %s',
/////////////////////////////////////
// article-related actions and info
// (some actions also apply to user pages)
/////////////////////////////////////
'tác vụ': 'tác vụ', ///// view articles and view talk
'popupsMenu': 'popups',
'togglePreviewsHint': 'Toggle preview generation in popups on this page',
'enable previews': 'enable previews',
'disable previews': 'disable previews',
'toggle previews': 'toggle previews',
'show preview': 'show preview',
'reset': 'tái tạo',
'more...': 'more...',
'disable': 'tắt popup',
'disablePopupsHint': 'Disable popups on this page. Reload page to re-enable.',
'historyfeedHint': 'RSS feed of recent changes to this page',
'purgePopupsHint': 'Reset popups, clearing all cached popup data.',
'PopupsHint': 'Reset popups, clearing all cached popup data.',
'spacebar': 'space',
'view': 'view',
'view article': 'xem bài viết',
'viewHint': 'Go to %s',
'talk': 'talk',
'talk page': 'trang thảo luận',
'this revision': 'this revision',
'revision %s of %s': 'revision %s of %s',
'Revision %s of %s': 'Revision %s of %s',
'the revision prior to revision %s of %s': 'the revision prior to revision %s of %s',
'Toggle image size': 'Click to toggle image size',
'del': 'del', ///// delete, protect, move
'delete': 'xóa',
'deleteHint': 'xóa %s',
'undeleteShort': 'un',
'UndeleteHint': 'Show the deletion history for %s',
'protect': 'protect',
'protectHint': 'Restrict editing rights to %s',
'unprotectShort': 'un',
'unprotectHint': 'Allow %s to be edited by anyone again',
'send thanks': 'send thanks',
'ThanksHint': 'Send a thank you notification to this user',
'move': 'di chuyển',
'move page': 'di chuyển trang',
'MovepageHint': 'Change the title of %s',
'sửa': 'sửa', ///// edit articles and talk
'edit article': 'sửa đổi',
'editHint': 'Change the content of %s',
'edit talk': 'edit talk',
'new': 'mới',
'new topic': 'chủ đề mới',
'newSectionHint': 'Start a new section on %s',
'null edit': 'null edit',
'nullEditHint': 'Submit an edit to %s, making no changes ',
'hist': 'hist', ///// history, diffs, editors, related
'history': 'lịch sử',
'historyHint': 'List the changes made to %s',
'last': 'prev', // For labelling the previous revision in history pages; the key is "last" for backwards compatibility
'lastEdit': 'lastEdit',
'mark patrolled': 'đánh dấu tuần tra',
'markpatrolledHint': 'Mark this edit as patrolled',
'Could not marked this edit as patrolled': 'Could not marked this edit as patrolled',
'show last edit': 'lần sửa đổi gần nhất',
'Show the last edit': 'Show the effects of the most recent change',
'lastContrib': 'lastContrib',
'last set of edits': 'latest edits',
'lastContribHint': 'Show the net effect of changes made by the last editor',
'cur': 'cur',
'diffCur': 'diffCur',
'Show changes since revision %s': 'Show changes since revision %s',
'%s old': '%s old', // as in 4 weeks old
'oldEdit': 'oldEdit',
'purge': 'purge',
'purgeHint': 'Demand a fresh copy of %s',
'raw': 'source',
'rawHint': 'Download the source of %s',
'render': 'simple',
'renderHint': 'Show a plain HTML version of %s',
'Show the edit made to get revision': 'Show the edit made to get revision',
'sinceMe': 'sinceMe',
'changes since mine': 'diff my edit',
'sinceMeHint': 'Show changes since my last edit',
'Couldn\'t find an edit by %s\nin the last %s edits to\n%s': 'Couldn\'t find an edit by %s\nin the last %s edits to\n%s',
'eds': 'eds',
'editors': 'editors',
'editorListHint': 'List the users who have edited %s',
'related': 'related',
'relatedChanges': 'relatedChanges',
'related changes': 'related changes',
'RecentchangeslinkedHint': 'Show changes in articles related to %s',
'editOld': 'editOld', ///// edit old version, or revert
'rv': 'rv',
'revert': 'revert',
'revertHint': 'Revert to %s',
'defaultpopupReviewedSummary': 'Accepted by reviewing the [[Special:diff/%s/%s|difference]] between this version and previously accepted version using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRedlinkSummary': 'Removing link to empty page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixDabsSummary': 'Disambiguate [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupFixRedirsSummary': 'Redirect bypass from [[%s]] to [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupExtendedRevertSummary': 'Revert to revision dated %s by %s, oldid %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertToPreviousSummary': 'Revert to the revision prior to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRevertSummary': 'Revert to revision %s using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertToPreviousSummary': 'Revert to the revision prior to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupQueriedRevertSummary': 'Revert to revision $1 dated $2 by $3 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'defaultpopupRmDabLinkSummary': 'Remove link to dab page [[%s]] using [[:en:Wikipedia:Tools/Navigation_popups|popups]]',
'Redirects': 'Redirects', // as in Redirects to ...
' to ': ' to ', // as in Redirects to ...
'Bypass redirect': 'Bypass redirect',
'Fix this redirect': 'Fix this redirect',
'disambig': 'disambig', ///// add or remove dab etc.
'disambigHint': 'Disambiguate this link to [[%s]]',
'Click to disambiguate this link to:': 'Click to disambiguate this link to:',
'remove this link': 'remove this link',
'remove all links to this page from this article': 'remove all links to this page from this article',
'remove all links to this disambig page from this article': 'remove all links to this disambig page from this article',
'mainlink': 'mainlink', ///// links, watch, unwatch
'wikiLink': 'wikiLink',
'wikiLinks': 'liên kết wiki',
'links here': 'links here',
'whatLinksHere': 'whatLinksHere',
'what links here': 'what links here',
'WhatlinkshereHint': 'List the pages that are hyperlinked to %s',
'unwatchShort': 'un',
'watchThingy': 'xem', // called watchThingy because {}.watch is a function
'watchHint': 'Thêm %s vào danh sách theo dõi',
'unwatchHint': 'Remove %s from my watchlist',
'Only found one editor: %s made %s edits': 'Only found one editor: %s made %s edits',
'%s seems to be the last editor to the page %s': '%s seems to be the last editor to the page %s',
'rss': 'rss',
/////////////////////////////////////
// diff previews
/////////////////////////////////////
'Diff truncated for performance reasons': 'Diff truncated for performance reasons',
'Old revision': 'Old revision',
'New revision': 'New revision',
'Something went wrong :-(': 'Something went wrong :-(',
'Empty revision, maybe non-existent': 'Empty revision, maybe non-existent',
'Unknown date': 'Unknown date',
/////////////////////////////////////
// other special previews
/////////////////////////////////////
'Empty category': 'Empty category',
'Category members (%s shown)': 'Category members (%s shown)',
'No image links found': 'No image links found',
'File links': 'File links',
'No image found': 'No image found',
'Image from Commons': 'Image from Commons',
'Description page': 'Description page',
'Alt text:': 'Alt text:',
'revdel':'Hidden revision',
/////////////////////////////////////
// user-related actions and info
/////////////////////////////////////
'user': 'user', ///// user page, talk, email, space
'user page': 'user page',
'user talk': 'user talk',
'edit user talk': 'edit user talk',
'leave comment': 'leave comment',
'email': 'email',
'email user': 'email cho thành viên',
'EmailuserHint': 'Send an email to %s',
'space': 'space', // short form for userSpace link
'PrefixIndexHint': 'Show pages in the userspace of %s',
'count': 'count', ///// contributions, log
'edit counter': 'đếm sửa đổi',
'editCounterLinkHint': 'Count the contributions made by %s',
'contribs': 'đóng góp',
'contributions': 'đóng góp',
'deletedContribs': 'đóng góp bị xóa',
'DeletedcontributionsHint': 'List deleted edits made by %s',
'ContributionsHint': 'List the contributions made by %s',
'log': 'nhật trình',
'user log': 'user log',
'userLogHint': 'Show %s\'s user log',
'arin': 'ARIN lookup', ///// ARIN lookup, block user or IP
'Look up %s in ARIN whois database': 'Look up %s in the ARIN whois database',
'unblockShort': 'un',
'block': 'cấm',
'block user': 'cấm thành viên',
'IpblocklistHint': 'Unblock %s',
'BlockipHint': 'Prevent %s from editing',
'block log': 'nhật trình cấm',
'blockLogHint': 'Show the block log for %s',
'protectLogHint': 'Show the protection log for %s',
'pageLogHint': 'Show the page log for %s',
'deleteLogHint': 'Show the deletion log for %s',
'Invalid %s %s': 'The option %s is invalid: %s',
'No backlinks found': 'No backlinks found',
' and more': ' and more',
'undo': 'undo',
'undoHint': 'undo this edit',
'Download preview data': 'Download preview data',
'Invalid or IP user': 'Invalid or IP user',
'Not a registered username': 'Not a registered username',
'CẤM': 'CẤM',
'Has blocks': 'Cấm một phần',
' sửa đổi từ: ': ' sửa đổi từ: ',
'sửa đổi lần cuối vào ': 'sửa đổi lần cuối vào ',
/////////////////////////////////////
// Autoediting
/////////////////////////////////////
'Enter a non-empty edit summary or press cancel to abort': 'Enter a non-empty edit summary or press cancel to abort',
'Failed to get revision information, please edit manually.\n\n': 'Failed to get revision information, please edit manually.\n\n',
'The %s button has been automatically clicked. Please wait for the next page to load.': 'The %s button has been automatically clicked. Please wait for the next page to load.',
'Could not find button %s. Please check the settings in your javascript file.': 'Could not find button %s. Please check the settings in your javascript file.',
/////////////////////////////////////
// Popups setup
/////////////////////////////////////
'Open full-size image': 'Open full-size image',
'zxy': 'zxy',
'autoedit_version': 'np20140416'
};
function popupString(str) {
if (typeof popupStrings != 'undefined' && popupStrings && popupStrings[str]) { return popupStrings[str]; }
if (pg.string[str]) { return pg.string[str]; }
return str;
}
function tprintf(str,subs) {
if (typeof subs != typeof []) { subs = [subs]; }
return simplePrintf(popupString(str), subs);
}
//</NOLITE>
// ENDFILE: strings.js
// STARTFILE: run.js
////////////////////////////////////////////////////////////////////
// Run things
////////////////////////////////////////////////////////////////////
// For some reason popups requires a fully loaded page jQuery.ready(...) causes problems for some.
// The old addOnloadHook did something similar to the below
if (document.readyState=="complete")
autoEdit(); //will setup popups
else
$( window ).on( 'load', autoEdit );
// Support for MediaWiki's live preview, VisualEditor's saves and Echo's flyout.
( function () {
var once = true;
function dynamicContentHandler( $content ) {
// Try to detect the hook fired on initial page load and disregard
// it, we already hook to onload (possibly to different parts of
// page - it's configurable) and running twice might be bad. Ugly…
if ( $content.attr( 'id' ) == 'mw-content-text' ) {
if ( once ) {
once = false;
return;
}
}
function registerHooksForVisibleNavpops () {
for (var i=0; pg.current.links && i<pg.current.links.length; ++i) {
var navpop = pg.current.links[i].navpopup;
if (!navpop || !navpop.isVisible()) { continue; }
Navpopup.tracker.addHook(posCheckerHook(navpop));
}
}
function doIt () {
registerHooksForVisibleNavpops();
$content.each( function () {
this.ranSetupTooltipsAlready = false;
setupTooltips( this );
} );
}
setupPopups( doIt );
}
// This hook is also fired after page load.
mw.hook( 'wikipage.content' ).add( dynamicContentHandler );
mw.hook( 'ext.echo.overlay.beforeShowingOverlay' ).add( function($overlay){
dynamicContentHandler( $overlay.find(".mw-echo-state") );
});
} )();
// Forked from User:Connel MacKenzie/mess-with-popups.js
Previewmaker.prototype.firstBit = function() {
try{
if (mw.config.get("wgNamespaceNumber") !== 0){
// dont't be givin' me no subsequent paragraphs, you hear me?
/// first we "normalize" section headings, removing whitespace after, adding before
var d = this.data;
if (getValueOf('popupPreviewCutHeadings')) {
this.data = this.data.replace(RegExp('\\s*(==+[^=]*==+)\\s*', 'g'), '\n\n$1 ');
/// then we want to get rid of paragraph breaks whose text ends badly
this.data = this.data.replace(RegExp('([:;]) *\\n{2,}', 'g'), '$1\n');
this.data = this.data.replace(RegExp('^[\\s\\n]*'), '');
stuff = (RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);
if (stuff) {
d = stuff[0];
}
if (!getValueOf('popupPreviewFirstParOnly')) {
d = this.data;
}
/// now put \n\n after sections so that bullets and numbered lists work
d = d.replace(RegExp('(==+[^=]*==+)\\s*', 'g'), '$1\n\n');
}
// superfluous sentences are RIGHT OUT.
// note: exactly 1 set of parens here needed to make the slice work
d = d.parenSplit(RegExp('([!?.]+["' + "'" + ']*\\s)', 'g'));
// leading space is bad, mmkay?
d[0] = d[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds = RegExp('([^.][a-z][.][a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|no|\\[[^\\]]*|\\s[A-Zvclm])$', 'i');
d = this.fixSentenceEnds(d, notSentenceEnds);
var maxChars=getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd;
do {
dd = this.firstSentences(d, n);
--n;
}
while (dd.length > maxChars && n > 0);
this.data = dd;
} else {
//TODO: Limit this override to urls that start with http://*.wiktionary.org/
//TODO: extend the above section to also preview Wikipedia links
var charsWikt = 0;
var linesWikt = this.data.split('\n');
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[in]transitive}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{[un]countable}}/g, '');
linesWikt[jWikt] = linesWikt[jWikt].replace(/\{\{cattag\|(.*?)}}/g, "");
linesWikt[jWikt] = linesWikt[jWikt].replace(/[\{\}]/g, "'");
if (linesWikt[jWikt].search(/^[#]/) == -1) {
if (linesWikt[jWikt].search(/^==[A-Z]/) == -1) {
linesWikt[jWikt] = '';
}
}
charsWikt += linesWikt[jWikt].length;
}
if (charsWikt < 2) {
linesWikt[0] = "No properly formatted content";
}
var maxChars = getValueOf('popupMaxPreviewCharacters');
var n = getValueOf('popupMaxPreviewSentences');
var dd = '';
for (jWikt = 0; jWikt < linesWikt.length; jWikt++) {
if (linesWikt[jWikt] !== '') {
dd += linesWikt[jWikt] + '\n';
}
if (dd.length < maxChars) {
this.data = dd;
}
}
}
}
catch (e) {
console.log("error from mess with popups: ", e);
}
};
});
tgw02rj17vat4hppgvmjmwl3h8srxpi
ᠡᠷᠬᠡᠦ
0
412106
2513937
2026-08-14T04:33:59Z
WhoAlone
40420
Trang mới: “{{also|ᡝᡵᡴᡝᠣ}} =={{langname|cmg}}== ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ riêng}} # {{lb|cmg|~ {{m|cmg|ᠭᠣᠣᠯ|tr=-}}}} {{place|cmg|sông|rep/Buryatia|và|obl/Irkutsk|c/Nga}} # {{lb|cmg|~ {{m|cmg|ᠬᠣᠲᠠ|tr=-}}}} {{place|cmg|thành phố|obl/Irkutsk|c/Nga|t=Irkutsk}} ===={{ĐM|decl}}==== {{rfinfl|cmg|danh từ riêng}} ===={{ĐM|alter form}}==== * {{alt|cmg|ᠡᠷᠬᠦᠦ}} ===={{ĐM|desc}}==== * {{desc|mn|Эрхүү}} * {{desc|mnc|ᡝᡵ…”
2513937
wikitext
text/x-wiki
{{also|ᡝᡵᡴᡝᠣ}}
=={{langname|cmg}}==
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{lb|cmg|~ {{m|cmg|ᠭᠣᠣᠯ|tr=-}}}} {{place|cmg|sông|rep/Buryatia|và|obl/Irkutsk|c/Nga}}
# {{lb|cmg|~ {{m|cmg|ᠬᠣᠲᠠ|tr=-}}}} {{place|cmg|thành phố|obl/Irkutsk|c/Nga|t=Irkutsk}}
===={{ĐM|decl}}====
{{rfinfl|cmg|danh từ riêng}}
===={{ĐM|alter form}}====
* {{alt|cmg|ᠡᠷᠬᠦᠦ}}
===={{ĐM|desc}}====
* {{desc|mn|Эрхүү}}
* {{desc|mnc|ᡝᡵᡴᡝᠣ|bor=1}}
** {{desc|cmn|厄爾口|bor=1}}
p5id838ur8f8lobrjmxi7eatneyaee9
ᠡᠷᠬᠦᠦ
0
412107
2513939
2026-08-14T04:36:39Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ riêng}} # {{alt form of|cmg|ᠡᠷᠬᠡᠦ}}”
2513939
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{alt form of|cmg|ᠡᠷᠬᠡᠦ}}
ek17wdog86jc3bqaz698zrw917dmb23
ᠢᠯᠡ
0
412108
2513940
2026-08-14T04:39:48Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|etym}}=== So sánh {{ncog|och|伊列|tr=*qlil red}}, {{ncog|ltc|伊麗|tr='jij lje}}, {{ncog|xqa|الا}}, {{ncog|chg|ایله|tr=ilä}}, {{ncog|kk|ىلە}} / {{mention|kk|Іле}}, v.v. {{rfe|cmg}} ==={{ĐM|pron}}=== * {{IPA4|cmg|/i.le/}} ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ riêng}} # {{place|cmg|sông|autonomous region/Tân Cương|c/Trung Quốc|và|c/Kazakhstan|t=Ili}} #* {{quote-book|cmg|year=19th century|author=J̌imbadorǰi|…”
2513940
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|etym}}===
So sánh {{ncog|och|伊列|tr=*qlil red}}, {{ncog|ltc|伊麗|tr='jij lje}}, {{ncog|xqa|الا}}, {{ncog|chg|ایله|tr=ilä}}, {{ncog|kk|ىلە}} / {{mention|kk|Іле}}, v.v. {{rfe|cmg}}
==={{ĐM|pron}}===
* {{IPA4|cmg|/i.le/}}
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{place|cmg|sông|autonomous region/Tân Cương|c/Trung Quốc|và|c/Kazakhstan|t=Ili}}
#* {{quote-book|cmg|year=19th century|author=J̌imbadorǰi|title=[[ᠪᠣᠯᠤᠷ ᠲᠣᠯᠢ]]|trans-title=the Crystal Mirror||text=[[ᠣᠶᠢᠷᠣᠳ]] [[ᠵᠡᠭᠦᠨ ᠭᠠᠷᠤᠨ]] [[ᠨᠤᠲᠤᠭ ᠤᠨ]] [[ᠵᠡᠭᠦᠨ]] [[ᠥᠵᠢᠭᠦᠷ ᠦᠨ]] [[ᠬᠢᠲᠠᠳ ᠤᠨ]] [[ᠵᠠᠶᠠᠭᡡᠨ]] [[ᠬᠡᠮᠡᠬᠦ]] [[ᠬᠡᠷᠡᠮ ᠡᠴᠡ]] [[ᠪᠠᠷᠠᠭᠤᠨ]] [[ᠪᠢᠶᠡ ᠦ]] '''ᠢᠯᠡ'''᠂ [[ᠡᠷᠴᠢᠰ]] [[ᠬᠣᠶᠠᠷ ᠤᠨ]] [[ᠤᠰᠤᠨ ᠤ]] [[ᠤᠷᠤᠰᠬ᠌ᠠᠯ]] [[ᠳᠠᠭᠠᠵᠤ]] [[ᠪᠠᠶᠢᠳᠠᠭ]] [[ᠮᠣᠩᠭᠣᠯ]] [[ᠲᠡᠭᠦᠨ ᠢ]] ︽[[ᠵᠡᠭᠦᠨ ᠭᠠᠷ]]︾ [[ᠬᠡᠮᠡᠮᠦᠢ]]᠃}}
===={{ĐM|decl}}====
{{rfinfl|cmg|danh từ riêng}}
==={{ĐM|alter form}}===
* {{alter|cmg|ᠢᠯᠢ}}
5kwx7tj06rpex3ysf0qbk5twawp6rll
ᠢᠯᠢ
0
412109
2513941
2026-08-14T04:41:11Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ riêng}} # {{alt form of|cmg|ᠢᠯᠡ}}”
2513941
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{alt form of|cmg|ᠢᠯᠡ}}
rfuj6kpqitoidvkt9vn6ffu49fdy0is
Thành viên:Hiyuune/Gadget-PagePreviews.js
2
412110
2513943
2026-08-14T04:41:35Z
Hiyuune
50834
Hiyuune đã đổi [[Thành viên:Hiyuune/Gadget-PagePreviews.js]] thành [[Thành viên:Hiyuune/Gadget-popups.js]]
2513943
javascript
text/javascript
/* #REDIRECT */mw.loader.load("//vi.wiktionary.org/w/index.php?title=Th%C3%A0nh_vi%C3%AAn:Hiyuune/Gadget-popups.js&action=raw&ctype=text/javascript");
kfky0qxwt7r06x5ykcmqmmxq1rwdogf
ᠣᠪᠯᠠᠰᠲ᠋ᠢ
0
412111
2513944
2026-08-14T04:42:43Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|etym}}=== {{bor+|cmg|ru|о́бласть}}. ==={{ĐM|pron}}=== {{rfp|cmg}} ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # {{lb|cmg|historical}} [[oblast]] {{gloss|tỉnh có đồn trú}} #: {{uxi|bua|[[ᠪᠠᠶ᠋ᠢᠭᠠᠯ]] [[ᠤ᠋ᠨ]] [[ᠡᠮᠦᠨᠡᠬᠢ]] '''ᠣᠪᠯᠠᠰᠲ᠋ᠢ'''|Zabaikalskaya '''Oblast''' (của Đế quốc Nga)}} ===={{ĐM|decl}}==== {{rfinfl|cmg|danh từ}} ===={{ĐM|syn}}==== * {{l|cmg|ᠮᠤᠵ…”
2513944
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|etym}}===
{{bor+|cmg|ru|о́бласть}}.
==={{ĐM|pron}}===
{{rfp|cmg}}
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# {{lb|cmg|historical}} [[oblast]] {{gloss|tỉnh có đồn trú}}
#: {{uxi|bua|[[ᠪᠠᠶ᠋ᠢᠭᠠᠯ]] [[ᠤ᠋ᠨ]] [[ᠡᠮᠦᠨᠡᠬᠢ]] '''ᠣᠪᠯᠠᠰᠲ᠋ᠢ'''|Zabaikalskaya '''Oblast''' (của Đế quốc Nga)}}
===={{ĐM|decl}}====
{{rfinfl|cmg|danh từ}}
===={{ĐM|syn}}====
* {{l|cmg|ᠮᠤᠵᠢ}}
4ksylhzrud5ukl0oe0qsp61vojsyymw
ᠣᠲ᠋ᠣᠭ
0
412112
2513947
2026-08-14T04:45:47Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # {{alternative spelling of|cmg|ᠣᠲᠣᠭ}} ==={{ĐM|ref}}=== * {{cite-web|year=2016–now|work={{lang|ru|Электронная база данных архивных документов на монгольской письменности из фондов Государственного архива Республики Бурятия}}|location=Ulan-Ude|url=https://gbu-garb.ru/nashi-proektyi/elektr…”
2513947
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# {{alternative spelling of|cmg|ᠣᠲᠣᠭ}}
==={{ĐM|ref}}===
* {{cite-web|year=2016–now|work={{lang|ru|Электронная база данных архивных документов на монгольской письменности из фондов Государственного архива Республики Бурятия}}|location=Ulan-Ude|url=https://gbu-garb.ru/nashi-proektyi/elektronnaya-baza-dannyix-arxivnyix-dokumentov-na-mongolskoj-pismennosti.html}}
izet5gsblajwn0gtej80aufmh0worcg
ᠨ᠋ᠣᠶ᠋ᠠᠨ
0
412113
2513950
2026-08-14T04:47:22Z
WhoAlone
40420
Trang mới: “{{also|ᠨᠣᠶ᠋ᠠᠨ|ᠨᠣᠶᠠᠨ}} =={{langname|cmg}}== ==={{ĐM|etym}}=== {{inh+|cmg|xng|ᠨ᠋ᠣᠶ᠋ᠠᠨ|t=lãnh chúa}}, có lẽ từ {{der|cmg|cmn-ear|老爺|t=chủ nhân}}. ==={{ĐM|pron}}=== * {{IPA4|cmg|/nɔ.jän/}} ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # {{alternative spelling of|cmg|ᠨᠣᠶ᠋ᠠᠨ}} ===={{ĐM|decl}}==== {{rfinfl|cmg|danh từ}} ===={{ĐM|desc}}==== * {{desc|mn|ᠨᠣᠶᠠᠨ}} *: {{desc|mn|ноён|sclang=1}}”
2513950
wikitext
text/x-wiki
{{also|ᠨᠣᠶ᠋ᠠᠨ|ᠨᠣᠶᠠᠨ}}
=={{langname|cmg}}==
==={{ĐM|etym}}===
{{inh+|cmg|xng|ᠨ᠋ᠣᠶ᠋ᠠᠨ|t=lãnh chúa}}, có lẽ từ {{der|cmg|cmn-ear|老爺|t=chủ nhân}}.
==={{ĐM|pron}}===
* {{IPA4|cmg|/nɔ.jän/}}
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# {{alternative spelling of|cmg|ᠨᠣᠶ᠋ᠠᠨ}}
===={{ĐM|decl}}====
{{rfinfl|cmg|danh từ}}
===={{ĐM|desc}}====
* {{desc|mn|ᠨᠣᠶᠠᠨ}}
*: {{desc|mn|ноён|sclang=1}}
srtzns3lqrquxmad676rb4swqa999ge
ᠨᠠᠷᠠᠨ
0
412114
2513953
2026-08-14T04:50:56Z
WhoAlone
40420
Trang mới: “=={{langname|mn}}== {{mn-variant|ᠨᠠᠷᠠᠨ|наран}} ==={{ĐM|pron}}=== {{rfp|mn}} ==={{ĐM|noun}}=== {{mn-noun-form}} # {{infl of|mn|ᠨᠠᠷᠠ|t=mặt trời||obl}}”
2513953
wikitext
text/x-wiki
=={{langname|mn}}==
{{mn-variant|ᠨᠠᠷᠠᠨ|наран}}
==={{ĐM|pron}}===
{{rfp|mn}}
==={{ĐM|noun}}===
{{mn-noun-form}}
# {{infl of|mn|ᠨᠠᠷᠠ|t=mặt trời||obl}}
1wtc4urdaktt9vn21hsogy6qfyu5i97
2513954
2513953
2026-08-14T04:54:40Z
WhoAlone
40420
2513954
wikitext
text/x-wiki
=={{langname|mn}}==
{{mn-variant|ᠨᠠᠷᠠᠨ|наран}}
==={{ĐM|pron}}===
{{rfp|mn}}
==={{ĐM|noun}}===
{{mn-noun-form}}
# {{infl of|mn|ᠨᠠᠷᠠ|t=mặt trời||obl}}
=={{langname|xng}}==
{{xng-alt|ᠨᠠᠷᠠᠨ|[[ꡋ ꡘꡋ|ꡋ ꡘꡋ]]|納<small>舌</small>闌}}
==={{ĐM|alter form}}===
* {{alt|xng|ᠨ᠋ᠠᠷᠠᠨ}}
* {{alt|xng|ناران||Muqaddimat al-Adab}}
==={{ĐM|etym}}===
{{inh+|xng|xgn-pro|*naran}}.
==={{ĐM|noun}}===
{{head|xng|Danh từ}} {{attn|xng|FVS/VS needed for final n}}
# [[Mặt Trời]].
===={{ĐM|decl}}====
{{rfinfl|xng|danh từ}}
===={{ĐM|desc}}====
* {{desc|xgn-cen}}
** {{desc|cmg|ᠨᠠᠷᠠᠨ}}
*** {{desc|ykh|нара<tr:nara>|нару<tr:naru>}}
*** {{desc|mn|-}}
***: {{desc|mn|ᠨᠠᠷᠠ|sclang=1}}
***: {{desc|mn|нар|sclang=1}}
** Oirat
*** {{desc|bua|наран}}
*** {{desc|xwo|ᠨᠠᠷᠠᠨ}}
**** {{desc|xal|нарн}}
293bq8nl6k8ppqif5g2so1a7pf8w8z8
2513955
2513954
2026-08-14T04:57:26Z
WhoAlone
40420
2513955
wikitext
text/x-wiki
=={{langname|mn}}==
{{mn-variant|ᠨᠠᠷᠠᠨ|наран}}
==={{ĐM|pron}}===
{{rfp|mn}}
==={{ĐM|noun}}===
{{mn-noun-form}}
# {{infl of|mn|ᠨᠠᠷᠠ|t=mặt trời||obl}}
=={{langname|cmg}}==
==={{ĐM|etym}}===
{{inh+|cmg|xng|ᠨᠠᠷᠠᠨ}}, từ {{inh|cmg|xgn-pro|*naran}}.
==={{ĐM|pron}}===
* {{IPA4|cmg|/nä.rän/}}
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# [[Mặt Trời]].
===={{ĐM|decl}}====
{{rfinfl|cmg|noun}}
===={{ĐM|desc}}====
* {{desc|ykh|нара<tr:nara>|наран<tr:naran>|нару<tr:naru>}}
* {{desc|mn|ᠨᠠᠷᠠ}}
*: {{desc|mn|нар|sclang=1}}
=={{langname|xng}}==
{{xng-alt|ᠨᠠᠷᠠᠨ|[[ꡋ ꡘꡋ|ꡋ ꡘꡋ]]|納<small>舌</small>闌}}
==={{ĐM|alter form}}===
* {{alt|xng|ᠨ᠋ᠠᠷᠠᠨ}}
* {{alt|xng|ناران||Muqaddimat al-Adab}}
==={{ĐM|etym}}===
{{inh+|xng|xgn-pro|*naran}}.
==={{ĐM|noun}}===
{{head|xng|Danh từ}} {{attn|xng|FVS/VS needed for final n}}
# [[Mặt Trời]].
===={{ĐM|decl}}====
{{rfinfl|xng|danh từ}}
===={{ĐM|desc}}====
* {{desc|xgn-cen}}
** {{desc|cmg|ᠨᠠᠷᠠᠨ}}
*** {{desc|ykh|нара<tr:nara>|нару<tr:naru>}}
*** {{desc|mn|-}}
***: {{desc|mn|ᠨᠠᠷᠠ|sclang=1}}
***: {{desc|mn|нар|sclang=1}}
** Oirat
*** {{desc|bua|наран}}
*** {{desc|xwo|ᠨᠠᠷᠠᠨ}}
**** {{desc|xal|нарн}}
8hrpb7uho94z812zjdu3hjbg55a0jgd
Thể loại:tuw-kkl:Lớp Lưỡng cư
14
412115
2513957
2026-08-14T05:03:22Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513957
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:tuw-kkl:Phân ngành Có xương sống
14
412116
2513958
2026-08-14T05:03:46Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513958
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:tuw-kkl:Danh sách chủ đề thuộc nhóm tập hợp
14
412117
2513959
2026-08-14T05:03:48Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513959
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:tuw-kkl:Tất cả chủ đề
14
412118
2513960
2026-08-14T05:03:57Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513960
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:tuw-kkl:Ngành Dây sống
14
412119
2513961
2026-08-14T05:03:59Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513961
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:tuw-kkl:Động vật
14
412120
2513962
2026-08-14T05:04:06Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513962
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:tuw-kkl:Sinh vật
14
412121
2513963
2026-08-14T05:04:15Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513963
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:tuw-kkl:Sự sống
14
412122
2513964
2026-08-14T05:04:22Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513964
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:tuw-kkl:Danh sách chủ đề thuộc nhóm liên quan
14
412123
2513965
2026-08-14T05:04:31Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513965
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:tuw-kkl:Tự nhiên
14
412124
2513966
2026-08-14T05:04:32Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513966
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:tuw-kkl:Hôn nhân
14
412125
2513968
2026-08-14T05:07:37Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513968
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:tuw-kkl:Mọi người
14
412126
2513969
2026-08-14T05:07:39Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513969
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:tuw-kkl:Con người
14
412127
2513970
2026-08-14T05:08:26Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513970
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:tuw-kkl:Văn hóa
14
412128
2513971
2026-08-14T05:08:28Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513971
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:tuw-kkl:Gia đình
14
412129
2513972
2026-08-14T05:08:29Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513972
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:tuw-kkl:Xã hội
14
412130
2513973
2026-08-14T05:08:38Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513973
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:tuw-kkl:Họ Chó
14
412131
2513976
2026-08-14T05:09:57Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513976
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:tuw-kkl:Bộ Ăn thịt
14
412132
2513977
2026-08-14T05:10:03Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513977
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
Thể loại:tuw-kkl:Lớp Thú
14
412133
2513978
2026-08-14T05:10:11Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2513978
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
父子有親
0
412134
2513985
2026-08-14T08:25:44Z
Hiyuune
50834
Trang mới: “=={{langname|ko}}== {{ko-hanjatab}} ==={{section|n}}=== {{ko-noun|hangeul=부자유친}} # {{hanja form of|부자유친}}”
2513985
wikitext
text/x-wiki
=={{langname|ko}}==
{{ko-hanjatab}}
==={{section|n}}===
{{ko-noun|hangeul=부자유친}}
# {{hanja form of|부자유친}}
c8zhugsg9tlv1d4wi67k1kbn8xnkmks
ᠨᠢᠲ᠋ᠤᠭ
0
412135
2513988
2026-08-14T08:31:00Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # {{alternative spelling of|cmg|ᠨᠢᠲᠤᠭ}} ==={{ĐM|ref}}=== * {{cite-web|year=2016–now|work={{lang|ru|Электронная база данных архивных документов на монгольской письменности из фондов Государственного архива Республики Бурятия}}|location=Ulan-Ude|url=https://gbu-garb.ru/nashi-proektyi/ele…”
2513988
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# {{alternative spelling of|cmg|ᠨᠢᠲᠤᠭ}}
==={{ĐM|ref}}===
* {{cite-web|year=2016–now|work={{lang|ru|Электронная база данных архивных документов на монгольской письменности из фондов Государственного архива Республики Бурятия}}|location=Ulan-Ude|url=https://gbu-garb.ru/nashi-proektyi/elektronnaya-baza-dannyix-arxivnyix-dokumentov-na-mongolskoj-pismennosti.html}}
akcz2ank07kdke5w1vn2wfop4b6iwgc
ᠪᠯᠠᠮ ᠠ
0
412136
2513989
2026-08-14T08:39:05Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|etym}}=== {{bor+|cmg|xct|བླ་མ}}. ==={{ĐM|noun}}=== {{head|cmg|Danh từ|head=ᠪᠯᠠᠮᠠ}} # {{lb|cmg|religion}} {{alt form of|cmg|ᠯᠠᠮᠠ|t=lạt ma}} ===={{ĐM|decl}}==== {{rfinfl|cmg|danh từ}}”
2513989
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|etym}}===
{{bor+|cmg|xct|བླ་མ}}.
==={{ĐM|noun}}===
{{head|cmg|Danh từ|head=ᠪᠯᠠᠮᠠ}}
# {{lb|cmg|religion}} {{alt form of|cmg|ᠯᠠᠮᠠ|t=lạt ma}}
===={{ĐM|decl}}====
{{rfinfl|cmg|danh từ}}
82oggguugs9hclq90v4tz9t1x8n574r
ᠯᠠᠮ ᠠ
0
412137
2513990
2026-08-14T08:41:19Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # [[lạt ma]].”
2513990
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# [[lạt ma]].
20yojdkd39fps2r7kyjitv8wpgq3hmf
ᠬ᠋ᠠᠲ᠋ᠠᠭᠢᠨ
0
412138
2513991
2026-08-14T08:43:54Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # {{alternative spelling of|cmg|ᠬᠠᠲᠠᠭᠢᠨ}} ==={{ĐM|ref}}=== * {{cite-web|year=2016–now|work={{lang|ru|Электронная база данных архивных документов на монгольской письменности из фондов Государственного архива Республики Бурятия}}|location=Ulan-Ude|url=https://gbu-garb.ru/nashi-proekt…”
2513991
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# {{alternative spelling of|cmg|ᠬᠠᠲᠠᠭᠢᠨ}}
==={{ĐM|ref}}===
* {{cite-web|year=2016–now|work={{lang|ru|Электронная база данных архивных документов на монгольской письменности из фондов Государственного архива Республики Бурятия}}|location=Ulan-Ude|url=https://gbu-garb.ru/nashi-proektyi/elektronnaya-baza-dannyix-arxivnyix-dokumentov-na-mongolskoj-pismennosti.html}}
25enpwjze790inq471iew9w4z6ng18e
ᠬᠠᠭ᠋ᠠᠨ
0
412139
2513992
2026-08-14T08:47:27Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # {{alt form of|cmg|ᠬᠠᠭᠠᠨ|t=hãn|nocat=1}} #: {{ux|cmg|[[ᠬᠠᠯᠬᠠ ᠶᠢᠨ]] [[ᠸᠠᠴᠢᠷᠠᠢ]] [[ᠪᠠᠲᠤ]] [[ᠲᠦᠰᠢᠶᠡ ᠲ᠋ᠦ]] '''ᠬᠠᠭᠠᠨ''' ᠤ [[ᠲᠠᠮᠠᠭ᠋ᠠ]]᠉|tr=qalq-a-yin wačirai batu tüsiy-ä-tü '''qaɣan'''-u tamaɣ-a.}}”
2513992
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# {{alt form of|cmg|ᠬᠠᠭᠠᠨ|t=hãn|nocat=1}}
#: {{ux|cmg|[[ᠬᠠᠯᠬᠠ ᠶᠢᠨ]] [[ᠸᠠᠴᠢᠷᠠᠢ]] [[ᠪᠠᠲᠤ]] [[ᠲᠦᠰᠢᠶᠡ ᠲ᠋ᠦ]] '''ᠬᠠᠭᠠᠨ''' ᠤ [[ᠲᠠᠮᠠᠭ᠋ᠠ]]᠉|tr=qalq-a-yin wačirai batu tüsiy-ä-tü '''qaɣan'''-u tamaɣ-a.}}
a22b5xbzh3eeoop63me68d0hqt2ck2o
ᠬᠠᠭ᠍ᠠᠨ
0
412140
2513993
2026-08-14T08:51:38Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # {{alt form of|cmg|ᠬᠠᠭᠠᠨ|t=hãn|nocat=1}}”
2513993
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# {{alt form of|cmg|ᠬᠠᠭᠠᠨ|t=hãn|nocat=1}}
c9jdkk2yehfze34to4fb1a8cudqv7md
ᠬᠠᠯᠪᠢᠨ
0
412141
2513994
2026-08-14T08:53:59Z
WhoAlone
40420
Trang mới: “{{also|ᡥᠠᠯᠪᡳᠨ}} =={{langname|cmg}}== ==={{ĐM|etym}}=== So sánh {{ncog|bua|хальбан}}. {{rfe|cmg}} ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # Khalban {{gloss|một gia tộc thuộc nhóm Khori của người Buryat}} ===={{ĐM|decl}}==== {{rfinfl|cmg|danh từ}}”
2513994
wikitext
text/x-wiki
{{also|ᡥᠠᠯᠪᡳᠨ}}
=={{langname|cmg}}==
==={{ĐM|etym}}===
So sánh {{ncog|bua|хальбан}}. {{rfe|cmg}}
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# Khalban {{gloss|một gia tộc thuộc nhóm Khori của người Buryat}}
===={{ĐM|decl}}====
{{rfinfl|cmg|danh từ}}
51bzj16ud8se65n9nd4pd2miiebq7ch
ᠬᠢᠲᠠᠳ
0
412142
2513995
2026-08-14T09:00:01Z
WhoAlone
40420
Trang mới: “=={{langname|mn}}== ==={{ĐM|etym}}=== {{inh+|mn|cmg|ᠬᠢᠲᠠᠳ|t=Trung Quốc}}, từ {{inh|mn|xng|乞塔惕|乞塔<sup>惕</sup>|t=[[Khiết Đan]]}}. So sánh {{w|đại tự Khiết Đan}} {{mention|zkt|}}, {{w|tiểu tự Khiết Đan}} {{mention|zkt|^𘱿𘲫}}; {{ncog|otk|𐰶𐰃𐱃𐰪|tr=Qïtań}}, và {{ncog|oui|𐽲𐽶𐾀𐽰𐽶|ts=qïtay}}, {{mention|oui|𐽲𐾀𐽰𐽶|ts=qïtay}}, {{m|oui|𐽲𐽶𐾀𐽶|ts=qïtay}}, cùng với {{ncog|ru|Ки…”
2513995
wikitext
text/x-wiki
=={{langname|mn}}==
==={{ĐM|etym}}===
{{inh+|mn|cmg|ᠬᠢᠲᠠᠳ|t=Trung Quốc}}, từ {{inh|mn|xng|乞塔惕|乞塔<sup>惕</sup>|t=[[Khiết Đan]]}}.
So sánh {{w|đại tự Khiết Đan}} {{mention|zkt|}}, {{w|tiểu tự Khiết Đan}} {{mention|zkt|^𘱿𘲫}}; {{ncog|otk|𐰶𐰃𐱃𐰪|tr=Qïtań}}, và {{ncog|oui|𐽲𐽶𐾀𐽰𐽶|ts=qïtay}}, {{mention|oui|𐽲𐾀𐽰𐽶|ts=qïtay}}, {{m|oui|𐽲𐽶𐾀𐽶|ts=qïtay}}, cùng với {{ncog|ru|Кита́й|t=Trung Quốc}} và {{ncog|en|Cathay}}.
==={{ĐM|pron}}===
{{mn-IPA-see|Хятад}}
==={{ĐM|pr-noun}}===
{{mn-variant|ᠬᠢᠲᠠᠳ|Хятад}}
{{mn-proper noun|Хятад}}
# {{lb|mn|Mongolia}} {{place|mn|khu vực/và/quốc gia|r/Đông Á|t1=Trung Quốc}}, [[Cathay]]
#: {{syn|mn|ᠳᠤᠮᠳᠠᠳᠤ ᠤᠯᠤᠰ|q1=Trung Quốc}}
# {{lb|mn|historical}} {{given name|mn|nam}}
==={{ĐM|noun}}===
{{mn-variant|ᠬᠢᠲᠠᠳ|хятад}}
{{mn-noun|хятад}}
# {{w|Người Hán}}.
#: {{uxi|mn|'''ᠬᠢᠲᠠᠳ''' [[ᠬᠡᠯᠡ]]|t=tiếng Trung Quốc}}
#: {{uxi|mn|'''ᠬᠢᠲᠠᠳ''' [[ᠬᠦᠮᠦᠨ]]|t=người Trung Quốc}}
#: {{uxi|mn|'''ᠬᠢᠲᠠᠳ''' [[ᠴᠠᠭᠠᠰᠤ]]|t=giấy Trung Hoa}}
#: {{uxi|mn|'''ᠬᠢᠲᠠᠳ''' [[ᠬᠠᠨᠵᠢ]]|t=chữ Hán}}
===={{ĐM|decl}}====
{{Mong-decl|ᠬᠢᠲᠠᠳ|a|d|pl=[[ᠬᠢᠲᠠᠳ]] [[ᠤ᠋ᠳ]]|d}}
kv4wcetx13bw8d0e3oeezwuvvb4gsxk
2513996
2513995
2026-08-14T09:05:32Z
WhoAlone
40420
2513996
wikitext
text/x-wiki
=={{langname|mn}}==
==={{ĐM|etym}}===
{{inh+|mn|cmg|ᠬᠢᠲᠠᠳ|t=Trung Quốc}}, từ {{inh|mn|xng|乞塔惕|乞塔<sup>惕</sup>|t=[[Khiết Đan]]}}.
So sánh {{w|đại tự Khiết Đan}} {{mention|zkt|}}, {{w|tiểu tự Khiết Đan}} {{mention|zkt|^𘱿𘲫}}; {{ncog|otk|𐰶𐰃𐱃𐰪|tr=Qïtań}}, và {{ncog|oui|𐽲𐽶𐾀𐽰𐽶|ts=qïtay}}, {{mention|oui|𐽲𐾀𐽰𐽶|ts=qïtay}}, {{m|oui|𐽲𐽶𐾀𐽶|ts=qïtay}}, cùng với {{ncog|ru|Кита́й|t=Trung Quốc}} và {{ncog|en|Cathay}}.
==={{ĐM|pron}}===
{{mn-IPA|Хятад}}
==={{ĐM|pr-noun}}===
{{mn-variant|ᠬᠢᠲᠠᠳ|Хятад}}
{{mn-proper noun|Хятад}}
# {{lb|mn|Mông Cổ}} {{place|mn|khu vực/và/quốc gia|r/Đông Á|t1=Trung Quốc}}, [[Cathay]]
#: {{syn|mn|ᠳᠤᠮᠳᠠᠳᠤ ᠤᠯᠤᠰ|q1=Trung Quốc}}
# {{lb|mn|historical}} {{given name|mn|nam}}
==={{ĐM|noun}}===
{{mn-variant|ᠬᠢᠲᠠᠳ|хятад}}
{{mn-noun|хятад}}
# {{w|Người Hán}}.
#: {{uxi|mn|'''ᠬᠢᠲᠠᠳ''' [[ᠬᠡᠯᠡ]]|t=tiếng Trung Quốc}}
#: {{uxi|mn|'''ᠬᠢᠲᠠᠳ''' [[ᠬᠦᠮᠦᠨ]]|t=người Trung Quốc}}
#: {{uxi|mn|'''ᠬᠢᠲᠠᠳ''' [[ᠴᠠᠭᠠᠰᠤ]]|t=giấy Trung Hoa}}
#: {{uxi|mn|'''ᠬᠢᠲᠠᠳ''' [[ᠬᠠᠨᠵᠢ]]|t=chữ Hán}}
===={{ĐM|decl}}====
{{Mong-decl|ᠬᠢᠲᠠᠳ|a|d|pl=[[ᠬᠢᠲᠠᠳ]] [[ᠤ᠋ᠳ]]|d}}
=={{langname|cmg}}==
==={{ĐM|alter form}}===
* {{alt|cmg|ᠬᠢᠲ᠋ᠠᠳ}}
==={{ĐM|etym}}===
{{inh+|cmg|xng||[[乞]][[塔]]<sup>[[惕]]</sup>|tr=kitad|t=[[Khiết Đan]]}}.
==={{ĐM|pron}}===
* {{IPA4|cmg|/kʰi.tʰät/}}
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# {{w|Người Hán}}.
===={{ĐM|decl}}====
{{rfinfl|cmg|danh từ}}
===={{ĐM|desc}}====
* {{desc|mn|ᠬᠢᠲᠠᠳ}}
*: {{desc|mn|хятад|sclang=1}}
* {{desc|bua|хитад|bor=1}}
* {{desc|sah|кытат|bor=1}}
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# [[Trung Quốc]], {{w|nhà Minh}}
# {{given name|cmg|nam}}
===={{ĐM|decl}}====
{{rfinfl|cmg|proper noun}}
===={{ĐM|desc}}====
* {{desc|mn|ᠬᠢᠲᠠᠳ}}
*: {{desc|mn|Хятад|sclang=1}}
* {{desc|bua|Хитад|bor=1}}
8s9rfxuub0ifs5hzdx7eddwj3868g3b
ᠬᠢᠲ᠋ᠠᠳ
0
412143
2513997
2026-08-14T09:06:29Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # {{alt form of|cmg|ᠬᠢᠲᠠᠳ}}”
2513997
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# {{alt form of|cmg|ᠬᠢᠲᠠᠳ}}
kpm3iq3owtw10vzyuw1imysqc0kvfmm
ᠬᠤᠸᠠᠩᠳᠠᠢ
0
412144
2513998
2026-08-14T09:08:24Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}} ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # {{lb|cmg|Buryat}} {{altform|cmg|ᠬᠤᠸᠠᠩᠳᠢ|t=hoàng đế}}”
2513998
wikitext
text/x-wiki
=={{langname|cmg}}
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# {{lb|cmg|Buryat}} {{altform|cmg|ᠬᠤᠸᠠᠩᠳᠢ|t=hoàng đế}}
6ok60wuoa157md6eimhlofsy37rmi5l
2513999
2513998
2026-08-14T09:11:34Z
WhoAlone
40420
2513999
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# {{lb|cmg|Buryat}} {{altform|cmg|ᠬᠤᠸᠠᠩᠳᠢ|t=hoàng đế}}
mxshbqqwoz8u3cybqfkpuxe9fm2cv3f
ᠮᠢᠨ᠌ᠤ
0
412145
2514000
2026-08-14T09:16:38Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|determiner}}=== {{head|cmg|Từ hạn định}} # {{alternative spelling of|cmg|ᠮᠢᠨᠤ|t=của tôi}}”
2514000
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|determiner}}===
{{head|cmg|Từ hạn định}}
# {{alternative spelling of|cmg|ᠮᠢᠨᠤ|t=của tôi}}
23lambe3ribw6zx17dcr4a6kin8726c
Thể loại:Từ hạn định tiếng Mông Cổ cổ điển
14
412146
2514001
2026-08-14T09:17:38Z
WhoAlone
40420
Trang mới: “{{auto cat}}”
2514001
wikitext
text/x-wiki
{{auto cat}}
eomzlm5v4j7ond1phrju7cnue91g5qx
ᠮᠤᠬᠤᠷ ᠰᠢᠪᠡᠷ
0
412147
2514002
2026-08-14T09:19:08Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|alter form}}=== * {{alt|cmg|ᠮᠤᠬᠤᠷᠰᠢᠪᠡᠷ}} ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ riêng}} # {{place|cmg|sông|rep/Buryatia|c/Nga|t=Mukhorshibirka}} # {{place|cmg|làng|rep/Buryatia|c/Nga|t=Mukhorshibir}} ===={{ĐM|drv}}==== {{col|cmg|ᠮᠤᠬᠤᠷ ᠰᠢᠪᠡᠷ ᠦᠨ ᠠᠢᠮᠠᠭ}} ===={{ĐM|desc}}==== * {{desc|ru|Мухоршиби́рь|bor=1}} ** {{desc|ru|Мухоршиби́рка|der=1}}”
2514002
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|alter form}}===
* {{alt|cmg|ᠮᠤᠬᠤᠷᠰᠢᠪᠡᠷ}}
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{place|cmg|sông|rep/Buryatia|c/Nga|t=Mukhorshibirka}}
# {{place|cmg|làng|rep/Buryatia|c/Nga|t=Mukhorshibir}}
===={{ĐM|drv}}====
{{col|cmg|ᠮᠤᠬᠤᠷ ᠰᠢᠪᠡᠷ ᠦᠨ ᠠᠢᠮᠠᠭ}}
===={{ĐM|desc}}====
* {{desc|ru|Мухоршиби́рь|bor=1}}
** {{desc|ru|Мухоршиби́рка|der=1}}
9x2wdr7yo53jf5hap0yy7ooyqd65bd0
2514003
2514002
2026-08-14T09:20:38Z
WhoAlone
40420
2514003
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|alter form}}===
* {{alt|cmg|ᠮᠤᠬᠤᠷᠰᠢᠪᠡᠷ}}
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{place|cmg|sông|rep/Buryatia|c/Nga|t=Mukhorshibirka}}
# {{place|cmg|làng|rep/Buryatia|c/Nga|t=Mukhorshibir}}
===={{ĐM|drv}}====
{{col|cmg|ᠮᠤᠬᠤᠷ ᠰᠢᠪᠡᠷ ᠦᠨ ᠠᠢᠮᠠᠭ}}
===={{ĐM|desc}}====
* {{desc|bua|Мухар Шэбэр}}
* {{desc|ru|Мухоршиби́рь|bor=1}}
** {{desc|ru|Мухоршиби́рка|der=1}}
9yr2qbib8xxozuhrix038vuhg2ksiyp
ᠮᠤᠬᠤᠷᠰᠢᠪᠡᠷ
0
412148
2514004
2026-08-14T09:21:10Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ riêng}} # {{altform|cmg|ᠮᠤᠬᠤᠷ ᠰᠢᠪᠡᠷ}}”
2514004
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{altform|cmg|ᠮᠤᠬᠤᠷ ᠰᠢᠪᠡᠷ}}
st650uxnh2ctgf8bd8bzglhho5jd1p1
ᡥᠣᠪᠣ
0
412149
2514013
2026-08-14T09:47:23Z
Kelly zhrm
58416
Trang mới: “=={{langname|mnc}}== ==={{ĐM|pron}}=== {{mnc-IPA}} ==={{ĐM|n}}=== {{head|mnc|Danh từ}} # [[quan tài|Quan tài]], [[hòm]].”
2514013
wikitext
text/x-wiki
=={{langname|mnc}}==
==={{ĐM|pron}}===
{{mnc-IPA}}
==={{ĐM|n}}===
{{head|mnc|Danh từ}}
# [[quan tài|Quan tài]], [[hòm]].
esjxjy8uypa8mcs7z8cjtvpxj3m9yqf
ᠮᠻᠠᠨᠪᠤᠸᠠ
0
412150
2514014
2026-08-14T10:50:24Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|etym}}=== {{bor+|cmg|xct|མཁན་པོ}}. ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # {{lb|cmg|Tibetan Buddhism}} {{alt form of|cmg|ᠻᠠᠮᠪᠤ|t=khenpo}}”
2514014
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|etym}}===
{{bor+|cmg|xct|མཁན་པོ}}.
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# {{lb|cmg|Tibetan Buddhism}} {{alt form of|cmg|ᠻᠠᠮᠪᠤ|t=khenpo}}
ju20ufvabzi1pbiywgwnzxvq59k1xwm
ᠻᠠᠮᠪᠤ
0
412151
2514015
2026-08-14T10:59:25Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # [[khenpo]].”
2514015
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# [[khenpo]].
87jz1sifvov58k9lsq37evfnw6clqb9
похвалата
0
412152
2514016
2026-08-14T11:02:24Z
Hiyuune
50834
/* */ + bg
2514016
wikitext
text/x-wiki
=={{langname|bg}}==
==={{section|pron}}===
{{bg-pr|похва́лата}}
==={{section|n}}===
{{head|bg|Biến thể hình thái danh từ|head=похва́лата}}
# {{infl of|bg|похва́ла||def|s}}
s3ttib6to9jxk8f13o64sfpd2zyyde5
ᠯᠣᠪᠽᠠᠩ
0
412153
2514017
2026-08-14T11:03:59Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ riêng}} # {{alt form of|cmg|ᠯᠣᠪᠰᠠᠩ}}”
2514017
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{alt form of|cmg|ᠯᠣᠪᠰᠠᠩ}}
oxvhz9zl49493y6amaz53roxv6wq2k0
ᠯᠠᠰᠠ
0
412154
2514018
2026-08-14T11:16:39Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|noun}}=== {{head|cmg|Danh từ}} # {{altform|cmg|ᡀᠠᠰᠠ}}”
2514018
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|noun}}===
{{head|cmg|Danh từ}}
# {{altform|cmg|ᡀᠠᠰᠠ}}
3i2jupzg09yvkbgv167djw73s7vodbb
Mabana
0
412155
2514019
2026-08-14T11:28:12Z
Hiyuune
50834
/* */
2514019
wikitext
text/x-wiki
=={{langname|en}}==
{{wp|en:}}
==={{section|pr-noun}}===
{{en-prop}}
# {{place|en|thị trấn|p/Annobón|c/Guinea Xích Đạo}}.
psb95d9kytg8pzqb37vo5vn8c1krkxv
ᠰᠡᠯᠡᠩᠭᠡ
0
412156
2514020
2026-08-14T11:48:01Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|etym}}=== {{inh+|cmg|xng|薛鿌格|ts=säläŋgä}}. ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ riêng}} # {{tcl|cmg|Selenga}} #* {{quote-book|cmg|year=1675|title=Thư của Tüsiyetü Khan Čaqundorji gửi Sa hoàng Nga|text=[[ᠲᠡᠳᠡ ᠡᠴᠡ|ᠲᠡᠳᠡᠴᠡ]] [[ᠡᠷᠬᠡᠦ ᠶᠢᠨ ᠪᠠᠢᠰᠢᠩ|ᠡᠷᠬᠡᠦ ᠭᠢ᠊ ᠶᠢᠨ ᠪᠠᠢᠰᠢᠭ᠌]] '''ᠰᠡᠯᠡᠩᠭᠡ ᠶᠢᠨ ᠪᠠᠢᠰᠢ…”
2514020
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|etym}}===
{{inh+|cmg|xng|薛鿌格|ts=säläŋgä}}.
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{tcl|cmg|Selenga}}
#* {{quote-book|cmg|year=1675|title=Thư của Tüsiyetü Khan Čaqundorji gửi Sa hoàng Nga|text=[[ᠲᠡᠳᠡ ᠡᠴᠡ|ᠲᠡᠳᠡᠴᠡ]] [[ᠡᠷᠬᠡᠦ ᠶᠢᠨ ᠪᠠᠢᠰᠢᠩ|ᠡᠷᠬᠡᠦ ᠭᠢ᠊ ᠶᠢᠨ ᠪᠠᠢᠰᠢᠭ᠌]] '''[[ᠰᠡᠯᠡᠩᠭᠡ ᠶᠢᠨ ᠪᠠᠢᠰᠢᠩ ᠳ᠋ᠤ]]''' [[ᠨᠢᠭᠡᠨ|ᠨ᠋ᠢᠭᠡᠨ]] [[ᠰᠠᠢᠨ]] [[ᠬᠦᠮᠦᠨ ᠢ]] [[ᠢᠯᠡᠭᠡ]]᠃|t=Từ đó, hãy cử một người giỏi đến '''Irkutsk''' và '''Selenginsk'''.}}
#* {{quote-book|cmg|year=1675|title=Thư của Tüsiyetü Khan Čaqundorji gửi Sa hoàng Nga|text=[[ᠬᠠᠷᠢᠶ᠋ᠠᠳ ᠢ]] [[ᠮᠠᠨ ᠢ]] [[ᠡᠰᠡ]] [[ᠥᠭᠭᠦᠭᠡᠳ]]᠃ [[ᠡᠨᠡ|ᠡᠨ᠌ᠡ]] [[ᠱᠣᠭᠯᠠᠭᠰᠠᠨ ᠢ]] [[ᠵᠥᠪ ᠪᠤᠷᠤᠭᠤ ᠶᠢ|ᠵᠥᠪ᠋ ᠪᠤᠷᠤᠭ᠍ᠤ ᠭᠢ᠊]] [[ᠡᠰᠡ]] [[ᠣᠯᠪᠠᠰᠤ]]᠂ '''[[ᠰᠡᠯᠡᠩᠭᠡ ᠶᠢᠨ ᠪᠠᠢᠰᠢᠩ|ᠰᠡᠯᠡᠩᠭᠡ]]''' [[ᠡᠷᠬᠡᠦ ᠶᠢᠨ ᠪᠠᠢᠰᠢᠩ|ᠡᠷᠬᠡᠦ]] [[ᠡᠷᠭᠡᠩ ᠦᠨ ᠪᠠᠢᠰᠢᠩ|ᠡᠷᠭᠡ᠋ᠩ ᠶᠢᠨ ᠪᠠᠢᠰᠢᠩ]] [[ᠰᠠᠭᠤᠵᠤ|ᠰᠠᠭ᠍ᠤᠵᠢ]] [[ᠪᠣᠯᠬᠤ]] [[ᠦᠭᠡᠢ]]᠃}}
===={{ĐM|alter form}}====
* {{alter|cmg|ᠰᠡᠯᠢᠩᠭᠡ|ᠴᠡᠯᠢᠩᠭᠡ||Buryat}}
===={{ĐM|desc}}====
* {{desc|mn|-|inh=1}}
*: {{desc|mn|ᠰᠡᠯᠡᠩᠭᠡ|sclang=1}}
*: {{desc|mn|Сэлэнгэ|sclang=1}}
* {{desc|mnc|ᠰᡝᠯᡝᠩᡤᡝ|bor=1}}
** {{desc|zh|色楞額|色楞格|qq2=via Mandarin}}
* {{desc|zle-mru|Селенга|bor=1}}
** {{desc|ru|Селенга́|g=f-in|inh=1}}
*** {{desc|en|Selenga|translit=1}}
5xx1q21gwda5nkhebnxyhg0hg8tk0ey
ᠴᠡᠯᠢᠩᠭᠡ
0
412157
2514021
2026-08-14T11:50:18Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ riêng}} # {{label|cmg|Buryat}} {{alt form|cmg|ᠰᠡᠯᠡᠩᠭᠡ|t=Selenga}}”
2514021
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{label|cmg|Buryat}} {{alt form|cmg|ᠰᠡᠯᠡᠩᠭᠡ|t=Selenga}}
adn5g4bxbl7y7lkto14i0hvtkgus33x
ᠰᠡᠯᠢᠩᠭᠡ
0
412158
2514022
2026-08-14T11:50:59Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ riêng}} # {{alt form|cmg|ᠰᠡᠯᠡᠩᠭᠡ|t=Selenga}}”
2514022
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{alt form|cmg|ᠰᠡᠯᠡᠩᠭᠡ|t=Selenga}}
s8as146j2eubdakeeezrniotx9qujdb
ᠰᠢᠢᠯᠬᠡ
0
412159
2514023
2026-08-14T11:54:09Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|etym}}=== Dạng viết của {{ncog|bua|Шиилхэ}}. ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ riêng}} # {{place|cmg|sông|t=Shilka}} #* {{quote-book|cmg|year=1875|author=Vandan Yumsunov|title=ᠬᠣᠷᠢ ᠶᠢᠨ ᠠᠷᠪᠠᠨ ᠨᠢᠭᠡᠨ ᠡᠴᠢᠭᠡ ᠵᠣᠨ ᠤ ᠤᠭ ᠢᠵᠠᠭᠤᠷ ᠤᠨ ᠲᠤᠭᠤᠵᠢ|text='''ᠰᠢᠢᠯᠬᠡ''' [[ᠠᠷᠭᠤᠨᠢ]] [[ᠬᠣᠶ᠋ᠠᠷ]] [[ᠭ᠋ᠣᠣᠯ]] …”
2514023
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|etym}}===
Dạng viết của {{ncog|bua|Шиилхэ}}.
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{place|cmg|sông|t=Shilka}}
#* {{quote-book|cmg|year=1875|author=Vandan Yumsunov|title=ᠬᠣᠷᠢ ᠶᠢᠨ ᠠᠷᠪᠠᠨ ᠨᠢᠭᠡᠨ ᠡᠴᠢᠭᠡ ᠵᠣᠨ ᠤ ᠤᠭ ᠢᠵᠠᠭᠤᠷ ᠤᠨ ᠲᠤᠭᠤᠵᠢ|text='''ᠰᠢᠢᠯᠬᠡ''' [[ᠠᠷᠭᠤᠨᠢ]] [[ᠬᠣᠶ᠋ᠠᠷ]] [[ᠭ᠋ᠣᠣᠯ]] [[ᠤ᠋ᠨ|ᠤᠨ]] [[ᠨᠡᠶ᠋ᠢᠯᠡᠬᠦ|ᠨᠢᠢᠯᠡᠬᠦ]] [[ᠪᠡᠯᠴᠢᠷ|ᠪᠢᠯᠴᠢᠷ]]᠃|t=…hợp lưu của sông '''Shilka''' và sông Argun…}}
at70o2z4kzlv3lmyp2p9s4u9t5gq3qx
ᠰᠢᠲ᠋ᠢ
0
412160
2514024
2026-08-14T11:55:48Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ riêng}} # {{alt form|cmg|ᠴᠢᠲᠡ|t=Chita}} ==={{ĐM|ref}}=== * {{cite-book|cmg|author={{xlit|ru|Тугултур Тобоев}}|year=1863|title=ᠬᠣᠷᠢ ᠬᠢᠭᠡᠳ ᠠᠭ᠋ᠤᠶ᠋ᠢᠨ ᠪᠤᠷᠢᠶ᠋ᠠᠳ ᠨᠠᠷ ᠤᠨ ᠤᠷᠢᠲᠠ ᠳ᠋ᠠᠭ᠋ᠠᠨ ᠪᠣᠯᠤᠭᠰᠠᠨ ᠠᠨᠤ|url=https://altaica.ru/LIBRARY/POPPE/Poppe_Hori.pdf}}”
2514024
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# {{alt form|cmg|ᠴᠢᠲᠡ|t=Chita}}
==={{ĐM|ref}}===
* {{cite-book|cmg|author={{xlit|ru|Тугултур Тобоев}}|year=1863|title=ᠬᠣᠷᠢ ᠬᠢᠭᠡᠳ ᠠᠭ᠋ᠤᠶ᠋ᠢᠨ ᠪᠤᠷᠢᠶ᠋ᠠᠳ ᠨᠠᠷ ᠤᠨ ᠤᠷᠢᠲᠠ ᠳ᠋ᠠᠭ᠋ᠠᠨ ᠪᠣᠯᠤᠭᠰᠠᠨ ᠠᠨᠤ|url=https://altaica.ru/LIBRARY/POPPE/Poppe_Hori.pdf}}
ricaxxkr1m8tpiqoyfudnr1143kabp9
ᠴᠢᠲᠡ
0
412161
2514025
2026-08-14T11:58:37Z
WhoAlone
40420
Trang mới: “=={{langname|cmg}}== ==={{ĐM|pr-noun}}=== {{head|cmg|Danh từ riêng}} # [[Chita]].”
2514025
wikitext
text/x-wiki
=={{langname|cmg}}==
==={{ĐM|pr-noun}}===
{{head|cmg|Danh từ riêng}}
# [[Chita]].
ehl7sqihzergfby6e7r3qabnpaycg7p