Vikigezgin trwikivoyage https://tr.wikivoyage.org/wiki/Anasayfa MediaWiki 1.47.0-wmf.24 first-letter Ortam Özel Tartışma Kullanıcı Kullanıcı mesaj Vikigezgin Vikigezgin tartışma Dosya Dosya tartışma MediaWiki MediaWiki tartışma Şablon Şablon tartışma Yardım Yardım tartışma Kategori Kategori tartışma TimedText TimedText talk Modül Modül tartışma Event Event talk Vikigezgin:Kullanıcı hakları adaylıkları 4 334 17862 17853 2026-10-24T16:49:33Z Brightt11 1786 Restored revision 17803 by [[Special:Contributions/Brightt11|Brightt11]] ([[User talk:Brightt11|talk]]) (TwinkleGlobal) 17862 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == === Hizmetli adayı [[User:Örnek|Örnek]] === <div class="plainlinks"> {{Aday kullanıcı |Kullanıcı= <!-- adayın veya sizin kullanıcı adınız --> |Status= <!-- burayı değiştirmeyin --> }} </div> ::''20:09, 26 Ekim 2026 (UTC) tarihinde oylama bitiyor.''<!-- burayı değiştirmeyin --> <!-- Adaylık açıklaması buraya gelecek --> Adaylık gerekçesi. [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 20.09, 12 Ekim 2026 (UTC) <!-- Adaylık alanı sonu, oylama alanı başlangıcı --> == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} 927ksyycynk3d8urfqbed07ajsvlvoy 17863 17862 2026-10-24T16:49:44Z Brightt11 1786 /* Hizmetli adayı Örnek */ Tarafsız ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi) 17863 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == === Hizmetli adayı [[User:Örnek|Örnek]] === <div class="plainlinks"> {{Aday kullanıcı |Kullanıcı= <!-- adayın veya sizin kullanıcı adınız --> |Status= <!-- burayı değiştirmeyin --> }} </div> ::''20:09, 26 Ekim 2026 (UTC) tarihinde oylama bitiyor.''<!-- burayı değiştirmeyin --> <!-- Adaylık açıklaması buraya gelecek --> Adaylık gerekçesi. [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 20.09, 12 Ekim 2026 (UTC) <!-- Adaylık alanı sonu, oylama alanı başlangıcı --> * {{Tarafsız}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} mzmcwe87nw5xj5xfycqserj3ppeqooz 17865 17863 2026-10-24T17:06:51Z Brightt11 1786 /* Hizmetli adayı Örnek */ Destek ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.) 17865 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == === Hizmetli adayı [[User:Örnek|Örnek]] === <div class="plainlinks"> {{Aday kullanıcı |Kullanıcı= <!-- adayın veya sizin kullanıcı adınız --> |Status= <!-- burayı değiştirmeyin --> }} </div> ::''20:09, 26 Ekim 2026 (UTC) tarihinde oylama bitiyor.''<!-- burayı değiştirmeyin --> <!-- Adaylık açıklaması buraya gelecek --> Adaylık gerekçesi. [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 20.09, 12 Ekim 2026 (UTC) <!-- Adaylık alanı sonu, oylama alanı başlangıcı --> * {{Tarafsız}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Destek}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} 3uazetg5wjygavhhhdyt8xg3mcnypo4 17866 17865 2026-10-24T17:07:02Z Brightt11 1786 /* Hizmetli adayı Örnek */ Kısmi destek ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.) 17866 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == === Hizmetli adayı [[User:Örnek|Örnek]] === <div class="plainlinks"> {{Aday kullanıcı |Kullanıcı= <!-- adayın veya sizin kullanıcı adınız --> |Status= <!-- burayı değiştirmeyin --> }} </div> ::''20:09, 26 Ekim 2026 (UTC) tarihinde oylama bitiyor.''<!-- burayı değiştirmeyin --> <!-- Adaylık açıklaması buraya gelecek --> Adaylık gerekçesi. [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 20.09, 12 Ekim 2026 (UTC) <!-- Adaylık alanı sonu, oylama alanı başlangıcı --> * {{Tarafsız}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Destek}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmi destek}} tedt [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} sh3ze9nk1g8ctvgp2zk9amtsuvozqth 17867 17866 2026-10-24T17:07:10Z Brightt11 1786 /* Hizmetli adayı Örnek */ Karşı ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.) 17867 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == === Hizmetli adayı [[User:Örnek|Örnek]] === <div class="plainlinks"> {{Aday kullanıcı |Kullanıcı= <!-- adayın veya sizin kullanıcı adınız --> |Status= <!-- burayı değiştirmeyin --> }} </div> ::''20:09, 26 Ekim 2026 (UTC) tarihinde oylama bitiyor.''<!-- burayı değiştirmeyin --> <!-- Adaylık açıklaması buraya gelecek --> Adaylık gerekçesi. [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 20.09, 12 Ekim 2026 (UTC) <!-- Adaylık alanı sonu, oylama alanı başlangıcı --> * {{Tarafsız}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Destek}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmi destek}} tedt [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Karşı}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} 8c7psxuejldbpl0s1e7q1ww9j2ovedl 17868 17867 2026-10-24T17:07:20Z Brightt11 1786 /* Hizmetli adayı Örnek */ Kısmi karşı ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.) 17868 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == === Hizmetli adayı [[User:Örnek|Örnek]] === <div class="plainlinks"> {{Aday kullanıcı |Kullanıcı= <!-- adayın veya sizin kullanıcı adınız --> |Status= <!-- burayı değiştirmeyin --> }} </div> ::''20:09, 26 Ekim 2026 (UTC) tarihinde oylama bitiyor.''<!-- burayı değiştirmeyin --> <!-- Adaylık açıklaması buraya gelecek --> Adaylık gerekçesi. [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 20.09, 12 Ekim 2026 (UTC) <!-- Adaylık alanı sonu, oylama alanı başlangıcı --> * {{Tarafsız}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Destek}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmi destek}} tedt [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Karşı}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmi karşı}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} 1bt2p83hvt6ji2lntya5vacf51gat5f 17869 17868 2026-10-24T17:07:30Z Brightt11 1786 /* Hizmetli adayı Örnek */ Yorum ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.) 17869 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == === Hizmetli adayı [[User:Örnek|Örnek]] === <div class="plainlinks"> {{Aday kullanıcı |Kullanıcı= <!-- adayın veya sizin kullanıcı adınız --> |Status= <!-- burayı değiştirmeyin --> }} </div> ::''20:09, 26 Ekim 2026 (UTC) tarihinde oylama bitiyor.''<!-- burayı değiştirmeyin --> <!-- Adaylık açıklaması buraya gelecek --> Adaylık gerekçesi. [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 20.09, 12 Ekim 2026 (UTC) <!-- Adaylık alanı sonu, oylama alanı başlangıcı --> * {{Tarafsız}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Destek}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmi destek}} tedt [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Karşı}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmi karşı}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Yorum}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} 1luakky5wqf2kirssht619q7uviqgox 17873 17869 2026-10-24T17:14:59Z Brightt11 1786 /* Hizmetli adayı Örnek */ Kısmen karşı ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.) 17873 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == === Hizmetli adayı [[User:Örnek|Örnek]] === <div class="plainlinks"> {{Aday kullanıcı |Kullanıcı= <!-- adayın veya sizin kullanıcı adınız --> |Status= <!-- burayı değiştirmeyin --> }} </div> ::''20:09, 26 Ekim 2026 (UTC) tarihinde oylama bitiyor.''<!-- burayı değiştirmeyin --> <!-- Adaylık açıklaması buraya gelecek --> Adaylık gerekçesi. [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 20.09, 12 Ekim 2026 (UTC) <!-- Adaylık alanı sonu, oylama alanı başlangıcı --> * {{Tarafsız}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Destek}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmi destek}} tedt [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Karşı}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmi karşı}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Yorum}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmen karşı}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} jy9pn08t8ssswq4i5v9rwb3lmxe9ndc 17874 17873 2026-10-24T17:15:35Z Brightt11 1786 /* Hizmetli adayı Örnek */ Kısmen karşı ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.) 17874 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == === Hizmetli adayı [[User:Örnek|Örnek]] === <div class="plainlinks"> {{Aday kullanıcı |Kullanıcı= <!-- adayın veya sizin kullanıcı adınız --> |Status= <!-- burayı değiştirmeyin --> }} </div> ::''20:09, 26 Ekim 2026 (UTC) tarihinde oylama bitiyor.''<!-- burayı değiştirmeyin --> <!-- Adaylık açıklaması buraya gelecek --> Adaylık gerekçesi. [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 20.09, 12 Ekim 2026 (UTC) <!-- Adaylık alanı sonu, oylama alanı başlangıcı --> * {{Tarafsız}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Destek}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmi destek}} tedt [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Karşı}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmi karşı}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Yorum}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmen karşı}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmen karşı}} tekrar test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} 3isajlelhbo25rggrhchp43whwf5s8r 17875 17874 2026-10-24T17:15:52Z Brightt11 1786 /* Hizmetli adayı Örnek */ 17875 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == === Hizmetli adayı [[User:Örnek|Örnek]] === <div class="plainlinks"> {{Aday kullanıcı |Kullanıcı= <!-- adayın veya sizin kullanıcı adınız --> |Status= <!-- burayı değiştirmeyin --> }} </div> ::''20:09, 26 Ekim 2026 (UTC) tarihinde oylama bitiyor.''<!-- burayı değiştirmeyin --> <!-- Adaylık açıklaması buraya gelecek --> Adaylık gerekçesi. [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 20.09, 12 Ekim 2026 (UTC) <!-- Adaylık alanı sonu, oylama alanı başlangıcı --> == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} 927ksyycynk3d8urfqbed07ajsvlvoy 17876 17875 2026-10-24T17:15:57Z Brightt11 1786 /* Hizmetli adayı Örnek */ Karşı ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.) 17876 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == === Hizmetli adayı [[User:Örnek|Örnek]] === <div class="plainlinks"> {{Aday kullanıcı |Kullanıcı= <!-- adayın veya sizin kullanıcı adınız --> |Status= <!-- burayı değiştirmeyin --> }} </div> ::''20:09, 26 Ekim 2026 (UTC) tarihinde oylama bitiyor.''<!-- burayı değiştirmeyin --> <!-- Adaylık açıklaması buraya gelecek --> Adaylık gerekçesi. [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 20.09, 12 Ekim 2026 (UTC) <!-- Adaylık alanı sonu, oylama alanı başlangıcı --> * {{Karşı}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} jnz59wer6et1zgwcovky0o3cji5udck 17879 17876 2026-10-24T19:14:28Z Brightt11 1786 /* Hizmetli adayı Örnek */ Karşı ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.) 17879 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == === Hizmetli adayı [[User:Örnek|Örnek]] === <div class="plainlinks"> {{Aday kullanıcı |Kullanıcı= <!-- adayın veya sizin kullanıcı adınız --> |Status= <!-- burayı değiştirmeyin --> }} </div> ::''20:09, 26 Ekim 2026 (UTC) tarihinde oylama bitiyor.''<!-- burayı değiştirmeyin --> <!-- Adaylık açıklaması buraya gelecek --> Adaylık gerekçesi. [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 20.09, 12 Ekim 2026 (UTC) <!-- Adaylık alanı sonu, oylama alanı başlangıcı --> * {{Karşı}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Karşı}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} 8ofzrk6ap9ksppeet68mz3dbazlcx62 17880 17879 2026-10-24T19:15:04Z Brightt11 1786 /* Hizmetli adayı Örnek */ Kısmi destek ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.) 17880 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == === Hizmetli adayı [[User:Örnek|Örnek]] === <div class="plainlinks"> {{Aday kullanıcı |Kullanıcı= <!-- adayın veya sizin kullanıcı adınız --> |Status= <!-- burayı değiştirmeyin --> }} </div> ::''20:09, 26 Ekim 2026 (UTC) tarihinde oylama bitiyor.''<!-- burayı değiştirmeyin --> <!-- Adaylık açıklaması buraya gelecek --> Adaylık gerekçesi. [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 20.09, 12 Ekim 2026 (UTC) <!-- Adaylık alanı sonu, oylama alanı başlangıcı --> * {{Karşı}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Karşı}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmi destek}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} 0ewrcf0nkyzabrdfmo2jo1l0dngkkid 17882 17880 2026-10-24T19:18:32Z Brightt11 1786 /* Hizmetli adayı Örnek */ Destek ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.) 17882 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == === Hizmetli adayı [[User:Örnek|Örnek]] === <div class="plainlinks"> {{Aday kullanıcı |Kullanıcı= <!-- adayın veya sizin kullanıcı adınız --> |Status= <!-- burayı değiştirmeyin --> }} </div> ::''20:09, 26 Ekim 2026 (UTC) tarihinde oylama bitiyor.''<!-- burayı değiştirmeyin --> <!-- Adaylık açıklaması buraya gelecek --> Adaylık gerekçesi. [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 20.09, 12 Ekim 2026 (UTC) <!-- Adaylık alanı sonu, oylama alanı başlangıcı --> * {{Karşı}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Karşı}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmi destek}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Destek}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 19.18, 24 Ekim 2026 (UTC) == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} dqa5qre5k86a6vxp2qvoptuldzmy69h 17883 17882 2026-10-24T19:20:13Z Brightt11 1786 /* Hizmetli adayı Örnek */ Yorum ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.) 17883 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == === Hizmetli adayı [[User:Örnek|Örnek]] === <div class="plainlinks"> {{Aday kullanıcı |Kullanıcı= <!-- adayın veya sizin kullanıcı adınız --> |Status= <!-- burayı değiştirmeyin --> }} </div> ::''20:09, 26 Ekim 2026 (UTC) tarihinde oylama bitiyor.''<!-- burayı değiştirmeyin --> <!-- Adaylık açıklaması buraya gelecek --> Adaylık gerekçesi. [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 20.09, 12 Ekim 2026 (UTC) <!-- Adaylık alanı sonu, oylama alanı başlangıcı --> * {{Karşı}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Karşı}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmi destek}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Destek}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 19.18, 24 Ekim 2026 (UTC) * {{Yorum}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 19.20, 24 Ekim 2026 (UTC) == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} guqlk7ckjk0t74metr1sk92hauk64nb 17886 17883 2026-10-24T19:21:33Z Brightt11 1786 /* Hizmetli adayı Örnek */ Karşı ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.) 17886 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == === Hizmetli adayı [[User:Örnek|Örnek]] === <div class="plainlinks"> {{Aday kullanıcı |Kullanıcı= <!-- adayın veya sizin kullanıcı adınız --> |Status= <!-- burayı değiştirmeyin --> }} </div> ::''20:09, 26 Ekim 2026 (UTC) tarihinde oylama bitiyor.''<!-- burayı değiştirmeyin --> <!-- Adaylık açıklaması buraya gelecek --> Adaylık gerekçesi. [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 20.09, 12 Ekim 2026 (UTC) <!-- Adaylık alanı sonu, oylama alanı başlangıcı --> * {{Karşı}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Karşı}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmi destek}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Destek}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 19.18, 24 Ekim 2026 (UTC) * {{Yorum}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 19.20, 24 Ekim 2026 (UTC) * {{Karşı}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 19.21, 24 Ekim 2026 (UTC) == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} fof3r480eesk4nam3fvs5nhjgv6k3nr 17887 17886 2026-10-24T19:21:53Z Brightt11 1786 /* Hizmetli adayı Örnek */ Tarafsız ([[Kullanıcı:Brightt11/Oyver.js|Oyver (beta)]] ile görüş belirtildi.) 17887 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == === Hizmetli adayı [[User:Örnek|Örnek]] === <div class="plainlinks"> {{Aday kullanıcı |Kullanıcı= <!-- adayın veya sizin kullanıcı adınız --> |Status= <!-- burayı değiştirmeyin --> }} </div> ::''20:09, 26 Ekim 2026 (UTC) tarihinde oylama bitiyor.''<!-- burayı değiştirmeyin --> <!-- Adaylık açıklaması buraya gelecek --> Adaylık gerekçesi. [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 20.09, 12 Ekim 2026 (UTC) <!-- Adaylık alanı sonu, oylama alanı başlangıcı --> * {{Karşı}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Karşı}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmi destek}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Destek}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 19.18, 24 Ekim 2026 (UTC) * {{Yorum}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 19.20, 24 Ekim 2026 (UTC) * {{Karşı}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 19.21, 24 Ekim 2026 (UTC) * {{Tarafsız}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 19.21, 24 Ekim 2026 (UTC) == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} spzvizqy9co66ymec6czpju0emowoxj 17888 17887 2026-10-24T19:46:39Z Brightt11 1786 /* Hizmetli adayı Örnek */ Destek ([[Kullanıcı:Brightt11/Oyver.js|Oyver (beta)]] ile görüş belirtildi.) 17888 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == === Hizmetli adayı [[User:Örnek|Örnek]] === <div class="plainlinks"> {{Aday kullanıcı |Kullanıcı= <!-- adayın veya sizin kullanıcı adınız --> |Status= <!-- burayı değiştirmeyin --> }} </div> ::''20:09, 26 Ekim 2026 (UTC) tarihinde oylama bitiyor.''<!-- burayı değiştirmeyin --> <!-- Adaylık açıklaması buraya gelecek --> Adaylık gerekçesi. [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 20.09, 12 Ekim 2026 (UTC) <!-- Adaylık alanı sonu, oylama alanı başlangıcı --> * {{Karşı}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Karşı}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Kısmi destek}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC) * {{Destek}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 19.18, 24 Ekim 2026 (UTC) * {{Yorum}} test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 19.20, 24 Ekim 2026 (UTC) * {{Karşı}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 19.21, 24 Ekim 2026 (UTC) * {{Tarafsız}} [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 19.21, 24 Ekim 2026 (UTC) * {{Destek}} Tekrar test [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 19.46, 24 Ekim 2026 (UTC) == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} psyong90lh6xyrhuepgqirl5fbtg1ur 17889 17888 2026-10-24T20:07:28Z Brightt11 1786 Restored revision 17700 by [[Special:Contributions/Brightt11|Brightt11]] ([[User talk:Brightt11|talk]]): Artık kullanıma hazır (TwinkleGlobal) 17889 wikitext text/x-wiki {{/Başlık}} == Arayüz yöneticisi adayları == == Bürokrat adayları == == Hizmetli adayları == == Ayrıca bakınız == * [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv]] * [[Vikigezgin:Hizmetliler]] * [[Vikigezgin:Bürokratlar]] * [[Özel:Listusers/sysop|Mevcut hizmetliler]] {{kullanıcı grupları}} os5286of3ywrqod0jarzo51cyiso1rz Vikigezgin:Kullanıcı hakları adaylıkları/Başlık 4 1407 17858 17760 2026-10-24T16:23:16Z Brightt11 1786 /* Haklar */ 17858 wikitext text/x-wiki Bu sayfada kullanıcı hak adaylıkları yer almaktadır. Herhangi bir adaylık başvurusu oluşturmadan önce [[Vikigezgin: politika ve yönergeler|politika ve yönergelere]] aşina olup olmadığınızı gözden geçirmelisiniz. Kendinizi hazır hissettiğinizde bu sayfada herhangi bir yetki için adaylık sayfası oluşturabilirsiniz. Adaylıklarda tartışma süresi '''14 gündür.''' Süre sonunda, bir hizmetli veya deneyimli bir kullanıcı adaylığı sonuçlandırır. Karşıt oyların fazla olması durumunda hizmetli, adaylığı erken kapatma inisiyatifine sahiptir. == Arayüz yöneticilği == {{bilgi|type=warn|Wikimedia Vakfı Ofisi (WMF Office), arayüz yöneticilerinin iki aşamalı kimlik doğrulama kullanmasını zorunlu kılmıştır.}} Arayüz yöneticileri, MediaWiki ad alanındaki genel metin sayfalarını ve CSS, JS, ve JSON sayfalarını düzenleyebilen kullanıcılardır. Aynı zamanda tüm CSS, JS ve JSON sayfalarını düzenleyebilen tek kullanıcı grubudur. == Hizmetlilik == Hizmetliler, insanların potansiyel olarak zararlı olabilecek araçları güvenle emanet edebileceklerini düşündükleri kullanıcılardır. Hizmetli olmanın, kişinin yaptığı hizmetlerden ötürü hakedilmiş bir rütbe ya da ödül gibi düşünülmemesi gerekir. Bir Vikiçelebi'nin [[Vikigezgin:Hizmetliler|hizmetli]] olabilmesi için, tarafsız olması, Vikigezgin yapısını biliyor olması ve grup çalışmasına uygun olması gerekir. Aynı zamanda problemler karşısında soğukkanlılığını koruması da önemli bir gerekliliktir. Tabii ki, bütün bu özelliklerini kullanıcı olarak yaptığı çalışmalarda göstermiş olması gerekmektedir. Hizmetlilerden yüksek standartlarda davranış ve rehberlik yeteneği beklenir, zira diğer kullanıcılar yardım ve öğüt almak için sıklıkla hizmetlilerin kapısını çalarlar. Aynı zamanda hizmetlilik, ek görevler ve sorumluluklar da getirmektedir. Bu ek görevler ve sorumluluklar sadece hizmetlilerin yapabileceği şeyler olduğu için öncelikli olup, madde yazmak gibi işler ikinci planda kalır. Bu durumdan rahatsız olacaksanız şimdiden vazgeçmeniz en doğrusudur. == Bürokratlar == Bazı hizmetliler ek olarak bürokrat sıfatına sahiptirler. Bürokratlar hizmetlilik görevlerinin yanı sıra özel fonksiyonlara da sahiptirler. ---- Topluluk bu yetkileri güvenilir kullanıcılara verirler, ve bu sebeple adayların, diğer kullanıcılar hakkında güvenilir olup olmadıklarına karar verebilecekleri kadar makul bir süre Vikigezgin'de veya diğer Wikimedia projelerinde bulunmuş olmaları gerekmekte. '''Oylama aşaması ve sonuçlandırma''': Herhangi bir kullanıcı, kullanıcı hesabı olan bir başka kullanıcıyı aday gösterebilir. Kişinin kendisini aday göstermesi de mümkündür. [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv|Eski başvurulara]] bakarak topluluğun hangi beklentilerde olduğuna dair fikir sahibi olabilir ve böylece sizin başvurunuza nasıl bir tepki geleceğini anlamaya çalışabilirsiniz. Adaylıklar başvurunun yapıldığı andan itibaren 14 gün boyunca sayfada aktif olarak kalır ve bu süre zarfında kullanıcılar fikirlerini belirtir, sorular sorar ve yorumlarda bulunurlar. Bu sürenin sonunda bir [[Vikigezgin:H|hizmetli]] veya kıdemli bir kullanıcı ilgili başvuruyu sonuçlandırarak hizmetlilik için bir fikir birliğinin sağlanıp sağlanamadığına bakar. '''Fikir belirtme ve yorumlama''': Kullanıcı hesabı olan her Vikigezgin kullanıcısı adaylara oy verip yorum yapabilir. Aday da kişilerin yorumlarını cevaplayabilir. Hile şüpheleri barındıran bazı yorumlar dikkate alınmayabilir; bunlar çok yeni yazarların, [[Vikigezgin:Kukla|''çorap'' kuklaların]] ve [[Vikigezgin:Kukla|''canlı'' kuklaların]] katılımları olabilir. Lütfen fikrinizi muhakemenize dair bir açıklama yazarak açıklayınız. Yorumunuz eğer uygun ve destekleyici kanıtlar taşıyorsa daha büyük önem arz edecektir. == Aday olmak == Hizmetlilik veya diğer haklar için aday olmak veya başka bir kullanıcıyı aday göstermek için '''[[Vikigezgin:Kullanıcı_hakları_adaylıkları/Adaylık|bu sayfadaki]]''' yönergeleri takip etmelisiniz. <div class="metadata" style="background-color: #CED8DF; margin: 2em 0 0 0; padding: 0 10px 0 10px; border: 1px solid #AAAAAA; font-family: 'Montserrat semibold', sans-serif;"> ;Aday olma kriterleri #En az 1 aydır üye olmak (Bürokratlık adaylığı için en az 6 ay) #Yeterli değişiklik sayıları: ##Bürokratlık adaylığın geçerli olması için en az 500 değişiklik yapılmış olması gerekmektedir. ##Hizmetli ve Arayüz Yöneticiliği adaylığı için ise en az 250 değişiklik yeterlidir. ;Başvuru anı #Adaylık gerekçesi ;Sonuç #Oylama süresi en az '''14 gün''' sürecektir. #Arayüz yöneticiliği ve hizmetlilik adaylğında en az '''5 oy''' ve %75 puan gereklidir. #Bürokratlık adaylığında en az '''15 oy''' ve %80 puan gereklidir; %75 ila %80 puan arası ise topluluk kararına bağlıdır. #Oylama kriterlerine sahip olan her Vikiçelebi oylamaya katılım sağlayabilir (bakınız: aşağıdaki notlar). '''Notlar''' *Vikiçelebiler, adaylıklarda sadece 1 oy kullanabilir. *Aynı kullanıcı, birden fazla oy kullanırsa bütün oyları '''geçersiz''' sayılır. *Eski başvurulara [[Vikigezgin:Kullanıcı hakları adaylıkları/Arşiv|bu]] sayfadan bakabilirsiniz. </div> [[Kategori:Vikigezgin panoları]] byukx78cpigswktvd6ilu1t290sf4vi Kullanıcı:Brightt11/İETT hatlarının listesi 2 4131 17890 15598 2026-10-24T22:01:21Z Brightt11 1786 17890 wikitext text/x-wiki == Hatlar == {| class="wikitable sortable" |- ! Hat ismi !! Tarife !! Bölge !! Transfer |- | 1 Kirazlıtepe - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 2 Bostancı - Harem || Standart || Anadolu || {{ulaşım sembol|istanbul|M8}} |- | 3 Burhaniye - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 3A Ünalan - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 3B Ünalan - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 4 Maltepe Sahili - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 5 Valievleri - Üsküdar || Standart || Anadolu || |- | 6 Esatpaşa - Harem || Standart || Anadolu || |- | 7 Topağacı - Ümraniye - Rasathane || Standart || Anadolu || |- | 8 Valievleri - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 8A Batı Alaşehir / Fikirtepe - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 8E Esenevler - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}}{{ulaşım sembol|istanbul|T3}} |- | 8K Tepeüstü - Alaşehir - Kozyatağı Metrosu|| Standart || Anadolu ||{{ulaşım sembol|istanbul|M5}} |- | 8Y Soyak Yenişehir - Yenisahra Metrosu|| Standart || Anadolu || {{ulaşım sembol|istanbul|M5}} |- | 9A Atakent - Ümraniye Metrosu || Standart || Anadolu || {{ulaşım sembol|istanbul|M5}} |- | 9ÇN Çekmeköy - Nişantepe || Standart || Anadolu || |- | 9H Cumhuriyetköy / Hüseyinli - Çekmeköy || Standart || Anadolu || |- | 9K Atatürk Mahallesi - Taşdelen - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 9S Yeşiltepe - Çekmeköy Metrosu - Atatürk Mahallesi || Standart || Anadolu || {{ulaşım sembol|istanbul|M5}} {{ulaşım sembol|istanbul|M13}} |- | 9KS Kasaba evleri - Necip Fazıl Metrosu || Standart || Anadolu || {{ulaşım sembol|istanbul|M5}} |- | 9T Şahinbey - Altunizade Metrobüs || Standart || Anadolu || {{ulaşım sembol|istanbul|Metrobüs}} |- | 10 Ümraniye Eğitim Araştırma Hastanesi - Ataşehir - Bostancı || Standart|| Anadolu|| {{ulaşım sembol|istanbul|M8}} |- | 10A Esatpaşa - Yeditepe Üniversitesi - Ferhatpaşa|| Standart || Anadolu || |- | 10B Bostancı - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}}{{ulaşım sembol|istanbul|M8}} |- | 10E Esatpaşa - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 10EK Esatpaşa - Ataşehir - Kozyatağı Metrosu|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 10G Ümraniye - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}}{{ulaşım sembol|istanbul|M5}} |- | 11 Yenidoğan - Altunizade || Standart || Anadolu ||{{ulaşım sembol|istanbul|Metrobüs}} |- | 11A Alemdağ - Altunizade || Standart || Anadolu ||{{ulaşım sembol|istanbul|Metrobüs}} |- | 11BE Burhaniye - Üsküdar|| Standart || Anadolu || {{ulaşım sembol|istanbul|Metrobüs}} |- | 11C Burhaniye - Üsküdar Marmaray|| Standart || Anadolu || {{ulaşım sembol|istanbul|Metrobüs}} |- | 11ÇB Kavacık - Ümraniye || Standart || Anadolu || |- | 11CÜ Burhaniye - Ümraniye Metrosu Ring|| Standart || Anadolu || {{ulaşım sembol|istanbul|M5}} |- | 11D İnklap - Ümraniye Metro || Standart || Anadolu || {{ulaşım sembol|istanbul|M5}} |- | 11E Esatpaşa - Üsküdar|| Standart || Anadolu || |- | 11ES Esenevler - Acıbadem Metrobüs / Üsküdar|| Standart || Anadolu || {{ulaşım sembol|istanbul|metrobüs}} |- | 11G Yenidoğan - Necip Fazıl Metrosu|| Standart || Anadolu || {{ulaşım sembol|istanbul|M5}} |- | 11H Ortaçeşme - Ümraniye || Standart || Anadolu || |- | 11K Kazım Karabekir - Ümraniye Metro || Standart || Anadolu|| {{ulaşım sembol|istanbul|M5}} |- | 11L Esatpaşa / Bulgurlu - Üsküdar|| Standart || Anadolu || |- | 11M Evtaş Blokları - Bulgurlu || Standart || Anadolu || |- | 11P Emek - Üsküdar|| Standart || Anadolu || |- | 11R Reşadiye - Necip Fazıl || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 11SA Uğurmumcu / Samandıra - Yenidoğan Peron|| Standart || Anadolu || |- | 11ST Dumlupınar - Ümraniye || Standart || Anadolu || |- | 11T Acarlar Sitesi - Harem || Standart || Anadolu || |- | 11Ü Esatpaşa / Ünalan - Üsküdar|| Standart || Anadolu || |- | 11ÜS Sultanbeyli - Üsküdar || Standart || Anadolu || |- | 11Y Yavuztürk - Üsküdar|| Standart || Anadolu || |- | 12 Kadıköy - Üsküdar || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 12A Kadıköy - Üsküdar || Standart || Anadolu ||{{ulaşım sembol|istanbul|M4}} |- | 12C Üsküdar - Üsküdar Devlet Hastanesi|| Standart || Anadolu || |- | 13 Ataşehir - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 13B Yenişehir - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 13FY Batı Alaşehir - Mevlana Metro|| Standart || Anadolu || {{ulaşım sembol|istanbul|M8}} |- | 13H Evtaş Blokları - Ümraniye Metro|| Standart || Anadolu || {{ulaşım sembol|istanbul|M5}} |- | 13M Şerifali - Üsküdar|| Standart || Anadolu || |- | 13Y Çakmak Mahallesi - Yenisahra - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 14 Yenidoğan / Ümraniye - Kadıköy|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 14A Alemdağ - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 14AK Alemdağ - Ziverbey|| Standart || Anadolu || |- | 14B Adem Yavuz Mahallesi - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 14BK Çekmeköy / Parseller Mahallesi - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M8}} |- | 14C Üsküdar Devlet hastanesi - Kadıköy|| Standart || Anadolu || |- | 14CE Çekmeköy - Kadıköy || Standart || Anadolu ||{{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M5}} |- | 14D Dumlupınar / Kazım Karabekir - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 14DK İnklap Mahallesi - Libadiye / Kadıköy|| Standart || Anadolu ||{{ulaşım sembol|istanbul|M4}} |- | 14E Kazım Karabekir - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 14ES Esenşehir - Kadıköy|| Standart || Anadolu ||{{ulaşım sembol|istanbul|M4}} |- | 14F Küflüce - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 14FD Ferah Mahallesi - Üsküdar Devlet Hastanesi|| Standart || Anadolu || |- | 14K Kazım Karabekir - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 14KS Sultanbeyli Gölet - Kozyatağı Metro|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M8}} |- | 14m Kavacık Yeni Camii - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 14R Rasathane - Kadıköy || Standart || Anadolu ||{{ulaşım sembol|istanbul|M4}} |- | 14S Sultanbeyli - Kozyatağı Metro || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M8}} |- | 14ŞB Şahinbey - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 14T Atatürk Mahallesi / Taşdelen - Kozyatağı Metro|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M8}} |- | 14Y Yavuztürk Mahallesi - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 14YE Yavuztürk - Ümraniye - Emek Mahallesi|| Standart|| Anadolu || |- | 14YK Yenidoğan / Şile Yolu - Ayrılıkçeşme|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|Marmaray}} |- | 15 Beykoz - Üsküdar || Standart || Anadolu || |- | 15A Anadolu Kavşak - Kavacık || Standart || Anadolu || |- | 15B Topağacı Mahallesi / Güzeltepe - Üsküdar|| Standart || Anadolu || |- | 15BK Dereseki - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 15C Büyük Çamlıca Camii / Ferah Mahallesi - Üsküdar|| Standart || Anadolu || |- | 15CA Büyük Çamlıca Camii / Bulgurlu Metro - Göztepe Köprüsü|| Standart || Anadolu || {{ulaşım sembol|istanbul|M5}} |- | 15ÇB Kavacık - Çavuşbaşı / Çengeldere|| Standart || Anadolu || |- | 15ÇK Güzeltepe - Kadıköy|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M8}} |- | 15D Anadolufeneri / Kaynarca / Dereski - Sultaniye || Standart || Anadolu|| |- | 15E Tekke Mevkii - Üsküdar || Standart || Anadolu|| |- | 15H Hekimbaşı - Üsküdar || Standart || Anadolu || |- | 15K Kirazlıtepe - Üsküdar || Standart || Anadolu || |- | 15KB Kavacık - Kozyatağı Metrosu || Standart || Anadolu || {{ulaşım sembol|istanbul|M8}} |- | 15KÇ Karanlıkdere / ÇAVUŞBAŞI - ÜSKÜDAR || Standart || Anadolu|| |- | 15M Kavacık Yeni Camii - Üsküdar|| Standart || Anadolu|| |- | 15N Arkboyu - Üsküdar|| Standart || Anadolu|| |- | 15P Soğuksu Mahallesi - Üsküdar || Standart || Anadolu|| |- | 15R Rasathane - Üsküdar|| Standart || Anadolu|| |- | 15S Sultanmurat - Üsküdar|| Standart || Anadolu|| |- | 15SD Ortaçeşme/Sultaniye-Dudullu İMES || Standart || Anadolu|| |- | 15SK Sultanmurat - Kadıköy|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 15ŞN Şenevler - Üsküdar || Standart || Anadolu|| |- | 15T Tokatköy - Üsküdar || Standart || Anadolu|| |- | 15TA Türk Alman Üniversitesi - Kavacık || Standart || Anadolu|| |- | 15TY Tokatköy/ Beykoz - Yenimahalle || Standart || Anadolu|| |- | 15U Tufan- Üsküdar || Standart || Anadolu|| |- | 15Y Beykoz Yenimahalle - Üsküdar || Standart || Anadolu|| |- | 15YK Beykoz Yenimahalle - Kadıköy || Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 15Z Güzeltepe - Üsküdar || Standart || Anadolu|| |- | 16A Pendik - Üsküdar || Standart || Anadolu|| |- | 16D Altıkaynarca - Pendik - Kadıköy || Çift katlı || Anadolu|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} |- | 16F Fındıklı Mahallesi - Harem || Standart || Anadolu|| |- | 16KH Yenişehir - Marmara Üniversite Hastanesi - Kadıköy || Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 16M Acarlar / Ataşehir - Üsküdar || Standart || Anadolu || {{ulaşım sembol|istanbul|M5}} |- | 16U Uğurmumcu - Usküdar || Standart || Anadolu || |- | 16YK Yeditepe Üniversitesi - Fındıklı Mahallesi - Kozyatağı Metrosu || Standart || Anadolu || |- | 17 Pendik - Kadıköy || Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} |- | 17L Küçükyalı Lojmanları - Marmara Üniversitesi - Üstbostancı || Standart || Anadolu || |- | 17S Uğurmumcu - Bostancı || Standart || Anadolu || {{ulaşım sembol|istanbul|M8}} |- | 18D Sultanbeyli - Kadıköy || Çift katlı|| Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 18K Sultanbeyli - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 18YS Yenidoğan / Samandıra - Üsküdar|| Standart || Anadolu || |- | 19 Ferhatpaşa - Yeditepe Üniversitesi - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 19A Yenidoğan - Ayrılıkçeşme|| Standart || Anadolu || |- | 19D Ümraniye - Bostancı || Standart || Anadolu || {{ulaşım sembol|istanbul|M8}} |- | 19E Yenidoğan / Ataşehir - Kozyatağı Metro|| Standart || Anadolu || |- | 19EK Yenidoğan / Ataşehir - || Standart || Anadolu || |- | 19ES Esenşehir - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 19F Yeditepe Üniversitesi - Fındıklı Mahallesi - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 19FB Fındıklı Mahallesi - Bostancı|| Standart || Anadolu || {{ulaşım sembol|istanbul|M8}} |- | 19FS Ferhatpaşa - Soyak Yenişehir|| Standart || Anadolu || |- | 19K Acarlar Sitesi - Kayışdağı - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 19M Acarlar Sitesi / Ataşehir - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 19S Yenidoğan / Sarıgazi - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 19SK Yenidoğan / Sarıgazi - Kozyatağı Metro|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}}{{ulaşım sembol|istanbul|M8}} |- | 19T Ferhatpaşa - Kadıköy|| Standart || Anadolu||{{ulaşım sembol|istanbul|M4}} |- | 19V Veysel Karani / Samandıra - Kozyatağı Metro|| Standart || Anadolu|| |- | 19Y Ferhatpaşa - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 19YK Yenidoğan - Kayışdağı || Standart || Anadolu || |- | 20 Çifteçınarlar - Ümraniye Eğitim ve Araştırma Hastanesi|| Standart || Anadolu || |- | 20D Sarıgazi - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 20E Esatpaşa - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 20K Kazım Karabekir - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 20Ü Ümraniye Tepeüstü - Kadıköy|| Standart || Anadolu || {{ulaşım sembol|istanbul|M4}} |- | 22 İstinye Dereiçi - Kabataş|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|F1}} {{ulaşım sembol|istanbul|T1}} |- | 22B Bebek - Kabataş|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|F1}} {{ulaşım sembol|istanbul|T1}} |- | 22RE Fatih Sultan Mehmet - Beşiktaş|| Standart || Avrupa|| |- | 22 Hacıosman Metro - Havantepe|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 25A RUmelikavağı - Hacıosman Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 25C Tarabyaüstü / Ferahevler - Hacıosman Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 25E Sarıyer - Baltalimanı || Standart || Avrupa|| |- | 25G Sarıyer - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 25S1 Hacıosman Metro - Sarıyer|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 25S2 Hacıosman Metro - Sarıyer|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 25Y Hacıosman Metro - Yeni Uyum|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 26 Dikilitaş - Eminönü || Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 26A Fulya - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 26B Fulya - Kabataş|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|F1}} {{ulaşım sembol|istanbul|T1}} |- | 27 Şirintepe - Kabataş|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|F1}} {{ulaşım sembol|istanbul|T1}} |- | 27SE Seyrantepe - Kabataş|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|F1}} {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 27T Şirintepe - Şişli|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 28 Topkapı / Edirnekapı - Beşiktaş|| Standart || Avrupa|| |- | 28T Topkapı - Beşiktaş|| Standart || Avrupa|| |- | 29 İstinye Dereiçi - Zincirlikuyu Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 29A Derbent Mahallesi - Beşiktaş || Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 29B Fatih Sultan Mehmet - 4. Levent|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 29C Tarabyaüstü - Kabataş|| Standart || Avrupa||{{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|F1}} {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 29D Ferahevler - Kabataş|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|F1}} {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 29E Maslak Gazeteciler Sitesi - Beşiktaş|| Standart || Avrupa|| |- | 29GM Maslak Gazeteciler Sitesi - 4. Levent|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 29M1 Hacıosman Metrosu - Ferahevler / İstinye|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 29M2 Hacıosman Metrosu - İstinye / Ferahevler|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 29Ş İstinye Dereiçi - Şişli|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 29T Tarabyaüstü - İstinye|| Standart || Avrupa|| |- | 30A Beşiktaş - Mecidiyeköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 30D Ortaköy Dereboyu - Yenikapı || Standart || Avrupa|| {{ulaşım sembol|istanbul|Marmaray}} {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M1b}} {{ulaşım sembol|istanbul|M2}} {{ulaşım sembol|istanbul|U3}} |- | 30M Beşiktaş - Mecidiyeköy|| Ring - Tek bilet|| Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 31 Kuyumcukent - Yenikapı || Standart || Avrupa|| {{ulaşım sembol|istanbul|Marmaray}} {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M1b}} {{ulaşım sembol|istanbul|M2}} {{ulaşım sembol|istanbul|T4}} {{ulaşım sembol|istanbul|U3}} |- | 31E Kuyumcukent - Eminönü || Standart || Avrupa|| {{ulaşım sembol|istanbul|m1a}} {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 31Y Toki Ayazma - Yenikapı || Standart || Avrupa|| {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M1b}} {{ulaşım sembol|istanbul|M2}} {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|U3}} {{ulaşım sembol|istanbul|Marmaray}} |- | 32 Cevatpaşa - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 32A Cevatpaşa - Aksaray|| Standart || Avrupa|| |- | 32M Cevatpaşa - Mecidiyeköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 32T Cevatpaşa - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | 33 Giyimkent / Turgutreis - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 33B Giyimkent / Birlik Mahallesi - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 33E Çiftedutlar - Eminönü || Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T4}} {{ulaşım sembol|istanbul|T5}} |- | 33ES Giyimkent - Eminönü || Standart || Avrupa||{{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 33M Giyimkent - Mecidiyeköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 33TE Turgutreis - Eminönü || Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 33TM Giyimkent / Turgutreis - Mecidiyeköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 33Y Giyimkent / Yüzyıl Mahallesi - Eminönü || Standart || Avrupa || {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 35 Kocamustafapaşa - Eminönü || Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 35C Kocamustafapaşa - Taksim || Standart || Avrupa||{{ulaşım sembol|istanbul|T2}} |- | 35D Kocamustafapaşa - Balat || Standart || Avrupa|| |- | 36 Gaziosmanpaşa Yenimahalle Metrosu - Aksaray || Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | 36A Cebeci - Vezneciler || Standart || Avrupa|| |- | 36AS Taşoluk Peronlar - Sefaköy Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 36AY Arnavutköy - Yenibosna Metrosu|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M9}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 36B Bolluca - Cebeci|| Standart || Avrupa|| |- | 36C Arnavutköy - Cebeci|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M11}} |- | 36CB Cebeci - Habipler|| Standart || Avrupa|| |- | 36CY Cebeci - Yenibosna Metrosu|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M9}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 36D Arnavutköy - Deliklikaya || Standart || Avrupa|| {{ulaşım sembol|istanbul|M11}} |- | 36ES Sultangazi Peron - Aksaray|| Standart || Avrupa|| |- | 36F Cebeci - Fenertepe/Olimpiyatköy Metrosu|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M9}} |- | 36G Sultangazi Peron - Beşiktaş|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 36HT Arnavutköy Fatih Mahallesi - Haraççı - Cebeci|| Standart || Avrupa|| |- | 36KE Gaziosmanpaşa Yenimahalle Metrosu - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}}{{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 36L Mescidi Selam - 4. Levent|| Standart || Avrupa|| |- | 36M Cebeci - Mecidiyeköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 36S Habipler - Gaziosmanpaşa Yenimahalle Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | 36TC Taşoluk Peronlar - Cebeci|| Standart || Avrupa|| |- | 36V Yayla - Vezneciler|| Standart || Avrupa|| |- | 36Y Taşoluk Peronlar - Mahmutbey Metrosu - Yenikapı Marmaray|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M1b}} {{ulaşım sembol|istanbul|M2}} {{ulaşım sembol|istanbul|U3}} {{ulaşım sembol|istanbul|M3}}{{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|M11}} {{ulaşım sembol|istanbul|T4}} {{ulaşım sembol|istanbul|Marmaray}} |- | 36YS Yassıören - Arnavutköy || Standart || Avrupa|| |- | 36Z Cebeci - Zincirlikuyu|| Standart || Avrupa||{{ulaşım sembol|istanbul|Metrobüs}} |- | <S>37M Çırçır Metro - Mecidiyeköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}}</S> |- | <S>37T Çırçır Metro - Taksim|| Standart || Avrupa||{{ulaşım sembol|istanbul|T2}} {{ulaşım sembol|istanbul|M7}}</S> |- |<S> 37Y Çırçır Metro - Vezneciler|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}}</S> |- | 38 Sultangazi Peron - Aksaray|| Standart || Avrupa|| |- | 38B Sultangazi Peron - Vezneciler|| Standart || Avrupa|| |- | 38E Gaziosmanpaşa Devlet Hastanesi - Eminönü || Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- |<S> 38G Gaziosmanpaşa Devlet Hastanesi - Mecidiyeköy|| Standart || Avrupa|| </S> |- | 38H Zübeyde Hanım Mahallesi - Habipler|| Standart || Avrupa|| |- | 38Z Zübeyde Hanım Mahallesi - Aksaray|| Standart || Avrupa|| |- | 40 Rumelifeneri / Garipçe - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | 40B Sarıyer - Beşiktaş|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 40T İstinye Dereiçi - Taksim|| Standart || Avrupa||{{ulaşım sembol|istanbul|T2}} |- | 41 Sinpaş Sitesi - 4. Levent|| Standart || Avrupa || {{ulaşım sembol|istanbul|M2}} |- | 41A Ayazağa - 4. Levent|| || Standart || {{ulaşım sembol|istanbul|M2}} |- | 41AT Ayazağa - Davutpaşa Üniversitesi|| Standart || Avrupa || {{ulaşım sembol|istanbul|T4}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 41C Ayazağa - Sarıyer|| Standart || Avrupa || |- | 41E Ayazağa - Kabataş|| Standart || Avrupa|| |- | 41N Nişantaşı Üniversitesi - Maslak|| Standart || Avrupa|| |- | 41SF Sarıyer - Fatih Sultan Mehmet|| Standart || Avrupa|| |- | 41SM Ayazağa Mezarlık - 4. Levent|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 41ST Seyrantepe - Topkapı|| Standart || Avrupa|| |- | 42 Bahçeköy / Ferahevler - Zincirlikuyu|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 42K Sarıyer - Kazım Karabekir|| Standart || Avrupa|| |- | 42KT Sarıyer - Kocataş|| Standart || Avrupa|| |- | <S>42M Bahçeköy - Zincirlikuyu|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} </S> |- | 42T Bahçeköy - Baltalimanı || Standart || Avrupa|| |- | 42Z Hacıosman Metrosu - Kazım Karabekir|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m2}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 43R Rumeli Hisarüstü - Kabataş|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 46Ç Kağıthane - Eminönü || Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 46KT Kuştepe - Şişli || Standart || Avrupa|| |- | 46T Çağlayan - Topkapı || Standart || Avrupa|| |- | 47L Binevler - Hacıosman Metrosu|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 48 Göktürk - Mecidiyeköy|| Standart || Avrupa|| |- | <S>48A Göktürk - Kazlıçeşme Marmaray|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T4}} {{ulaşım sembol|istanbul|Marmaray}} {{ulaşım sembol|istanbul|B1}} {{ulaşım sembol|istanbul|U3}}</S> |- | 48C Göktürk - Çiftalan Köyü|| Standart || Avrupa|| |- | 48D Göktürk - Hacıosman Metrosu|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 48G Göktürk - Mescidi Selam|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T4}} |- | 48H Hamidiye Mahallesi - Şişli|| Standart || Avrupa|| |- | 48K Kemerburgaz - Ağaçlı Köyü|| Standart || Avrupa|| |- | 48KA Kemerburgaz - Arnavutköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m11}} |- | 48L Göktürk - 4. Levent Metrosu|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m2}} |- | 48M Akpınar Köyü - Arnavutköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 48N Emniyettepe - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | 48P Kemerburgaz - Akpınar Köyü|| Standart || Avrupa|| |- | 48S Sadabat Evleri - Şişli || Standart || Avrupa|| |- | 48T Hamidiye Mahallesi - Taksim|| Standart || {{ulaşım sembol|istanbul|T2}} |- | 48U Bahçeşehir Üniversitesi - Kemerburgaz|| Standart || Avrupa|| |- |<S> 49G Sultangazi Peron - Küçükköy - Şişli|| Standart || Avrupa|| </S> |- |<S> 49GB Sultangazi Peron - Baraj Yolu - Şişli|| Standart || Avrupa|| </S> |- | 49Y Yeşilpınar - Zincirlikuyu|| Standart || Avrupa|| |- | 50A Alibeyköy - Edirnekapı Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 50AT Cebeci - Gazi - Alibeyköy|| Standart || Avrupa|| |- | 50B Alibeyköy - Bakırköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m3}} |- | 50C Alibeyköy - Şişli|| Standart || Avrupa|| |- | 50D Alibeyköy - Ayazağa|| Standart || Avrupa|| |- | 50E Alibeyköy - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 50F Alibeyköy - 4. Levent Metrosu|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 50H Alibeyköy - Hacıosman Metrosu|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 50K Alibeyköy - Kazlıçeşme Marmaray|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T4}} {{ulaşım sembol|istanbul|Marmaray}} |- | 50M Alibeyköy - Beşiktaş|| Standart || Avrupa|| |- | 50N Alibeyköy - Taksim|| Standart || Avrupa||{{ulaşım sembol|istanbul|T2}} |- | 50P Çırçır - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 50R Alibeyköy - Cep Otogarı || Standart || Avrupa|| |- | 50S Alibeyköy - Şişli|| Standart || Avrupa|| |- | 50T Alibeyköy - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | 50V Alibeyköy - Vezneciler|| Standart || Avrupa|| |- | 50Y Alibeyköy - Yenikapı|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Marmaray}} {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M1b}} {{ulaşım sembol|istanbul|M2}} {{ulaşım sembol|istanbul|U3}} |- | 50Z Alibeyköy - Zincirlikuyu|| Standart || Avrupa||{{ulaşım sembol|istanbul|Metrobüs}} |- | 54Ç Çıksalın - Taksim || Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | 54E Okmeydanı - Eminönü || Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 54HŞ Hasköy - Şişli|| Standart || Avrupa|| |- | 54K Hasköy - Kulaksız - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | 54KT Kaptanpaşa - Dolapdere / Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | 54ÖR Örnektepe - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | 54P Piyalepaşa Mahallesi - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | 54T Sabadat Evleri / Talatpaşa - Taksim|| Standart || Avrupa||{{ulaşım sembol|istanbul|T2}} |- | 54TE Sabadat Evleri / Talatpaşa - Eminönü || Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 55 Gaziosmanpaşa - Şişli || Standart || Avrupa|| |- | 55B Gaziosmanpaşa - Beşiktaş|| Standart || Avrupa|| |- | 55ET Gaziosmanpaşa - Beşiktaş|| Standart || Avrupa|| |- | 55T Gaziosmanpaşa - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | 55Y Kağıthane Garaj - Yenibosna Metrosu|| Standart || Avrupa|| |- | 57UL Beşiktaş - Kuruçeşmeüstü|| Standart || Avrupa|| |- | 58A Poligon Mahallesi - Kabataş|| Standart || Avrupa|| {{ulaşım sembol|istanbul|t1}} {{ulaşım sembol|istanbul|t5}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 58N Fatih Sultan Mehmet - Kabataş|| Standart || Avrupa|| {{ulaşım sembol|istanbul|t1}} {{ulaşım sembol|istanbul|t5}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 58UL Ulus Mahallesi - Kabataş|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 59A Rumeli Hisarüstü - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | 59CH Beşiktaş Arnavutköy Mahallesi - Mecidiyeköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 59HS Hacıosman Metrosu - Sarıyer Kampüsü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 59K Karayolları - Şişli|| Standart || Avrupa|| |- | 59N Fatih Sultan Mehmet - Şişli || Standart || Avrupa|| |- | 59R Rumeli Hisarüstü - Şişli|| Standart || Avrupa|| |- | 59RH Rumeli Hisarüstü - Hacıosman Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 59RK Rumeli Hisarüstü - Sarıtepe Kampüs|| Standart || Avrupa|| |- | 59RS Rumeli Hisarüstü - Sarıyer|| Standart || Avrupa|| |- | 62 Kağıthane - Gültepe - Kabataş|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 62G Emniyettepe / Hamidiye - Beşiktaş|| Standart || Avrupa|| |- | 62H Emniyettepe - Hacıosman Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 63 Kağıthane - Kabataş|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 64 Emniyettepe / Çeliktepe - Şişli|| Standart || Avrupa|| |- | 65A Seyrantepe - Kağıthane Garaj|| Standart || Avrupa|| |- | 65G Kağıthane Garaş / Gültepe - Şişli|| Standart || Avrupa|| |- | 66 Gülbağ - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 66Z Zincirlidere - Şişli|| Standart || Avrupa|| |- | 70 Kurtuluş / Dolapdere - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 70FE Feriköy - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 70FY Feriköy - Yenikapı|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Marmaray}} {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M1b}} {{ulaşım sembol|istanbul|M2}} {{ulaşım sembol|istanbul|U3}} |- | 70KE Kurtuluş - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 70KY Kurtuluş - Yenikapı|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Marmaray}} {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M1b}} {{ulaşım sembol|istanbul|M2}} {{ulaşım sembol|istanbul|U3}} |- | 71T Ataköy - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | 72T Yeşilköy - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | 72YT Cerrahpaşa Tıp Fakültesi Acil Durum Hastanesi - Yenikapı Etkinlik Alanı|| Standart || Avrupa|| |- | 73 Taksim - Yenibosna Metrosu|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M9}} {{ulaşım sembol|istanbul|T2}} |- | 73B Florya - Bakırköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 73F Florya - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} {{ulaşım sembol|istanbul|T2}} |- | 73H Cerrahpaşa Tıp Fakültesi Acil Durum Hastanesi - Yenibosna Metrosu|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M9}} |- | 74 Hilton - Eminönü || Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 74A Gayrettepe - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 75O Esenler Otogar - Mecidiyeköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 76 Beylikdüzü - Bakırköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|M34}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 76A Kiptaş Vaditepe - Yenikapı || Standart || Avrupa|| |- | 76B Avcılar Cihangir Mahallesi - Avcılar Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 76BA Avcılar Metrobüs - Büyükçekmece|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | <S>76C Büyükçekmece - Bakırköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|Metrobüs}} </S> |- | 76D Bahçeşehir / Esenkent - Taksim|| Çift katlı|| Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} {{ulaşım sembol|istanbul|T2}} |- | 76G Gürpınar Mahallesi - Avcılar Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 76GM Gürpınar - Beylikdüzü E5 Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 76O Avcılar Cihangir Mahallesi - Esenler Otogar|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} |- | 76TM Tepekent - Haramidere|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 76V Kiptaş Vaditepe - Bakırköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M1b}} {{ulaşım sembol|istanbul|M2}} {{ulaşım sembol|istanbul|m3}} {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|M9}} {{ulaşım sembol|istanbul|U3}} {{ulaşım sembol|istanbul|T4}} {{ulaşım sembol|istanbul|Marmaray}} |- | 77 Yenikapı - Şişli || Standart || Avrupa|| |- | 77A Vezneciler - Şişli || Standart || Avrupa|| |- | 77Ç Çıksalım - Şişli || Standart || Avrupa|| |- | 78 Metrokent - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 78B Fenertepe Peron / Başakşehir - Yenibosna Metrosu|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M9}} |- | 78C Fenertepe Peron - Yunus Emre Mahallesi|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} |- | 78E Fenertepe Peron / Başakşehir - Kayabaşı Kiptaş Konutları|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} |- | 78F Fenertepe Peron / Başakşehir - Metrokent|| Standart || Avrupa||{{ulaşım sembol|istanbul|M3}} |- | 78G Fenertepe Peron / Başakşehir - Kayaşehir|| Standart || Avrupa||{{ulaşım sembol|istanbul|M3}} |- | 78H Fenertepe Peron / Başakşehir - Eminönü || Standart || Avrupa|| {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 78K Fenertepe Peron / Başakşehir - Kanuni Sultan Süleyman Eğitim ve Araştırma Hastanesi|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} |- | 78Ş Fenertepe Peron / Başakşehir - Kayaşehir|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} |- | 78ZB Fenertepe Peron / Başakşehir - Zeytinburnu|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 79B Kayaşehir - Bakırköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|M3}} |- | 79C Kayaşehir - Cebeci|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} |- | 79E Kayaşehir - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T4}} {{ulaşım sembol|istanbul|T5}} {{ulaşım sembol|istanbul|F2}} |- | 79F Kayaşehir - Yenibosna|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M3}} |- | 79FY Fenertepe Peronlar - Yenibosna|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M3}} |- | 79G Kayaşehir - Zeytinburnu|| Standart || Avrupa||{{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 79GE Güvercintepe - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|T1}} {{ulaşım sembol|istanbul|T5}} |- | 79K Kayaşehir - Yenibosna|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M9}} |- | 79KM Kayaşehir - Mecidiyeköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} {{ulaşım sembol|istanbul|M3}} |- | 79KT Kayaşehir - Tekstilkent Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|M3}} |- | 79Ş Şahintepe İGTOT - Topkapı|| Standart || Avrupa|| |- | 79T Kayaşehir - Topkapı|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} |- | 79Y Kayaşehir - Yenibosna|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|M9}} |- | 80 Kazlıçeşme Marmaray - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Marmaray}} |- | 80T Kazlıçeşme Marmaray - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|Marmaray}} {{ulaşım sembol|istanbul|T2}} |- | 82 Kuyumcukent - Eminönü|| Standart || Avrupa|| |- | 85C Davutpaşa YTÜ - Kazlıçeşme Marmaray|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T4}} {{ulaşım sembol|istanbul|Marmaray}} |- |<S> 85T Esenler Metro - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M1B}} {{ulaşım sembol|istanbul|T2}}</S> |- | 86V Eyüpsultan - Vezneciler|| Standart || Avrupa|| |- | 87 Topkapı / Edirnekapı - Taksim|| Standart || Avrupa||{{ulaşım sembol|istanbul|T2}} |- | 88 Sultangazi Peronlar - Topkapı|| Standart || Avrupa|| |- | 88A Sultangazi Peronlar - Yenikapı Etkinlik Alanı|| Standart || Avrupa|| |- | 89 Kanuni Sultan Eğitim ve Araştırma Hastanesi / Atakent - Aksaray|| Standart || Avrupa|| |- | 89A Kanuni Sultan Eğitim ve Araştırma Hastanesi - Zeytinburnu|| Standart || Avrupa|| |- | 89B Küçükçekmece - Aksaray|| Standart || Avrupa||{{ulaşım sembol|istanbul|Metrobüs}} |- | 89BS Bezdirganbahçe TOKİ - Sefaköy Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 89C İETT İkitelli Garajı / Başakşehir 4. Etap - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | 89F Küçükçekmece - İstoç|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 89K Kanuni Sultan Eğitim ve Araştırma Hastanesi - Aksaray|| Standart || Avrupa|| |- | 89M Mehmet Akif Mahallesi - Zeytinburnu|| Standart || Avrupa|| |- | 89S Küçükçekmece Söğütlüçeşme - Zeytinburnu|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 89T Kanuni Sultan Eğitim ve Araştırma Hastanesi / Atakent - Taksim|| Standart || Avrupa||{{ulaşım sembol|istanbul|T2}} |- | 89YB Kanuni Sultan Eğitim ve Araştırma Hastanesi / Atakent - Bakırköy|| Standart || Avrupa|| |- | 90 Draman - Eminönü|| Standart || Avrupa|| |- | 90B Draman - Aksaray|| Standart || Avrupa|| |- | 91E Göztepe Mahallesi - Aksaray|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | 91M Bağcılar Fatih Mahallesi Yanyol - Mecidiyeköy Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|metrobüs}} |- | 92 Bağcılar Yenimahalle Metro - Eminönü || Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} |- | 92B Bağcılar Yenimahalle Metro - Aksaray|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} |- | 92C Haznedar - Eminönü || Standart || Avrupa|| |- | 92K Bağcılar Yenimahalle Metro - Kirazlı - Aksaray|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|T4}} |- | 92M Çınar Mahallesi / Bağcılar Devlet Hastanesi - Mecidiyeköy Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|metrobüs}} |- | 92Ş Bağcılar Yenimahalle Metro - Mecidiyeköy Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|metrobüs}} |- | 92T Bağcılar Devlet Hastanesi - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | 93 Zeytinburnu - Eminönü|| Standart || Avrupa|| |- | 93M Zeytinburnu - Mecidiyeköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T4}} {{ulaşım sembol|istanbul|metrobüs}} |- | 93T Zeytinburnu - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | <S>94 İbni Sina İlköğretim Okulu - Zeytinburnu|| Standart || Avrupa|| {{ulaşım sembol|istanbul|metrobüs}}</S> |- | 97 15 Temmuz Mahallesi - Aksaray|| Standart || Avrupa||{{ulaşım sembol|istanbul|M9}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 97A Basınsitesi - Eminönü|| Standart || Avrupa|| |- | 97BT Kocasinan İtfaiye - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | 97E Mahmutbey - Kazlıçeşme Marmaray|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|Marmaray}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 97G Bağcılar Yenimahalle Metro - Aksaray|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} |- | 97GE 15 Temmuz Mahallesi / Güneşli - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 97KZ Kocasinan - Zeytinburnu|| Standart || Avrupa|| |- | 97M 15 Temmuz Mahallesi / Güneşli - Mecidiyeköy Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 98 Fenertepe - Bakırköy|| Standart || Avrupa||{{ulaşım sembol|istanbul|m3}} {{ulaşım sembol|istanbul|M9}} |- | 98A Göztepe Mahallesi - Bakırköy || Standart || Avrupa|| {{ulaşım sembol|istanbul|m3}} {{ulaşım sembol|istanbul|M7}} |- | 98AB Atakent / Halkalı - Bakırköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m3}} {{ulaşım sembol|istanbul|m9}} {{ulaşım sembol|istanbul|marmaray}} {{ulaşım sembol|istanbul|metrobüs}} |- | 98B Kuyumcukent - Bakırköy|| Standart || Avrupa||{{ulaşım sembol|istanbul|m1a}} {{ulaşım sembol|istanbul|m3}} {{ulaşım sembol|istanbul|metrobüs}} |- | 98D Bağcılar Yenimahalle Metro - Bakırköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} |- | 98E Basın Tesisleri - Bakırköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m3}} |- | 98G Giyimkent / Turgutreis - Bakırköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m3}} {{ulaşım sembol|istanbul|M7}} |- | 98H Metrokent - Bakırköy|| Standart || Avrupa|| |- | 98KM Fenertepe - Sefaköy Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M9}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 98M İstoç - Bakırköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|M7}} |- | 98MB Kanuni Sultan Eğitim ve Araştırma Hastanesi - Bakırköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m3}} |- | 98T Kanuni Sultan Eğitim ve Araştırma Hastanesi - Atakent - Bakırköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m3}} {{ulaşım sembol|istanbul|m9}} |- | 98TB Küçükçekmece Fatih Mahallesi - Bakırköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m3}} |- | 98Y Giyimkent / Yüzyıl - Bakırköy|| Standart || Avrupa||{{ulaşım sembol|istanbul|m3}} {{ulaşım sembol|istanbul|M7}} |- | 99A Gaziosmanpaşa - Eminönü|| Standart || Avrupa|| |- | 121A Beykoz - Mecidiyeköy|| 2 tam bilet - 2. boğazgeç|| Avrupa - Anadolu|| |- | 121B Kavacık - Mecidiyeköy|| 2 tam bilet - 2. boğazgeç|| Avrupa - Anadolu|| |- | 121BS Sultaniye - Mecidiyeköy|| 2 tam bilet - 2. boğazgeç|| Avrupa - Anadolu|| |- | 122C Tepeüstü - Mecidiyeköy|| 2 tam bilet - 2. boğazgeç|| Avrupa - Anadolu|| |- | 122ÇK Şahinbey - Kavacık||Standart||Anadolu|| |- | 122D Atatürk Mahallesi - Mecidiyeköy|| 2 tam bilet - 2. boğazgeç || Avrupa - Anadolu|| |- | 122M Şahinbey - Mecidiyeköy|| 2 tam bilet - 2. boğazgeç|| Avrupa - Anadolu|| |- | 122V Veysel Karani - 4. Levent Metro|| 2 tam bilet - 2. boğazgeç|| Avrupa - Anadolu|| {{ulaşım sembol|istanbul|M2}} |- | 122Y Çekmeköy - Mecidiyeköy|| 2 tam bilet - 1. boğazgeç || Avrupa - Anadolu|| |- | 129L Kozyatağı - 4. Levent Metro|| 2 tam bilet - 1. boğazgeç|| Avrupa - Anadolu|| {{ulaşım sembol|istanbul|M2}} |- | 129T Kadıköy Bostancı - Taksim|| 2 tam bilet - 1. boğazgeç|| Avrupa - Anadolu|| {{ulaşım sembol|istanbul|M8}} {{ulaşım sembol|istanbul|T2}} |- | 130A Deniz Harp Okulu - Kadıköy|| Standart || Anadolu|| |- | 130E Tuzla / Evora Konutları - Kartal Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 130Ş Şifa Mahallesi - Kadıköy|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 130ŞT Şifa Mahallesi - Tavşantepe Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 130T Aydınlı Toki - Tuzla|| Standart || Anadolu|| |- | <S>131 Sultanbeyli - Ümraniye|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M5}}</S> |- | <S> 131A Ahmet Yesevi Mahallesi - Ümraniye|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M5}}</S> |- |<S> 131B Mimar Sinan Mahallesi - Ümraniye|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M5}}</S> |- | <S>131H Hilal Konutları - Tepeüstü|| Standart || Anadolu|| </S> |- | 131K Yeşiltepe - Kartal|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 131SB Sultanbeyli Merkez - Sultanbeyli Yeni Devlet Hastanesi|| Standart || Anadolu|| |- | 131T Atatürk Mahallesi / Taşdelen - Ümraniye|| Standart || Anadolu|| |- | <S>131Ü Sultanbeyli - Estergon Caddesi - Ümraniye|| Standart || Anadolu|| </S> |- | 131V Veysel Karani - Kartal|| Standart || Anadolu|| |- | 131Y Çekmeköy - Kartal|| Standart || Anadolu|| |- | 131YS Sultanbeyli Devlet Hastanesi - Yenidoğan - Ümraniye|| Standart || Anadolu|| |- | 132 Akfırat - Kartal|| Standart || Anadolu|| |- | 132A Çamlık - Kartal|| Standart || Anadolu|| |- | 132B Akfırat evleri - Kartal Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 132C Sultanbeyli Gölet - Kartal|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 132ÇK Çekmeköy - Kartal|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M5}} |- | 132D Kurtdoğmuş - Kartal|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 132E Emirli / Kurnaköy - Kartal|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 132F Esenler - Tavşantepe Metrosu - Kartal|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 132G Gözdağ - Pendik Yüksek Hızlı Tren / Pendik|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}}{{ulaşım sembol|türkiye|yüksekhızlıtren}} |- | 132K Yenişehir - Kartal|| Standart || Anadolu|| |- | 132M Sultanbeyli Mimarsinan - Kartal Metro / Cevizli|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 132N Nişantepe - Kartal|| Standart || Anadolu|| |- | 132P Veysel Karani - Sultanbeyli - Kartal|| Standart || Anadolu|| |- | 132S Yenidoğan - Kartal|| Standart || Anadolu|| |- | 132SB Sultanbeyli Devlet Hastanesi - Marmara Üniversitesi Başıbüyük Kampüsü || Standart || Anadolu|| |- | 132T Tepeören - Kartal Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 132V Yenidoğan - Pendik Yüksek Hızlı Tren|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} {{ulaşım sembol|türkiye|yüksekhızlıtren}} |- | 132Y Yenidoğan - Marmara Üniversitesi Başıbüyük Kampüsü|| Standart || Anadolu|| |- | 132YM Yeşiltepe - Marmara Eğitim Köyü|| Standart || Anadolu|| |- | 132Z Sultanbeyli Uzundere - Kartal|| Standart || Anadolu|| |- | 133A Aydınlı Bahçeler - Kartal|| Standart || Anadolu|| |- | 133AK Tepeören - Kartal|| Standart || Anadolu|| |- | 133F Acarlar Sitesi - Maltepe - Cevizli|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 133G Gülensu Mahallesi - Kartal|| Standart || Anadolu|| |- | 133GP Göçbeyli - Kartal|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 133K Kurtköy Sanayi Mahallesi - Kartal|| Standart || Anadolu|| |- | 133KT Aydınlı Toki - Kartal|| Standart || Anadolu|| |- | 133M Tuzla OSB - Kafkale Tuzla Sahili|| Standart || Anadolu|| |- | 133N Maltepe Üniversitesi - Maltepe Metro / Kartal|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 133P Tepeören - Tuzla|| Standart || Anadolu|| |- | 133Ş Çağdaşkent / Şekerpınar - Kartal|| Standart || Anadolu|| |- | 133T Deniz Harp Okulu - Maltepe Peronlar|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | 133TM Aydınlı Toki / Mimarsinan Mahallesi - Tuzla|| Standart || Anadolu|| |- | 133Ü Gazi Bulvarı / Orhanlı - Pendik Metro / Yüksek Hızlı Tren / Pendik|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} {{ulaşım sembol|türkiye|yüksekhızlıtren}} |- | 134 Viaport / Hilal Konutları - Kartal|| Standart || Anadolu|| |- | 134CK Şeyhli Mezarlık / Cezaevi - Kartal|| Standart || Anadolu|| |- | 134GK Uğurmumcu / Gümüşpınar - Kartal|| Standart || Anadolu|| |- | 134UK Uğurmumcu - Kartal|| Standart || Anadolu|| |- | 134YK Uğurmumcu - Yakacık - Kartal|| Standart || Anadolu|| |- | 135 Poyrazköy - Sultaniye|| Standart || Anadolu|| |- | 135A Acarkent - Kavacık|| Standart || Anadolu|| |- | 135G Tokatköy - Çavuşbaşı|| Standart || Anadolu|| |- | 135K Karlıtepe - Ortaçeşme|| Standart || Anadolu|| |- | 135T Arkboyu - Tokatköy|| Standart || Anadolu|| |- | 136 Değirmendere - Sultaniye|| Standart || Anadolu|| |- | 136B Alman Üniversitesi - Kavacık / Hekimbaşı|| Standart || Anadolu|| |- | 136R Riva - Kavacık|| Standart || Anadolu|| |- | 136Z Ali Bahadır / Zerzevat - Sultaniye|| Standart || Avrupa|| |- | 137 Sultaniye / Beykoz - Riva / Cumhuriyet Köyü|| Standart || Anadolu|| |- | 138 Cumhuriyet Köyü / Ömerli Madenler - Ümraniye || Standart || Anadolu|| |- | 138B Göllü / Ömerli Madenler - Ümraniye|| Standart || Anadolu|| |- | 139 Şile - Üsküdar|| VIP Kademeli|| Anadolu|| |- | 139A Üsküdar - Şile / Ağva|| VIP Kademeli|| Anadolu|| |- | 139D Bereket Sitesi - Darlık|| Bölgesel|| Anadolu|| |- | 139S Üsküdar - Sofular Köyü|| Bölgesel|| Anadolu|| |- | 139T Tepeüstü - Şile|| Bölgesel|| Anadolu|| |- | 141A Kanuni Sultan Eğitim ve Araştırma Hastanesi - Atakent|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | 141K Kanuni Sultan Eğitim ve Araştırma Hastanesi - Mahmutbey Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|n7}} |- | 141M Kanuni Sultan Eğitim ve Araştırma Hastanesi - Mecidiyeköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 142 Boğazköy - Avcılar Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 142A Boğazköy - Avcılar Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 142B Kanuni Sultan Eğitim ve Araştırma Hastanesi - Büyükçekmece|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 142E Esenyurt Kiptaş - Aksaray|| Standart || Avrupa|| |- | 142ES İstanbul Üniversitesi Cerrahpaşa Kampüsü - Avcılar Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 142F Yeşilkent - Avcılar|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 142K Esenyurt Kiptaş - Avcılar|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 142T Kanuni Sultan Eğitim ve Araştırma Hastanesi - Tüyap Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 143 Küçükçekmece - İstoç|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} |- | 144 Bahçekent - Haramidere|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 144A Deliklikaya - Avcılar Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 144B Deliklikaya - Yeşilbayır Köyü - Gürpınar Su Ürünleri Hali|| Standart || Avrupa|| |- | 144H Deliklikaya - Haramidere|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 144K Ömerli Kiptaş Konutları - Esenkent|| Standart || Avrupa|| |- | 144M Deliklikaya - Mahmutbey Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|M9}} |- | 145 İhlas Marmara - Küçükçekmece|| Standart || Avrupa|| {{ulaşım sembol|istanbul|marmaray}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 145M Beykent - Taksim ||Çift katlı - Ekspress || Avrupa || {{ulaşım sembol|istanbul|T2}} {{ulaşım sembol|istanbul|T4}} |- | 145T Beylikdüzü - Taksim|| Çift katlı - Ekspress|| Avrupa|| {{ulaşım sembol|istanbul|T2}} {{ulaşım sembol|istanbul|T4}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 146 Boğazköy - Bakırköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m3}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 146A Esenkent - Birlik Mahallesi|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | 146B Metrokent - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | 146F Fenertepe - Metrokent|| Standart || Avrupa||{{ulaşım sembol|istanbul|M3}} |- | 146K Fenertepe - Birlik Mahallesi|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|M9}} |- | 146M Metrokent - Mecidiyeköy Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 146T Boğazköy - Yenikapı|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M1a}} {{ulaşım sembol|istanbul|M1b}} {{ulaşım sembol|istanbul|M2}} {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|M9}} {{ulaşım sembol|istanbul|U3}} {{ulaşım sembol|istanbul|T4}} {{ulaşım sembol|istanbul|Marmaray}} |- | 147 İGTOT - Avcılar Cihangir Mahallesi|| Standart || Avrupa|| |- | 150 Hacıosman Metro - Rumelifeneri|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 151 Hacıosman Metro - Kilyos / Demirciköy || Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 152 Hacıosman Metro - Zekeriyaköy / Kısırkaya || Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 153 Bahçeköy - Sarıyer|| Standart || Avrupa|| |- | 154 Hacıosman Metro - Koç Üniversitesi Kampüsü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | 251 Pendik - Mecidiyeköy || 2 tam bilet 1. boğazgeç|| Avrupa|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} |- | 256 Yeditepe Üniversitesi / Ataşehir - Taksim|| 2 tam bilet 1. boğazgeç|| Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | 300 Silivri - Tüyap|| Bölgesel - Kademeli || Avrupa|| |- | 300B Silivri Büyükçavuşlu - Tüyap|| Bölgesel - Kademeli|| Avrupa|| |- | 300C Silivri Çanta - Tüyap|| Bölgesel - Kademeli|| Avrupa|| |- | 300D Değirmenköy - Tüyap|| Bölgesel - Kademeli|| Avrupa|| |- | 300G Gümüşkaya - Tüyap|| Bölgesel - Kademeli|| Avrupa|| |- | 300M Silivri Mimarsinan / Kavaklı - Tüyap|| Bölgesel - Kademeli|| Avrupa|| |- | 300S Silivri Cezaevi - Tüyap|| Bölgesel - Kademeli|| Avrupa|| |- | 303A Silivri - Avcılar Metrobüs|| Bölgesel - Kademeli|| Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 303B Silivri Cezaevi - Esenler Otogar|| Bölgesel - Kademeli|| Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 336 Arnavutköy - Eminönü|| Standart || Avrupa|| |- | 336A Balaban - Mescid-i Selam|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T4}} |- | 336E Sultançifliği - Eminönü|| Standart || Avrupa|| |- | 336G Taşoluk Peronlar - Tekstilkent Metro || Standart || Avrupa|| {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|M11}} {{ulaşım sembol|istanbul|T4}} |- | 336H Hadımköy Akpınar - Mescid-i Selam|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T4}} |- | 336K Yeniköy - Mescid-i Selam|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T4}} |- | 336M Arnavutköy - Mecidiyeköy Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 336Y Cebeci Peronlar - Yenikapı Marmaray|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Marmaray}} {{ulaşım sembol|istanbul|b1}} {{ulaşım sembol|istanbul|t4}} |- | 400A Beykent - Haramidere|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 401 Çatalca - Avcılar Metrobüs||Bölgesel - Kademeli|| Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 401T Tepekent - Haramidere|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 402 Binkılıç - Çatalca || Bölgesel - Kademeli|| Avrupa|| |- | 403 Çiflikköy - Çatalca|| Bölgesel - Kademeli|| Avrupa|| |- | 404 Yalıköy - Çatalca|| Bölgesel - Kademeli|| Avrupa|| |- | 418 Hadımköy Akpınar - Haramidere|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 429A Kıraç - Avcılar Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 448 Güzelce - Haramidere|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 458 Yedikardeşler Camii / Gürpınar - İstanbul Üniversitesi Cerrahpaşa Avcılar Kampüsü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | 500L Levent - Cevizlibağ|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} {{ulaşım sembol|istanbul|T4}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 500L 4. Levent - Cevizlibağ (yakında)|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} {{ulaşım sembol|istanbul|T4}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 500T Tuzla Şifa Mahallesi - Cevizlibağ / Zeytinburnu Çırpıcı|| 2 tam bilet 2. boğazgeç|| Avrupa - Anadolu|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|T4}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 500T Tuzla Şifa Mahallesi - 4. Levent (yakında)|| 2? bilet 2. boğazgeç - 500L aktarması ücretsiz|| Avrupa - Anadolu|| {{ulaşım sembol|istanbul|M2}} {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 522 Alemdağ - Mecidiyeköy Mezarlık || 2 tam bilet 1. boğazgeç|| Avrupa Anadolu|| {{ulaşım sembol|istanbul|M2}}{{ulaşım sembol|istanbul|M7}} |- | 522ST Sultanbeyli - Mecidiyeköy Mezarlık|| 2 tam bilet 1. boğazgeç||Avrupa - Anadolu || {{ulaşım sembol|istanbul|M2}}{{ulaşım sembol|istanbul|M7}} |- | 559C Rumeli Hisarüstü - Taksim || Standart || Avrupa || {{ulaşım sembol|istanbul|T2}} |- | 622 Yenidoğan - Mecidiyeköy Mezarlık|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M2}}{{ulaşım sembol|istanbul|M7}} |- | BM4 Kavaklı - Haramidere Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | BM5 Sahil Mahallesi - Haramidere|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | BM6 Beylikdüzü - Haramidere|| Standart || Avrupa||{{ulaşım sembol|istanbul|Metrobüs}} |- | BM7 Haramidere - Yakuplu - Beylikdüzü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | BM8 Gürpınar - Adnan Kahveci Mahallesi - Cumhuriyet Mahallesi Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | BN1 Halkalı - Eminönü|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Marmaray}} |- | ÇM41 Çekmeköy - Necip Fazıl|| Standart || Anadolu|| |- | ÇM42 Şahinbey - Çekmeköy Metro - Emek|| Standart || Anadolu|| |- | ÇM43 Atatürk Mahallesi - Necip Fazıl|| Standart || Anadolu|| |- | ÇM44 Nişantepe - Çekmeköy Metro|| Standart || Anadolu|| |- | D1 Tepeüstü - Polonezköy / Cumhuriyet Köyü || Yaz Tarifeli - Standart|| Anadolu|| |- | DS1 Dudullu Organize Sanayi Bölgesi - Dudullu Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M5}} {{ulaşım sembol|istanbul|M8}} |- | DS2 Dudullu Organize Sanayi Bölgesi - Dudullu Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M5}} {{ulaşım sembol|istanbul|M8}} |- | DT1 Ortaköy - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | DT2 Ortaköy - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | E-56 Ispartakule - Taksim|| Çift katlı - 2 tam bilet || Avrupa|| {{ulaşım sembol|istanbul|T4}} |- | E-57 Esenkent - Bahçeşehir - Bakırköy || Çift katlı - 2 tam bilet|| Avrupa|| {{ulaşım sembol|istanbul|M3}} |- | E-58 Bahçeşehir - Mecidiyeköy|| Çift katlı - 2 tam bilet || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | E-59 Boğazköy - Taksim|| Çift katlı - 2 tam bilet|| Avrupa|| {{ulaşım sembol|istanbul|T2}} {{ulaşım sembol|istanbul|T4}} |- | EL2 Emirgan - 4. Levent|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | EM1 Eminönü - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | EM2 Eminönü - Taksim|| Standart || Avrupa|| {{ulaşım sembol|istanbul|T2}} |- | ER1 Kadıköy - Erenköy|| Ring - Tek bilet|| Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | ER2 Kadıköy - Erenköy|| Ring - Tek bilet|| Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | ES1 Esenyurt Belediye - Beylikdüzü Devlet Hastanesi / Beykent|| Standart || Avrupa|| |- | ES2 Tüyap - Akçaburgaz|| Standart || Avrupa|| |- | ES3 Esenyurt Devlet Hastanesi - Haramidere || Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | ES4 Esenyurt Kiptaş 4. Etap - Haramidere|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | FB1 Kadıköy - Fenerbahçe|| Ring - Tek bilet|| Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | FB2 Kadıköy - Fenerbahçe|| Ring - Tek bilet|| Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | HM1 Hacıosman Metro - 4. Levent Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | HM2 Konaklar - 4. Levent Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M2}} |- | HM3 Hürriyet - Şişli|| Standart || Avrupa|| |- | HM4 Levent Basın Sitesi - Şişli|| Standart || Avrupa|| |- | HT1 Kanuni Sultan Eğitim ve Araştırma Hastanesi - Üçyüzlü Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m1a}} |- | HT5 Kocatepe - Nur Sitesi|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m1a}} {{ulaşım sembol|istanbul|m1b}} |- | HT6 Batışehir - Kocatepe|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m1a}} {{ulaşım sembol|istanbul|m1b}} |- | HT10 Batışehir - Yenibosna Metro / Kuyumcukent|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M1A}} {{ulaşım sembol|istanbul|m1b}} {{ulaşım sembol|istanbul|M2}} {{ulaşım sembol|istanbul|m3}}{{ulaşım sembol|istanbul|M9}} {{ulaşım sembol|istanbul|Metrobüs}} |- | HT11 Giyimkent - Tekstilkent Metro / Kuyumcukent || Standart || Avrupa|| {{ulaşım sembol|istanbul|M1A}} {{ulaşım sembol|istanbul|M2}} {{ulaşım sembol|istanbul|m3}} {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|M9}} {{ulaşım sembol|istanbul|Metrobüs}} |- | HT12 Güneşli Basın Tesisler - Yenibosna Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M1A}} {{ulaşım sembol|istanbul|M9}} {{ulaşım sembol|istanbul|Metrobüs}} |- | HT13 Bölge İdare Mahkemesi / Bağcılar Yenimahalle Metro - Zeytinburnu|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M1A}} {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|Metrobüs}} |- | HT18 Hadımköy Akpınar Peronlar / İstanbul Üniversitesi Cerrahpaşa Kampüsü - Tüyap|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | HT19 İstanbul Üniversitesi Cerrahpaşa Kampüsü - Tüyap|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | HT20 Şahintepesi / İGTOT - Yenibosna Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m1a}} {{ulaşım sembol|istanbul|m9}} |- | HT29 Kıraç Yolu - Avcılar Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | HT48 Arel Üniversitesi - Tepekent - Avcılar Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|Metrobüs}} |- | K1 Kağıthane - Seyrantepe - Hamidiye Etfal|| Standart || Avrupa|| |- | K2 Kağıthane - Seyrantepe - Hamidiye Etfal|| Standart || Avrupa|| |- | K3 Kağıthane Garaj - Vadi İstanbul|| Standart || Avrupa|| |- | KÇ2 Kanuni Sultan Eğitim ve Araştırma Hastanesi - Kartaltepe / Yenibosna Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m1a}} {{ulaşım sembol|istanbul|m9}} |- | KM2 Kavakpınar - Tavşantepe|| Standart || Anadolu||{{ulaşım sembol|istanbul|M4}} |- | KM3 Uğurmumcu - Yakacık Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | KM10 Aydınlı TOKİ / İçmeler - Pendik Metro / Yüksek Hızlı Tren - Kartal || Standart || Anadolu|| {{ulaşım sembol|türkiye|YHT}} {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} {{ulaşım sembol|türkiye|yüksekhızlıtren}} |- | KM11 Aydınlı TOKİ - Pendik Köprü|| Standart || Anadolu|| |- | KM12 Deniz Harp Okulu - Kartal Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} |- | KM13 Tuzla - Pendik Metro / Yüksek Hızlı Tren - Pendik|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}}{{ulaşım sembol|türkiye|yüksekhızlıtren}} |- | KM14 Ahmet Yesevi - Tavşantepe Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | KM15 Yenişehir - Kartal Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | KM17 Tuzla Emlak Konutları - Tavşantepe Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M5}} |- | KM18 Sabancı Üniversitesi / Medeniyet Üniversitesi - Kurtköy Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | KM21 Hilal Konutları - Kartal Metro / Cevizli|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | KM23 Kavakpınar - Kartal Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | KM23 Kavakpınar / Marmara Hastanesi - Pendik Metro / Yüksek Hızlı Tren / Pendik|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} {{ulaşım sembol|türkiye|yüksekhızlıtren}} |- | KM24 Kavakpınar - Pendik Metro / Yüksek Hızlı Tren / Pendik|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} {{ulaşım sembol|türkiye|yüksekhızlıtren}} |- | KM25 Yenişehir - Cevizli|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | KM26 Çamlık Mahallesi - Pendik Metro / Yüksek Hızlı Tren / Pendik || Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} {{ulaşım sembol|türkiye|yüksekhızlıtren}} |- | KM27 Yenişehir - Pendik Metro / Yüksek Hızlı Tren / Pendik|| Standart || Anadolu||{{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} {{ulaşım sembol|türkiye|yüksekhızlıtren}} |- | KM28 Okan Üniversitesi - Kurtköy Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} |- | KM29 Yenişehir - Marmara Hastanesi - Kartal Metro / Cevizli|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | KM30 Kartal - Esenkent Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | KM31 Uğurmumcu - Kartal|| Standart || Anadolu|| |- | KM32 Kurfalı - Kartal Metro / Kartal|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | KM33 Pendik Sahil - Maltepe Esenkent Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | KM34 Kartal Metro / Topselvi - Gülsuyu Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | KM35 Uğurmumcu - Kartal|| Standart || Anadolu|| |- | KM36 Kurfalı - Cevizli|| Standart || Anadolu|| |- | KM37 Hilal Konutları - Pendik Metro / Yüksek Hızlı Tren - Pendik|| Standart || Anadolu|| {{ulaşım sembol|türkiye|yüksekhızlıtren}} |- | KM41 Marmara Üniversitesi - Küçükyalı Metro - Başıbüyük Sağlık Kampüsü|| Standart || Anadolu|| |- | KM42 Başıbüyük - Kartal|| Standart || Anadolu|| |- | KM43 Maltepe Üniversitesi - Zümrütevler - Maltepe|| Standart || Anadolu|| |- | KM44 Maltepe Üniversitesi - Siteler - Maltepe|| Standart || Anadolu|| |- | KM45 Marmara Üniversitesi / Küçükyalı Metro - Mimar Sinan Öğrenci Yurdu - Maltepe Üniversitesi|| Standart || Anadolu|| |- | KM46 Batı Alaşehir - Kozyatağı Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M8}} |- | KM47 Gülensu - Huzurevi Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | KM48 Esenkent - Huzurevi Metro|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | KM60 Veysel Karani - Kartal Metro / Cevizli|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | KM61 Paşaköy / Samandıra - Kartal|| Standart || Anadolu|| |- | KM70 Sultanbeyli - Kartal Metro - Cevizli|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M4}} |- | KM71 Yenidoğan - Cevizli|| Standart || Anadolu|| |- | KM72 Pendik / Pendik Metro / Yüksek Hızlı Tren - Sultanbeyli Devlet Hastanesi|| Standart || Anadolu|| {{ulaşım sembol|türkiye|yüksekhızlıtren}} |- | <s>KRB3 Pendik Yüksek Hızlı Tren - Kurnaköy Kurban Pazarı</s>||<s> Geçici hat</s>|| <s>Anadolu</s>||<s> {{ulaşım sembol|türkiye|yüksekhızlıtren}}</s> |- | <S>MK2 Olimpiyatköy Metro - Şehir Hastanesi|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m9}}</S> |- | MK11 Olimpiyatköy Metro - Kayaşehir - Güvercintepe|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} |- | MK12 Olimpiyatköy Metro - Güvercintepe - Kayaşehir|| Standart || Avrupa|| {{ulaşım sembol|istanbul|M3}} |- | MK13 Şahintepesi / İGTOT - Olimpiyatköy Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m9}} |- | MK14 Kiptaş Vaditepe - Olimpiyatköy Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m9}} |- | MK15 Boğazköy - Olimpiyatköy Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m9}} |- | MK16 Olimpiyatköy Metro - Küçükçekmece Metrobüs|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m9}} {{ulaşım sembol|istanbul|Metrobüs}} |- | MK19 Ayazma - Arenapark|| Standart || Avrupa|| |- | MK22 Taşoluk - Şehir Hastanesi|| Standart || Avrupa|| |- | MK31 Kanuni Sultan Eğiti ve Araştırma Hastanesi - Fenertepe || Standart || Avrupa|| |- | MK42 15 Temmuz Mahallesi - Kirazlı Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m1b}} |- | MK49 Sultangazi - Kazım Karabekir Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m7}} |- | MK50 Sultangazi - Kazım Karabekir Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m7}} |- | MK53 Sultangazi - Giyimkent / Tekstilkent Metro|| Standart || Avrupa|| {{ulaşım sembol|istanbul|m7}} |- | MK97 Kerim Çavuşpaşa - Zeytinburnu|| Standart || Avrupa|| {{ulaşım sembol|istanbul|metrobüs}} |- | MR9 Atatürk Mahallesi - Ayrılıkçeşme|| Standart || Anadolu|| {{ulaşım sembol|istanbul|m5}} {{ulaşım sembol|istanbul|marmaray}} {{ulaşım sembol|istanbul|metrobüs}} |- |<S> MR10 Zeytinburnu - Kazlıçeşme|| Standart || Avrupa|| {{ulaşım sembol|istanbul|marmaray}}</S> |- | MR11 Kazlıçeşme - Topkapı || Standart || Avrupa|| {{ulaşım sembol|istanbul|marmaray}} {{ulaşım sembol|istanbul|metrobüs}} |- | MR20 Yenibosna Metro - Kazlıçeşme|| Standart || Avrupa||{{ulaşım sembol|istanbul|marmaray}} |- | MR40 Kanuni Sultan Eğitim ve Araştırma Hastanesi - Atakent - Halkalı|| Standart || Avrupa|| {{ulaşım sembol|istanbul|marmaray}} |- | MR42 Halkalı - Sefaköy|| Standart || Avrupa|| {{ulaşım sembol|istanbul|marmaray}} {{ulaşım sembol|istanbul|metrobüs}} |- | MR50 Halkalı - Şehir Hastanesi|| Standart || Avrupa|| {{ulaşım sembol|istanbul|marmaray}} |- | MR51 İstanbul Üniversitesi Cerrahpaşa Kampüsü / Bahçeşehir - Halkalı|| Standart || Avrupa|| {{ulaşım sembol|istanbul|marmaray}} |- | MR52 Küçükçekmece Söğütlüçeşme - Halkalı - Şehir Hastanesi|| Standart || Avrupa|| {{ulaşım sembol|istanbul|marmaray}} |- | MR52 İGTOT - Halkalı || Standart || Avrupa|| {{ulaşım sembol|istanbul|marmaray}} |- | MR61 Güzelyalı - Tuzla Devlet Hastanesi|| Standart || Anadolu|| |- | PD1 Yenikapı - Beyazıt|| Besleme|| Avrupa|| |- | SM1 Sultanbeyli - Samandıra Merkez Metro|| Besleme|| Anadolu|| {{ulaşım sembol|istanbul|M5}} |- | SM2 Ahmet Yesevi Mahallesi - Sancaktepe Metro|| Besleme|| Anadolu|| {{ulaşım sembol|istanbul|M5}} |- | SM3 Mimarsinan Mahallesi - Profesör Doktor Feriha Öz Hastanesi|| Besleme|| Anadolu|| {{ulaşım sembol|istanbul|M5}} |- | SM4 Hilal Konutları - Samandıra Merkez Metro|| Besleme|| Anadolu|| {{ulaşım sembol|istanbul|M5}} |- | SM5 Sultanbeyli Orman Caddesi - Samandıra Merkez Metro|| Besleme|| Anadolu|| {{ulaşım sembol|istanbul|M5}} |- | SM6 Sultanbeyli Devlet Hastanesi - Samandıra Merkez Metro|| Besleme|| Anadolu|| {{ulaşım sembol|istanbul|M5}} |- | SM7 Yenidoğan - Samandıra Merkez Metro|| Besleme|| Anadolu|| {{ulaşım sembol|istanbul|M5}} |- | SM9 Kartal Metro - Samandıra Merkez Metro|| Besleme|| Anadolu|| {{ulaşım sembol|istanbul|M4}}{{ulaşım sembol|istanbul|M5}} |- | SM12 Maltepe MYO - Samandıra Merkez Metro|| Besleme|| Anadolu|| {{ulaşım sembol|istanbul|M5}} |- | SM13 Yenidoğan - Sancaktepe Metro|| Standart || Besleme|| {{ulaşım sembol|istanbul|M5}} |- | SM14 Ayazma Yaşam Vadisi - Sancaktepe Metro|| Besleme|| Anadolu|| {{ulaşım sembol|istanbul|M5}} |- | TM1 Emniyettepe - Alibeyköy|| Besleme - Metro Entegre|| Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | TM2 Hamidiye - Güzeltepe - Alibeyköy|| Besleme - Metro Entegre|| Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | TM3 Hamidiye - Kağıthane - Alibeyköy|| Besleme - Metro Entegre|| Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | TM4 Göktürk - Alibeyköy|| Besleme - Metro Entegre|| Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | TM5 Alibeyköy - 5. Levent|| Besleme - Metro Entegre|| Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | TM6 Nur Sitesi - Fabrikalar|| Besleme || Avrupa|| |- | TM7 Çırçır Metro - Alibeyköy|| Besleme - Metro Entegre|| Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | TM8 Akşemsettin - Alibeyköy|| Besleme - Metro Entegre|| Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | TM9 Gaziosmanpaşa Yenimahalle - Kazım Karabekir - Alibeyköy|| Besleme - Metro Entegre|| Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | TM10 Yeşilpınar - Alibeyköy|| Besleme - Metro Entegre|| Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | TM11 Yeşilpınar - Alibeyköy|| Besleme - Metro Entegre|| Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | TM12 Karadolap - Alibeyköy|| Besleme - Metro Entegre|| Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | TM13 Eyüpsultan - Gaziosmanpaşa - Alibeyköy|| Besleme - Metro Entegre|| Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | TM14 Gaziosmanpaşa Yenimahalle / Sarıgöl - Alibeyköy|| Besleme - Metro Entegre|| Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | TM15 Cebeci - Alibeyköy || Besleme - Metro Entegre|| Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | TM16 GOP Yenimahalle - Gaziosmanpaşa - Alibeyköy|| Besleme - Metro Entegre|| Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | TM17 Sultangazi - Gazi - Alibeyköy || Besleme - Metro Entegre|| Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | TM18 Mescid-i Selam - Alibeyköy|| Besleme - Metro Entegre|| Avrupa|| {{ulaşım sembol|istanbul|M7}} |- | TM19 Zübeyde Hanım - Alibeyköy|| Besleme - Metro Entegre|| Avrupa|| {{ulaşım sembol|istanbul|M7}} |-]] |- | UM11 Kirazlıtepe - Kısıklı Metro - Esatpaşa|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M5}} |- | UM13 Küçüksu / Yavuztürk - Ümraniye Eğitim ve Araşıtrma Hastanesi|| Standart || Anadolu|| |- | UM36 Esenevler - Bulgurlu|| Standart || Anadolu|| {{ulaşım sembol|istanbul|M5}} |- | UM40 Atatürk Mahallesi - Necip Fazıl|| Besleme|| Anadolu|| {{ulaşım sembol|istanbul|M5}} |- | UM60 Sancaktepe - Necip Fazıl|| Besleme|| Anadolu|| {{ulaşım sembol|istanbul|M5}} |- | UM61 Paşaköy - Necip Fazıl|| Besleme|| Anadolu|| {{ulaşım sembol|istanbul|M5}} |- | UM62 Yenidoğan - Necip Fazıl|| Besleme|| Anadolu|| {{ulaşım sembol|istanbul|M5}} |- | UM74 Sultanbeyli Devlet Hastanesi - Necip Fazıl - Dudullu OSB|| Besleme|| Anadolu|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M5}} |} == Depar hatlar == {| class="wikitable sortable" style="width: 100%;" |- ! Hat adı !! Güzergah !! Bölge !! Garajın bulunduğu ilçe !! Aktarma |- | AVR1 || Güvercintepe Sapağı - Adalet Sarayı|| Avrupa || Küçükçekmece || {{ulaşım sembol|istanbul|M3}} |- | AVR2 || |- | AVR3 || |- | AVR3K || |- | AND1A || |- | AND1Ş || |- | AND2K || |- | AND2S || |- | AND2Y || |} == Hava ulaşım hatları == {| class="wikitable sortable" style="width: 100%;" |- ! Hat ismi !! Tarife !! Aktarma |- | H1 Mahmutbey Metro - İstanbul Havalimanı|| 2 tam bilet - indirim geçmez|| {{ulaşım sembol|istanbul|M3}} {{ulaşım sembol|istanbul|M7}} {{ulaşım sembol|istanbul|M11}} |- | H2 Mecidiyeköy Metrobüs - İstanbul Havalimanı (Gece hattı) || 2 tam bilet - indirim geçmez|| {{ulaşım sembol|istanbul|M11}} {{ulaşım sembol|istanbul|Metrobüs}} |- | H3 Halkalı Marmaray - İstanbul Havalimanı || 2 tam bilet - indirim geçmez||{{ulaşım sembol|istanbul|M11}} {{ulaşım sembol|istanbul|Marmaray}} |- | H6 Yunus Emre - İstanbul Havalimanı || 2 tam bilet - indirim geçmez|| {{ulaşım sembol|istanbul|M11}} |- | H8 Hacıosman - İstanbul Havalimanı || 2 tam bilet - indirim geçmez|| {{ulaşım sembol|istanbul|M2}} {{ulaşım sembol|istanbul|M11}} |- | 16S Sabiha Gökçen Havalimanı - Uzunçayır / Harem Peronlar || Standart || {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} {{ulaşım sembol|istanbul|Metrobüs}} |- | 122H Sabiha Gökçen Havalimanı - 4. Levent Metro|| bölgesel - 2. boğazgeç|| {{ulaşım sembol|istanbul|M2}} {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} |- | 130H Deniz Harp Okulu - Sabiha Gökçen Havalimanı|| Standart || {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} |- | 132H Pendik / Pendik Yüksek Hızlı Tren - Sabiha Gökçen Havalimanı|| Standart || {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} {{ulaşım sembol|türkiye|yüksekhızlıtren}} |- | MR60 Pendik - Sabiha Gökçen Havalimanı|| Besleme - Tek bilet|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} |- | E3 Sabiha Gökçen Havalimanı - 4.Levent|| 3 tam bilet - indirim geçmez|| {{ulaşım sembol|istanbul|M2}} {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} |- | E10 Sabiha Gökçen Havalimanı - Kadıköy|| 2 tam bilet - indirim geçmez|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} |- | SG1 Sabiha Gökçen Havalimanı - Kadıköy (HAVABÜS)|| 7 tam bilet - abonman ve indirim geçmez || {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} {{ulaşım sembol|türkiye|yüksekhızlıtren}} |- | SG2 Sabiha Gökçen Havalimanı - Taksim (HAVABÜS)|| 11 tam bilet - abonman ve indirim geçmez||{{ulaşım sembol|istanbul|T2}} {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M10}} |- | UM73 Sabiha Gökçen Havalimanı - Necip Fazıl|| Besleme - Metro entegre - Tek bilet|| {{ulaşım sembol|istanbul|M4}} {{ulaşım sembol|istanbul|M5}} {{ulaşım sembol|istanbul|M10}} |- |} == Metrobüs hatları == {| class="wikitable sortable" style="width: 100%;" |- ! Hat ismi !! Sefer süresi |- | 34 Avcılar - Zincirlikuyu || 72 dakika |- | 34A Cevizlibağ - Kadıköy Söğütlüçeşme || 49 dakika |- | 34B Beylikdüzü - Avcılar || 24 dakika |- | 34BZ Beylikdüzü - Zincirlikuyu || 80 dakika |- | 34C Beylikdüzü - Cevizlibağ || 70 dakika |- | 34G Beylikdüzü - Kadıköy Söğütlüçeşme || 116 dakika |- | 34T Avcılar - Topkapı || 47 dakika |- | 34U Zincirlikuyu - Uzunçayır || 16 dakika |- | 34Z Zincirlikuyu - Kadıköy Söğütlüçeşme || 21 dakika rsexkt6ffmjrp79q6tfrwbxfqq8cxxx Kullanıcı:Brightt11/Oyver.js 2 5074 17860 17855 2026-10-24T16:47:17Z Brightt11 1786 yorum simgesi eklendi 17860 javascript text/javascript // Oyver - EasyVoteMeta (Author: Max) betiğinin yerelleştirilmiş versiyonu. // Orijinal betik kaynağı/Original script source: https://meta.wikimedia.org/wiki/User:Max/EasyVoteMeta.js // Bu betik, oylama/görüş bildirme sayfalarında bölüm başlıklarına "hızlı oy ver" bağlantısı ekler. // sürüm: 1.1 beta. // <nowiki> $(() => { 'use strict'; const Oyver = { version: '1.1 beta', config: { messages: { windowTitle: 'Oyver', windowButton: 'Gönder', cancelButton: 'İptal', vote: 'Görüş belirtmek için oy seçin:', comment: 'Yorum:', signatureAutoAdd: 'İmzanız otomatik olarak eklenecektir.', editSummaryLabel: 'Değişiklik özeti', showPreview: 'Önizlemeyi göster', hidePreview: 'Önizlemeyi gizle', preferencesTitle: 'Tercihler', preferencesIconSetLabel: 'Oy simgesi görünümü', preferencesIconSetCodex: 'Codex simgeleri', preferencesIconSetOnWiki: 'Wiki simgeleri', preferencesFullContextLabel: 'Önizlemede daima tüm bölümü göster', preferencesSave: 'Kaydet', preferencesButtonLabel: 'Tercihler', closeButtonLabel: 'Kapat', toggleFullSectionLabel: 'Tüm bölüm önizlemesini açın/kapatın', loadingPreviewLabel: 'Önizleme yükleniyor', submitting: 'Gönderiliyor. Lütfen internet tarayıcınızı açık tutun.', voteLinkText: 'hızlı oy ver', editError: 'Hata: Bir hata oldu. Klavyenizden F5 tuşuna basarak sayfayı yenileyin ve tekrar deneyin', confirmPossibleDouble: 'Bu başvuruda daha önce görüş yapmışsınız. Devam etmek istediğinizden emin misiniz? Lütfen önceki görüş(ler)inizin üstünü çizerek (<nowiki><s></s></nowiki> kullanarak) oyunuzu geri çekin.' }, voteOptions: { support: { label: 'Destek', template: 'Destek', icon: 'cdxIconCheck', group: 'support', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/9/94/Symbol_support_vote.svg' }, halfsupport: { label: 'Kısmi destek', template: 'Kısmi destek', icon: 'cdxIconCheck', group: 'support', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/8c/GA_candidate.svg' }, oppose: { label: 'Karşı', template: 'Karşı', icon: 'cdxIconClose', group: 'oppose', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/7/7f/Symbol_oppose_vote.svg' }, halfoppose: { label: 'Kısmi karşı', template: 'Kısmi karşı', icon: 'cdxIconClose', group: 'oppose', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/f/f5/BA_candidate.svg' }, neutral: { label: 'Tarafsız', template: 'Tarafsız', icon: 'cdxIconInfoFilled', group: 'neutral', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/89/Symbol_neutral_vote.svg' }, comment: { label: 'Yorum', template: 'Yorum', icon: 'cdxIconSpeechBubble', group: 'neutral', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/d/df/Pictogram_voting_comment_%28orange%29.svg?utm_source=commons.wikimedia.org&utm_campaign=index&utm_content=original' }, requiresComment: true, skipDoubleCheck: true } }, voteAliases: [], groupOrder: ['support', 'oppose', 'neutral'], groupHeaders: { support: ['Destek', 'Evet'], oppose: ['Karşı', 'Hayır'], neutral: ['Tarafsız', 'Yorum'] }, iconNames: ['cdxIconCheck', 'cdxIconClose', 'cdxIconInfoFilled', 'cdxIconAlert', 'cdxIconSpeechBubble', 'cdxIconEye', 'cdxIconEyeClosed', 'cdxIconSettings'], summfix: ' ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.)', pageConfigs: { 'Vikigezgin:Kullanıcı hakları adaylıkları': { type: 'auto', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [] } }, namespaceConfigs: { 8: { type: 'direct', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [], dbNames: [] }, 828: { type: 'direct', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [], dbNames: [] } } }, icons: {}, prefsPrefix: 'userjs-oyver-', prefsDefaults: { iconSet: 'codex', fullContextPreviewDefault: false }, prefsCache: {}, prefs: {}, currentPageConfig: null, init() { const currentPage = mw.config.get('wgPageName').replace(/_/g, ' '); this.currentPageConfig = this.config.pageConfigs[currentPage] || (this.config.namespaceConfigs || {})[mw.config.get('wgNamespaceNumber')]; if (!this.currentPageConfig) return; const dbNames = this.currentPageConfig.dbNames; if (dbNames && dbNames.length && !dbNames.includes(mw.config.get('wgDBname'))) return; if (mw.user.isAnon()) return; mw.loader.using(['vue', '@wikimedia/codex', 'mediawiki.util', 'mediawiki.api', 'mediawiki.user']).then((require) => { this.Vue = require('vue'); this.Codex = require('@wikimedia/codex'); this.run(); }); }, async loadIcons() { try { const api = new mw.Api(); const data = await api.get({ action: 'query', list: 'codexicons', names: this.config.iconNames.join('|'), formatversion: 2 }); this.icons = (data && data.query && data.query.codexicons) || {}; } catch (error) { this.icons = {}; } }, getPrefFullKey(key) { return this.prefsPrefix + key; }, loadPref(key) { const fullKey = this.getPrefFullKey(key); const fallback = this.prefsDefaults[key]; let value; if (Object.prototype.hasOwnProperty.call(this.prefsCache, fullKey)) { value = this.prefsCache[fullKey]; } else { try { value = window.localStorage ? window.localStorage.getItem(fullKey) : null; } catch (error) { value = null; } } if (value !== null && value !== undefined) { try { return JSON.parse(value); } catch (error) { return fallback; } } try { const serverValue = mw.user.options.get(fullKey, JSON.stringify(fallback)); return JSON.parse(serverValue); } catch (error) { return fallback; } }, loadAllPrefs() { const prefs = {}; Object.keys(this.prefsDefaults).forEach((key) => { prefs[key] = this.loadPref(key); }); return prefs; }, async savePref(key, value) { const fullKey = this.getPrefFullKey(key); const fullValue = JSON.stringify(value); this.prefsCache[fullKey] = fullValue; try { if (window.localStorage) window.localStorage.setItem(fullKey, fullValue); } catch (error) { } try { const api = new mw.Api(); await api.postWithEditToken({ action: 'options', optionname: fullKey, optionvalue: fullValue }); } catch (error) { console.error('Oyver: Hata. Tercih kaydedilemedi.', key, error); } }, async savePrefs(newPrefs) { const keys = Object.keys(newPrefs); for (const key of keys) { await this.savePref(key, newPrefs[key]); } }, async run() { this.addStyle(); this.prefs = this.loadAllPrefs(); await this.loadIcons(); const pageConfig = this.currentPageConfig; const skipPrefixes = (pageConfig.skipPrefixes || []).map((p) => p.toLocaleLowerCase('tr')); $(pageConfig.headingSelector || 'div.mw-heading.mw-heading3').each((_, heading) => { const $heading = $(heading); const editLinks = $heading.find('.mw-editsection'); const headingId = $heading.find('h1,h2,h3,h4,h5,h6').first().attr('id'); if (!headingId) return; const sectionTitle = headingId.replace(/_/g, ' '); const normalizedTitle = sectionTitle.toLocaleLowerCase('tr'); if (skipPrefixes.some((prefix) => normalizedTitle.startsWith(prefix))) return; if (pageConfig.type === 'direct') { this.handleDirectPage(editLinks, sectionTitle, $heading); } else if (pageConfig.type === 'transcluded') { this.handleTranscludedPage(editLinks, sectionTitle, $heading); } else if (pageConfig.type === 'auto') { this.handleAutoPage(editLinks, sectionTitle, $heading); } }); }, handleAutoPage(editLinks, sectionTitle, $heading) { const info = this.getTargetPageInfo(editLinks); if (!info) return; const currentPage = mw.config.get('wgPageName').replace(/_/g, ' '); if (info.targetPage === currentPage && /^\d+$/.test(info.sectionId)) { this.addVoteLink(editLinks, sectionTitle, { type: 'direct', sectionNum: parseInt(info.sectionId, 10) }, $heading); } else { this.addVoteLink(editLinks, sectionTitle, Object.assign({ type: 'transcluded' }, info), $heading); } }, handleDirectPage(editLinks, sectionTitle, $heading) { const sectionNum = this.getSectionNumber(editLinks); if (sectionNum !== -1) { this.addVoteLink(editLinks, sectionTitle, { type: 'direct', sectionNum }, $heading); } }, handleTranscludedPage(editLinks, sectionTitle, $heading) { const targetPageInfo = this.getTargetPageInfo(editLinks); if (targetPageInfo && targetPageInfo.targetPage && targetPageInfo.sectionId) { this.addVoteLink(editLinks, sectionTitle, Object.assign({ type: 'transcluded' }, targetPageInfo), $heading); } }, addStyle() { mw.util.addCSS(` .script-Oyver-link { color: var(--color-progressive, #36c); text-decoration: none; font-weight: bold; } .script-Oyver-link:hover { text-decoration: underline; } .script-Oyver-disabled { color: var(--color-disabled, #a2a9b1) !important; pointer-events: none; } .script-Oyver-divider { border: none; border-top: 1px solid var(--border-color-subtle, #eaecf0); margin: 8px 0 4px; } .script-Oyver-panel { border: 1px solid var(--border-color-base, #a2a9b1); background: var(--background-color-base, #fff); padding: 16px; margin: 8px 0 16px; } .script-Oyver-panel-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } .script-Oyver-panel-title { font-weight: bold; font-size: 15px; } .script-Oyver-version { font-size: 0.7em; font-weight: normal; color: var(--color-subtle, #54595d); vertical-align: middle; margin-left: 2px; } .script-Oyver-header-actions { display: flex; gap: 4px; } .script-Oyver-panel-body p:first-child { margin-top: 0; } .script-Oyver-option { margin: 8px 0; } .script-Oyver-option .cdx-icon { margin-right: 4px; } .script-Oyver-onwiki-icon { margin-right: 4px; vertical-align: middle; } .script-Oyver-field { margin-top: 14px; } .script-Oyver-signature-note { font-style: italic; color: var(--color-subtle, #54595d); font-size: 0.9em; margin-top: 8px; } .script-Oyver-status { margin-top: 12px; } .script-Oyver-buttons { margin-top: 16px; display: flex; gap: 8px; flex-wrap: wrap; } .script-Oyver-preview-wrapper { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border-color-subtle, #eaecf0); } .script-Oyver-preview-toolbar { display: flex; justify-content: flex-end; } .script-Oyver-preview { margin-top: 0; } .script-Oyver-preview-content { font-size: 0.95em; line-height: 1.6; } .script-Oyver-preview-loading { display: flex; align-items: center; gap: 8px; color: var(--color-subtle, #54595d); font-size: 0.88em; } .script-Oyver-prefs { border: 1px solid var(--border-color-subtle, #eaecf0); padding: 12px; } .script-Oyver-prefs-title { font-weight: bold; margin: 0 0 8px; } `); }, getSectionNumber(editLinks) { const editSectionUrl = editLinks.find('a:first').attr('href'); const sectionMatch = editSectionUrl && editSectionUrl.match(/[?&]section=(\d+)(?:&|$)/); return sectionMatch ? parseInt(sectionMatch[1], 10) : -1; }, getTargetPageInfo(editLinks) { const editSectionUrl = editLinks.find('a:first').attr('href'); if (!editSectionUrl) return null; const titleMatch = editSectionUrl.match(/[?&]title=([^&]+)/); const sectionMatch = editSectionUrl.match(/[?&]section=([^&]+)/); if (!titleMatch || !sectionMatch) return null; return { targetPage: decodeURIComponent(titleMatch[1]).replace(/_/g, ' '), sectionId: sectionMatch[1] }; }, extractContent(apiData) { const pages = apiData && apiData.query && apiData.query.pages; if (!pages || !pages[0] || !pages[0].revisions || !pages[0].revisions[0]) return null; return pages[0].revisions[0].content; }, sanitizeSectionTitle(rawTitle) { if (!rawTitle) return ''; const emojiPattern = /(\p{Extended_Pictographic}|\p{Emoji_Presentation}|[\u{1F1E6}-\u{1F1FF}]|\u{FE0F}|\u{200D})/gu; let text = rawTitle.replace(emojiPattern, ''); let cutIndex = -1; for (let i = 0; i < text.length; i++) { if (text[i] === '(') { cutIndex = i; break; } if (text[i] === '[') { if (text[i + 1] === '[') { i++; continue; } cutIndex = i; break; } } if (cutIndex !== -1) { text = text.substring(0, cutIndex); } return text.replace(/\s+/g, ' ').trim(); }, detectGroupHeaderType(line) { const match = line.match(/^;\s*(\S.*)$/); if (!match) return null; const text = match[1].toLocaleLowerCase('tr'); for (const key of this.config.groupOrder) { const words = this.config.groupHeaders[key] || []; for (const word of words) { const w = word.toLocaleLowerCase('tr'); if (text.startsWith(w) && !/^\p{L}/u.test(text.slice(w.length))) { return key; } } } return null; }, findGroupHeaders(lines) { return lines.reduce((acc, line, idx) => { const type = this.detectGroupHeaderType(line); if (type) acc.push({ type, index: idx }); return acc; }, []); }, findGroupBlockEnd(lines, headerIdx) { for (let i = headerIdx + 1; i < lines.length; i++) { if (this.detectGroupHeaderType(lines[i])) return i; } return lines.length; }, getBlockBulletChar(lines, headerIdx, blockEnd) { for (let i = headerIdx + 1; i < blockEnd; i++) { const match = lines[i].match(/^\s*([#*:]+)/); if (match) return match[1]; } return '*'; }, getGroupHeaderText(group) { const words = this.config.groupHeaders[group]; return (words && words[0]) || group; }, buildGroupedVoteText(sectionText, group, voteLine) { const lines = sectionText.split('\n'); const headers = this.findGroupHeaders(lines); const existing = headers.find((h) => h.type === group); const headerLine = ';' + this.getGroupHeaderText(group); if (existing) { const blockEnd = this.findGroupBlockEnd(lines, existing.index); let insertIdx = blockEnd; while (insertIdx - 1 > existing.index && lines[insertIdx - 1].trim() === '') insertIdx--; lines.splice(insertIdx, 0, voteLine); return lines.join('\n'); } const order = this.config.groupOrder; const orderIdx = order.indexOf(group); for (let o = orderIdx + 1; o < order.length; o++) { const laterHeader = headers.find((h) => h.type === order[o]); if (laterHeader) { lines.splice(laterHeader.index, 0, headerLine, voteLine, ''); return lines.join('\n'); } } for (let o = orderIdx - 1; o >= 0; o--) { const earlierHeader = headers.find((h) => h.type === order[o]); if (earlierHeader) { const blockEnd = this.findGroupBlockEnd(lines, earlierHeader.index); lines.splice(blockEnd, 0, '', headerLine, voteLine); return lines.join('\n'); } } let lastNonBlank = lines.length - 1; while (lastNonBlank >= 0 && lines[lastNonBlank].trim() === '') lastNonBlank--; lines.splice(lastNonBlank + 1, 0, '', headerLine, voteLine); return lines.join('\n'); }, hasExistingVote(sectionText, currentUser) { const templates = Object.values(this.config.voteOptions).filter((opt) => !opt.skipDoubleCheck).map((opt) => opt.template); const aliases = this.config.voteAliases || []; const allNames = [...templates, ...aliases].map((name) => mw.util.escapeRegExp(name)); const escapedUser = mw.util.escapeRegExp(currentUser); const pattern = new RegExp('\\{\\{(?:' + allNames.join('|') + ')\\}\\}[^\\n~]{0,300}?' + escapedUser, 'i'); return pattern.test(sectionText); }, addVoteLink(editLinks, sectionTitle, pageInfo, $heading) { const uniqueId = 'script-Oyver-launch-' + Math.random().toString(36).slice(2, 11); const mountId = 'script-Oyver-mount-' + uniqueId; const voteLink = $('<a>') .attr('id', uniqueId) .addClass('script-Oyver-link') .text(this.config.messages.voteLinkText) .attr('href', '#') .click((event) => { event.preventDefault(); if (!voteLink.hasClass('script-Oyver-disabled')) { voteLink.addClass('script-Oyver-disabled'); if (!$('#' + mountId).length) { $heading.after( $('<div>').attr('id', mountId), $('<hr>').addClass('script-Oyver-divider') ); } this.showPanel(sectionTitle, pageInfo, $heading, uniqueId, mountId); } }); editLinks.children().last().before(' | ', voteLink); }, getPreviewComponent() { const loadingLabel = this.config.messages.loadingPreviewLabel; return { components: { CdxProgressBar: this.Codex.CdxProgressBar }, props: { wikitext: { type: String, default: '' } }, data() { return { previewHtml: '', haveHtml: false, loadingLabel }; }, methods: { loadPreview(wikitextToPreview) { new mw.Api().get({ action: 'parse', formatversion: 2, title: mw.config.get('wgPageName'), text: wikitextToPreview, prop: 'text', pst: true, disablelimitreport: true, disableeditsection: true, sectionpreview: true }).then((res) => { if (res && res.parse && res.parse.text) { this.previewHtml = res.parse.text; this.haveHtml = true; } }); } }, watch: { wikitext: { immediate: true, handler(newValue) { this.previewHtml = ''; this.haveHtml = false; this.loadPreview(newValue); } } }, template: ` <div class="script-Oyver-preview"> <div v-if="!haveHtml" class="script-Oyver-preview-loading"> <cdx-progress-bar inline :aria-label="loadingLabel"></cdx-progress-bar> </div> <div v-else class="script-Oyver-preview-content" v-html="previewHtml"></div> </div> ` }; }, showPanel(sectionTitle, pageInfo, $heading, uniqueId, mountId) { const self = this; const rawLiveSectionTitle = $heading.contents().not($heading.find('.mw-editsection')).text().trim() || sectionTitle; const liveSectionTitle = self.sanitizeSectionTitle(rawLiveSectionTitle); const vueAppInstance = self.Vue.createMwApp({ components: { CdxRadio: self.Codex.CdxRadio, CdxCheckbox: self.Codex.CdxCheckbox, CdxTextArea: self.Codex.CdxTextArea, CdxTextInput: self.Codex.CdxTextInput, CdxField: self.Codex.CdxField, CdxAccordion: self.Codex.CdxAccordion, CdxMessage: self.Codex.CdxMessage, CdxButton: self.Codex.CdxButton, CdxIcon: self.Codex.CdxIcon, previewRenderer: self.getPreviewComponent() }, data() { return { version: self.version, messages: self.config.messages, sectionTitle: liveSectionTitle, uniqueId, voteOptions: Object.entries(self.config.voteOptions).map(([value, opt]) => ({ value, label: opt.label, group: opt.group, icon: self.icons[opt.icon] || null, iconUrl: opt.iconUrl, requiresComment: !!opt.requiresComment })), selectedVote: null, comment: '', showSummary: false, editSummaryInput: '/* ' + liveSectionTitle + ' */ ', summaryEdited: false, showPreview: false, submitting: false, submitError: '', showContextPreview: false, contextPreviewWikitext: '', contextPreviewLoading: false, contextPreviewDebounce: null, showPreferences: false, iconSetPref: self.prefs.iconSet, prefsIconSet: self.prefs.iconSet, prefsFullContext: self.prefs.fullContextPreviewDefault, savingPrefs: false, checkIcon: self.icons.cdxIconCheck || null, closeIcon: self.icons.cdxIconClose || null, eyeIcon: self.icons.cdxIconEye || null, eyeClosedIcon: self.icons.cdxIconEyeClosed || null, settingsIcon: self.icons.cdxIconSettings || null }; }, computed: { panelTitle() { return this.messages.windowTitle + ': ' + this.sectionTitle; }, radioName() { return 'script-Oyver-vote-' + this.uniqueId; }, prefsIconSetName() { return 'script-Oyver-prefs-iconset-' + this.uniqueId; }, selectedOption() { return this.voteOptions.find((opt) => opt.value === this.selectedVote) || null; }, defaultSummary() { const base = '/* ' + this.sectionTitle + ' */ '; return this.selectedOption ? base + this.selectedOption.label : base; }, canSubmit() { if (this.submitting || !this.selectedVote) return false; if (this.selectedOption && this.selectedOption.requiresComment && !this.comment.trim()) return false; return true; }, previewToggleIcon() { return this.showPreview ? this.eyeClosedIcon : this.eyeIcon; }, previewToggleLabel() { return this.showPreview ? this.messages.hidePreview : this.messages.showPreview; }, contextToggleIcon() { return this.showContextPreview ? this.eyeClosedIcon : this.eyeIcon; }, previewWikitext() { if (!this.selectedOption) return ''; const templateName = self.config.voteOptions[this.selectedVote].template; const trimmedComment = this.comment.trim(); let voteLine = '* {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC)'; return voteLine; } }, watch: { selectedVote() { if (!this.summaryEdited) { this.editSummaryInput = this.defaultSummary; } if (this.showContextPreview) { this.refreshContextPreview(); } }, comment() { if (!this.showContextPreview) return; clearTimeout(this.contextPreviewDebounce); this.contextPreviewDebounce = setTimeout(() => { this.refreshContextPreview(); }, 400); } }, methods: { cancelPanel() { if (this.submitting) return; vueAppInstance.unmount(); $('#' + mountId).empty(); $('#' + uniqueId).removeClass('script-Oyver-disabled'); }, togglePreferences() { if (this.submitting) return; this.prefsIconSet = self.prefs.iconSet; this.prefsFullContext = self.prefs.fullContextPreviewDefault; this.showPreferences = !this.showPreferences; }, async savePreferences() { this.savingPrefs = true; const newPrefs = { iconSet: this.prefsIconSet, fullContextPreviewDefault: this.prefsFullContext }; await self.savePrefs(newPrefs); self.prefs = Object.assign({}, self.prefs, newPrefs); this.iconSetPref = self.prefs.iconSet; this.savingPrefs = false; this.showPreferences = false; }, async togglePreview() { if (this.showPreview) { this.showPreview = false; return; } if (self.prefs.fullContextPreviewDefault && !this.showContextPreview) { await this.toggleContextPreview(); } this.showPreview = true; }, async toggleContextPreview() { if (this.showContextPreview) { this.showContextPreview = false; return; } if (!this.selectedOption) return; await this.refreshContextPreview(); this.showContextPreview = true; }, buildQueryParams() { const queryParams = { action: 'query', prop: 'revisions', rvprop: 'content', formatversion: 2 }; if (pageInfo.type === 'direct') { queryParams.titles = mw.config.get('wgPageName'); queryParams.rvsection = pageInfo.sectionNum; } else { queryParams.titles = pageInfo.targetPage; queryParams.rvsection = pageInfo.sectionId; } return queryParams; }, buildNewSectionText(sectionText) { const templateName = self.config.voteOptions[this.selectedVote].template; const group = self.config.voteOptions[this.selectedVote].group; const trimmedComment = this.comment.trim(); const headers = self.findGroupHeaders(sectionText.split('\n')); if (headers.length) { const lines = sectionText.split('\n'); const existingHeader = headers.find((h) => h.type === group); const blockEnd = existingHeader ? self.findGroupBlockEnd(lines, existingHeader.index) : -1; const bulletChar = existingHeader ? self.getBlockBulletChar(lines, existingHeader.index, blockEnd) : '*'; let voteLine = bulletChar + ' {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC)'; return { text: self.buildGroupedVoteText(sectionText, group, voteLine) }; } let voteLine = '\n* {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC)'; return { appendtext: voteLine, previewText: sectionText + voteLine }; }, async refreshContextPreview() { if (!this.selectedOption) return; this.contextPreviewLoading = true; try { const api = new mw.Api(); const res = await api.get(this.buildQueryParams()); const sectionText = self.extractContent(res); if (sectionText === null) throw new Error('Bölüm içeriği alınamadı'); const built = this.buildNewSectionText(sectionText); this.contextPreviewWikitext = built.text !== undefined ? built.text : built.previewText; } catch (error) { console.error('Oyver: bölüm önizlemesi başarısız', error); } finally { this.contextPreviewLoading = false; } }, async doVote() { this.submitError = ''; this.submitting = true; try { const api = new mw.Api(); const res = await api.get(this.buildQueryParams()); const sectionText = self.extractContent(res); if (sectionText === null) throw new Error('Bölüm içeriği alınamadı'); const currentUser = mw.config.get('wgUserName'); if (self.hasExistingVote(sectionText, currentUser)) { if (!confirm(this.messages.confirmPossibleDouble)) { this.submitting = false; return; } } const summaryBase = this.editSummaryInput.trim() || this.defaultSummary; const editSummary = summaryBase + self.config.summfix; const editParams = { action: 'edit', summary: editSummary, token: mw.user.tokens.get('csrfToken') }; if (pageInfo.type === 'direct') { editParams.title = mw.config.get('wgPageName'); editParams.section = pageInfo.sectionNum; } else { editParams.title = pageInfo.targetPage; editParams.section = pageInfo.sectionId; } const built = this.buildNewSectionText(sectionText); if (built.text !== undefined) { editParams.text = built.text; } else { editParams.appendtext = built.appendtext; } await api.postWithEditToken(editParams); setTimeout(() => { window.location.reload(); }, 1200); } catch (error) { console.error('Oyver: gönderim başarısız', error); this.submitError = this.messages.editError; this.submitting = false; } } }, template: ` <div class="script-Oyver-panel"> <div class="script-Oyver-panel-header"> <span class="script-Oyver-panel-title"> {{ panelTitle }} <span class="script-Oyver-version">v{{ version }}</span> </span> <div class="script-Oyver-header-actions"> <cdx-button weight="quiet" :disabled="submitting" :aria-label="messages.preferencesButtonLabel" :title="messages.preferencesButtonLabel" @click="togglePreferences"> <cdx-icon v-if="settingsIcon" :icon="settingsIcon"></cdx-icon> </cdx-button> <cdx-button weight="quiet" :disabled="submitting" :aria-label="messages.closeButtonLabel" :title="messages.closeButtonLabel" @click="cancelPanel"> <cdx-icon v-if="closeIcon" :icon="closeIcon"></cdx-icon> </cdx-button> </div> </div> <div v-if="showPreferences" class="script-Oyver-panel-body"> <div class="script-Oyver-prefs"> <p class="script-Oyver-prefs-title">{{ messages.preferencesTitle }}</p> <cdx-field class="script-Oyver-field"> <template #label>{{ messages.preferencesIconSetLabel }}</template> <cdx-radio v-model="prefsIconSet" input-value="codex" :name="prefsIconSetName"> {{ messages.preferencesIconSetCodex }} </cdx-radio> <cdx-radio v-model="prefsIconSet" input-value="onwiki" :name="prefsIconSetName"> {{ messages.preferencesIconSetOnWiki }} </cdx-radio> </cdx-field> <cdx-checkbox v-model="prefsFullContext" class="script-Oyver-field"> {{ messages.preferencesFullContextLabel }} </cdx-checkbox> <div class="script-Oyver-buttons"> <cdx-button weight="primary" action="progressive" :disabled="savingPrefs" @click="savePreferences"> <cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon> {{ messages.preferencesSave }} </cdx-button> <cdx-button :disabled="savingPrefs" @click="showPreferences = false"> {{ messages.cancelButton }} </cdx-button> </div> </div> </div> <div v-else class="script-Oyver-panel-body"> <p>{{ messages.vote }}</p> <div v-for="option in voteOptions" :key="option.value" class="script-Oyver-option"> <cdx-radio v-model="selectedVote" :input-value="option.value" :name="radioName" :disabled="submitting" > <cdx-icon v-if="iconSetPref === 'codex' && option.icon" :icon="option.icon"></cdx-icon> <img v-else-if="iconSetPref === 'onwiki' && option.iconUrl" :src="option.iconUrl" width="20" height="20" alt="" class="script-Oyver-onwiki-icon" > {{ option.label }} </cdx-radio> </div> <cdx-field class="script-Oyver-field"> <template #label>{{ messages.comment }}</template> <cdx-text-area v-model="comment" :rows="3" :disabled="submitting"></cdx-text-area> </cdx-field> <p class="script-Oyver-signature-note">{{ messages.signatureAutoAdd }}</p> <cdx-accordion v-model="showSummary" class="script-Oyver-field"> <template #title>{{ messages.editSummaryLabel }}</template> <cdx-text-input v-model="editSummaryInput" :disabled="submitting" @input="summaryEdited = true" ></cdx-text-input> </cdx-accordion> <cdx-message v-if="submitError" type="error" :allow-user-dismiss="false" class="script-Oyver-status"> {{ submitError }} </cdx-message> <cdx-message v-if="submitting" type="notice" :allow-user-dismiss="false" class="script-Oyver-status"> {{ messages.submitting }} </cdx-message> <div class="script-Oyver-buttons"> <cdx-button weight="primary" action="progressive" :disabled="!canSubmit" @click="doVote"> <cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon> {{ messages.windowButton }} </cdx-button> <cdx-button :disabled="!selectedVote || submitting" @click="togglePreview"> <cdx-icon v-if="previewToggleIcon" :icon="previewToggleIcon"></cdx-icon> {{ previewToggleLabel }} </cdx-button> <cdx-button :disabled="submitting" @click="cancelPanel"> {{ messages.cancelButton }} </cdx-button> </div> <div v-if="showPreview && selectedVote" class="script-Oyver-preview-wrapper"> <div class="script-Oyver-preview-toolbar"> <cdx-button weight="quiet" :disabled="contextPreviewLoading" :aria-label="messages.toggleFullSectionLabel" :title="messages.toggleFullSectionLabel" @click="toggleContextPreview" > <cdx-icon v-if="contextToggleIcon" :icon="contextToggleIcon"></cdx-icon> </cdx-button> </div> <preview-renderer :wikitext="showContextPreview ? contextPreviewWikitext : previewWikitext"></preview-renderer> </div> </div> </div> ` }); vueAppInstance.mount('#' + mountId); } }; Oyver.init(); }); // </nowiki> rx6wddjjnhi4hpbs6ng8v5celd4897j 17861 17860 2026-10-24T16:48:18Z Brightt11 1786 Restored revision 17855 by [[Special:Contributions/Brightt11|Brightt11]] ([[User talk:Brightt11|talk]]) (TwinkleGlobal) 17861 javascript text/javascript // Oyver - EasyVoteMeta (Author: Max) betiğinin yerelleştirilmiş versiyonu. // Orijinal betik kaynağı/Original script source: https://meta.wikimedia.org/wiki/User:Max/EasyVoteMeta.js // Bu betik, oylama/görüş bildirme sayfalarında bölüm başlıklarına "hızlı oy ver" bağlantısı ekler. // <nowiki> $(() => { 'use strict'; const Oyver = { version: '1.0.1 beta', config: { messages: { windowTitle: 'Oyver', windowButton: 'Gönder', cancelButton: 'İptal', vote: 'Görüş belirtmek için oy seçin:', comment: 'Yorum:', signatureAutoAdd: 'İmzanız otomatik olarak eklenecektir.', editSummaryLabel: 'Değişiklik özeti', showPreview: 'Önizlemeyi göster', hidePreview: 'Önizlemeyi gizle', preferencesTitle: 'Tercihler', preferencesIconSetLabel: 'Oy simgesi görünümü', preferencesIconSetCodex: 'Codex simgeleri', preferencesIconSetOnWiki: 'Wiki simgeleri', preferencesFullContextLabel: 'Önizlemede daima tüm bölümü göster', preferencesSave: 'Kaydet', preferencesButtonLabel: 'Tercihler', closeButtonLabel: 'Kapat', toggleFullSectionLabel: 'Tüm bölüm önizlemesini açın/kapatın', loadingPreviewLabel: 'Önizleme yükleniyor', submitting: 'Gönderiliyor. Lütfen internet tarayıcınızı açık tutun.', voteLinkText: 'hızlı oy ver', editError: 'Hata: Bir hata oldu. Klavyenizden F5 tuşuna basarak sayfayı yenileyin ve tekrar deneyin', confirmPossibleDouble: 'Bu başvuruda daha önce görüş yapmışsınız. Devam etmek istediğinizden emin misiniz? Lütfen önceki görüş(ler)inizin üstünü çizerek (<nowiki><s></s></nowiki> kullanarak) oyunuzu geri çekin.' }, voteOptions: { support: { label: 'Destek', template: 'Destek', icon: 'cdxIconCheck', group: 'support', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/9/94/Symbol_support_vote.svg' }, halfsupport: { label: 'Kısmi destek', template: 'Kısmi destek', icon: 'cdxIconCheck', group: 'support', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/8c/GA_candidate.svg' }, oppose: { label: 'Karşı', template: 'Karşı', icon: 'cdxIconClose', group: 'oppose', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/7/7f/Symbol_oppose_vote.svg' }, halfoppose: { label: 'Kısmi karşı', template: 'Kısmi karşı', icon: 'cdxIconClose', group: 'oppose', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/f/f5/BA_candidate.svg' }, neutral: { label: 'Tarafsız', template: 'Tarafsız', icon: 'cdxIconInfoFilled', group: 'neutral', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/89/Symbol_neutral_vote.svg' }, comment: { label: 'Yorum', template: 'Yorum', icon: 'cdxIconSpeechBubble', group: 'neutral', requiresComment: true, skipDoubleCheck: true } }, voteAliases: [], groupOrder: ['support', 'oppose', 'neutral'], groupHeaders: { support: ['Destek', 'Evet'], oppose: ['Karşı', 'Hayır'], neutral: ['Tarafsız', 'Yorum'] }, iconNames: ['cdxIconCheck', 'cdxIconClose', 'cdxIconInfoFilled', 'cdxIconAlert', 'cdxIconSpeechBubble', 'cdxIconEye', 'cdxIconEyeClosed', 'cdxIconSettings'], summfix: ' ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi)', pageConfigs: { 'Vikigezgin:Kullanıcı hakları adaylıkları': { type: 'auto', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [] } }, namespaceConfigs: { 8: { type: 'direct', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [], dbNames: [] }, 828: { type: 'direct', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [], dbNames: [] } } }, icons: {}, prefsPrefix: 'userjs-oyver-', prefsDefaults: { iconSet: 'codex', fullContextPreviewDefault: false }, prefsCache: {}, prefs: {}, currentPageConfig: null, init() { const currentPage = mw.config.get('wgPageName').replace(/_/g, ' '); this.currentPageConfig = this.config.pageConfigs[currentPage] || (this.config.namespaceConfigs || {})[mw.config.get('wgNamespaceNumber')]; if (!this.currentPageConfig) return; const dbNames = this.currentPageConfig.dbNames; if (dbNames && dbNames.length && !dbNames.includes(mw.config.get('wgDBname'))) return; if (mw.user.isAnon()) return; mw.loader.using(['vue', '@wikimedia/codex', 'mediawiki.util', 'mediawiki.api', 'mediawiki.user']).then((require) => { this.Vue = require('vue'); this.Codex = require('@wikimedia/codex'); this.run(); }); }, async loadIcons() { try { const api = new mw.Api(); const data = await api.get({ action: 'query', list: 'codexicons', names: this.config.iconNames.join('|'), formatversion: 2 }); this.icons = (data && data.query && data.query.codexicons) || {}; } catch (error) { this.icons = {}; } }, getPrefFullKey(key) { return this.prefsPrefix + key; }, loadPref(key) { const fullKey = this.getPrefFullKey(key); const fallback = this.prefsDefaults[key]; let value; if (Object.prototype.hasOwnProperty.call(this.prefsCache, fullKey)) { value = this.prefsCache[fullKey]; } else { try { value = window.localStorage ? window.localStorage.getItem(fullKey) : null; } catch (error) { value = null; } } if (value !== null && value !== undefined) { try { return JSON.parse(value); } catch (error) { return fallback; } } try { const serverValue = mw.user.options.get(fullKey, JSON.stringify(fallback)); return JSON.parse(serverValue); } catch (error) { return fallback; } }, loadAllPrefs() { const prefs = {}; Object.keys(this.prefsDefaults).forEach((key) => { prefs[key] = this.loadPref(key); }); return prefs; }, async savePref(key, value) { const fullKey = this.getPrefFullKey(key); const fullValue = JSON.stringify(value); this.prefsCache[fullKey] = fullValue; try { if (window.localStorage) window.localStorage.setItem(fullKey, fullValue); } catch (error) { } try { const api = new mw.Api(); await api.postWithEditToken({ action: 'options', optionname: fullKey, optionvalue: fullValue }); } catch (error) { console.error('Oyver: Hata. Tercih kaydedilemedi.', key, error); } }, async savePrefs(newPrefs) { const keys = Object.keys(newPrefs); for (const key of keys) { await this.savePref(key, newPrefs[key]); } }, async run() { this.addStyle(); this.prefs = this.loadAllPrefs(); await this.loadIcons(); const pageConfig = this.currentPageConfig; const skipPrefixes = (pageConfig.skipPrefixes || []).map((p) => p.toLocaleLowerCase('tr')); $(pageConfig.headingSelector || 'div.mw-heading.mw-heading3').each((_, heading) => { const $heading = $(heading); const editLinks = $heading.find('.mw-editsection'); const headingId = $heading.find('h1,h2,h3,h4,h5,h6').first().attr('id'); if (!headingId) return; const sectionTitle = headingId.replace(/_/g, ' '); const normalizedTitle = sectionTitle.toLocaleLowerCase('tr'); if (skipPrefixes.some((prefix) => normalizedTitle.startsWith(prefix))) return; if (pageConfig.type === 'direct') { this.handleDirectPage(editLinks, sectionTitle, $heading); } else if (pageConfig.type === 'transcluded') { this.handleTranscludedPage(editLinks, sectionTitle, $heading); } else if (pageConfig.type === 'auto') { this.handleAutoPage(editLinks, sectionTitle, $heading); } }); }, handleAutoPage(editLinks, sectionTitle, $heading) { const info = this.getTargetPageInfo(editLinks); if (!info) return; const currentPage = mw.config.get('wgPageName').replace(/_/g, ' '); if (info.targetPage === currentPage && /^\d+$/.test(info.sectionId)) { this.addVoteLink(editLinks, sectionTitle, { type: 'direct', sectionNum: parseInt(info.sectionId, 10) }, $heading); } else { this.addVoteLink(editLinks, sectionTitle, Object.assign({ type: 'transcluded' }, info), $heading); } }, handleDirectPage(editLinks, sectionTitle, $heading) { const sectionNum = this.getSectionNumber(editLinks); if (sectionNum !== -1) { this.addVoteLink(editLinks, sectionTitle, { type: 'direct', sectionNum }, $heading); } }, handleTranscludedPage(editLinks, sectionTitle, $heading) { const targetPageInfo = this.getTargetPageInfo(editLinks); if (targetPageInfo && targetPageInfo.targetPage && targetPageInfo.sectionId) { this.addVoteLink(editLinks, sectionTitle, Object.assign({ type: 'transcluded' }, targetPageInfo), $heading); } }, addStyle() { mw.util.addCSS(` .script-Oyver-link { color: var(--color-progressive, #36c); text-decoration: none; font-weight: bold; } .script-Oyver-link:hover { text-decoration: underline; } .script-Oyver-disabled { color: var(--color-disabled, #a2a9b1) !important; pointer-events: none; } .script-Oyver-divider { border: none; border-top: 1px solid var(--border-color-subtle, #eaecf0); margin: 8px 0 4px; } .script-Oyver-panel { border: 1px solid var(--border-color-base, #a2a9b1); background: var(--background-color-base, #fff); padding: 16px; margin: 8px 0 16px; } .script-Oyver-panel-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } .script-Oyver-panel-title { font-weight: bold; font-size: 15px; } .script-Oyver-version { font-size: 0.7em; font-weight: normal; color: var(--color-subtle, #54595d); vertical-align: middle; margin-left: 2px; } .script-Oyver-header-actions { display: flex; gap: 4px; } .script-Oyver-panel-body p:first-child { margin-top: 0; } .script-Oyver-option { margin: 8px 0; } .script-Oyver-option .cdx-icon { margin-right: 4px; } .script-Oyver-onwiki-icon { margin-right: 4px; vertical-align: middle; } .script-Oyver-field { margin-top: 14px; } .script-Oyver-signature-note { font-style: italic; color: var(--color-subtle, #54595d); font-size: 0.9em; margin-top: 8px; } .script-Oyver-status { margin-top: 12px; } .script-Oyver-buttons { margin-top: 16px; display: flex; gap: 8px; flex-wrap: wrap; } .script-Oyver-preview-wrapper { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border-color-subtle, #eaecf0); } .script-Oyver-preview-toolbar { display: flex; justify-content: flex-end; } .script-Oyver-preview { margin-top: 0; } .script-Oyver-preview-content { font-size: 0.95em; line-height: 1.6; } .script-Oyver-preview-loading { display: flex; align-items: center; gap: 8px; color: var(--color-subtle, #54595d); font-size: 0.88em; } .script-Oyver-prefs { border: 1px solid var(--border-color-subtle, #eaecf0); padding: 12px; } .script-Oyver-prefs-title { font-weight: bold; margin: 0 0 8px; } `); }, getSectionNumber(editLinks) { const editSectionUrl = editLinks.find('a:first').attr('href'); const sectionMatch = editSectionUrl && editSectionUrl.match(/[?&]section=(\d+)(?:&|$)/); return sectionMatch ? parseInt(sectionMatch[1], 10) : -1; }, getTargetPageInfo(editLinks) { const editSectionUrl = editLinks.find('a:first').attr('href'); if (!editSectionUrl) return null; const titleMatch = editSectionUrl.match(/[?&]title=([^&]+)/); const sectionMatch = editSectionUrl.match(/[?&]section=([^&]+)/); if (!titleMatch || !sectionMatch) return null; return { targetPage: decodeURIComponent(titleMatch[1]).replace(/_/g, ' '), sectionId: sectionMatch[1] }; }, extractContent(apiData) { const pages = apiData && apiData.query && apiData.query.pages; if (!pages || !pages[0] || !pages[0].revisions || !pages[0].revisions[0]) return null; return pages[0].revisions[0].content; }, sanitizeSectionTitle(rawTitle) { if (!rawTitle) return ''; const emojiPattern = /(\p{Extended_Pictographic}|\p{Emoji_Presentation}|[\u{1F1E6}-\u{1F1FF}]|\u{FE0F}|\u{200D})/gu; let text = rawTitle.replace(emojiPattern, ''); let cutIndex = -1; for (let i = 0; i < text.length; i++) { if (text[i] === '(') { cutIndex = i; break; } if (text[i] === '[') { if (text[i + 1] === '[') { i++; continue; } cutIndex = i; break; } } if (cutIndex !== -1) { text = text.substring(0, cutIndex); } return text.replace(/\s+/g, ' ').trim(); }, detectGroupHeaderType(line) { const match = line.match(/^;\s*(\S.*)$/); if (!match) return null; const text = match[1].toLocaleLowerCase('tr'); for (const key of this.config.groupOrder) { const words = this.config.groupHeaders[key] || []; for (const word of words) { const w = word.toLocaleLowerCase('tr'); if (text.startsWith(w) && !/^\p{L}/u.test(text.slice(w.length))) { return key; } } } return null; }, findGroupHeaders(lines) { return lines.reduce((acc, line, idx) => { const type = this.detectGroupHeaderType(line); if (type) acc.push({ type, index: idx }); return acc; }, []); }, findGroupBlockEnd(lines, headerIdx) { for (let i = headerIdx + 1; i < lines.length; i++) { if (this.detectGroupHeaderType(lines[i])) return i; } return lines.length; }, getBlockBulletChar(lines, headerIdx, blockEnd) { for (let i = headerIdx + 1; i < blockEnd; i++) { const match = lines[i].match(/^\s*([#*:]+)/); if (match) return match[1]; } return '*'; }, getGroupHeaderText(group) { const words = this.config.groupHeaders[group]; return (words && words[0]) || group; }, buildGroupedVoteText(sectionText, group, voteLine) { const lines = sectionText.split('\n'); const headers = this.findGroupHeaders(lines); const existing = headers.find((h) => h.type === group); const headerLine = ';' + this.getGroupHeaderText(group); if (existing) { const blockEnd = this.findGroupBlockEnd(lines, existing.index); let insertIdx = blockEnd; while (insertIdx - 1 > existing.index && lines[insertIdx - 1].trim() === '') insertIdx--; lines.splice(insertIdx, 0, voteLine); return lines.join('\n'); } const order = this.config.groupOrder; const orderIdx = order.indexOf(group); for (let o = orderIdx + 1; o < order.length; o++) { const laterHeader = headers.find((h) => h.type === order[o]); if (laterHeader) { lines.splice(laterHeader.index, 0, headerLine, voteLine, ''); return lines.join('\n'); } } for (let o = orderIdx - 1; o >= 0; o--) { const earlierHeader = headers.find((h) => h.type === order[o]); if (earlierHeader) { const blockEnd = this.findGroupBlockEnd(lines, earlierHeader.index); lines.splice(blockEnd, 0, '', headerLine, voteLine); return lines.join('\n'); } } let lastNonBlank = lines.length - 1; while (lastNonBlank >= 0 && lines[lastNonBlank].trim() === '') lastNonBlank--; lines.splice(lastNonBlank + 1, 0, '', headerLine, voteLine); return lines.join('\n'); }, hasExistingVote(sectionText, currentUser) { const templates = Object.values(this.config.voteOptions).filter((opt) => !opt.skipDoubleCheck).map((opt) => opt.template); const aliases = this.config.voteAliases || []; const allNames = [...templates, ...aliases].map((name) => mw.util.escapeRegExp(name)); const escapedUser = mw.util.escapeRegExp(currentUser); const pattern = new RegExp('\\{\\{(?:' + allNames.join('|') + ')\\}\\}[^\\n~]{0,300}?' + escapedUser, 'i'); return pattern.test(sectionText); }, addVoteLink(editLinks, sectionTitle, pageInfo, $heading) { const uniqueId = 'script-Oyver-launch-' + Math.random().toString(36).slice(2, 11); const mountId = 'script-Oyver-mount-' + uniqueId; const voteLink = $('<a>') .attr('id', uniqueId) .addClass('script-Oyver-link') .text(this.config.messages.voteLinkText) .attr('href', '#') .click((event) => { event.preventDefault(); if (!voteLink.hasClass('script-Oyver-disabled')) { voteLink.addClass('script-Oyver-disabled'); if (!$('#' + mountId).length) { $heading.after( $('<div>').attr('id', mountId), $('<hr>').addClass('script-Oyver-divider') ); } this.showPanel(sectionTitle, pageInfo, $heading, uniqueId, mountId); } }); editLinks.children().last().before(' | ', voteLink); }, getPreviewComponent() { const loadingLabel = this.config.messages.loadingPreviewLabel; return { components: { CdxProgressBar: this.Codex.CdxProgressBar }, props: { wikitext: { type: String, default: '' } }, data() { return { previewHtml: '', haveHtml: false, loadingLabel }; }, methods: { loadPreview(wikitextToPreview) { new mw.Api().get({ action: 'parse', formatversion: 2, title: mw.config.get('wgPageName'), text: wikitextToPreview, prop: 'text', pst: true, disablelimitreport: true, disableeditsection: true, sectionpreview: true }).then((res) => { if (res && res.parse && res.parse.text) { this.previewHtml = res.parse.text; this.haveHtml = true; } }); } }, watch: { wikitext: { immediate: true, handler(newValue) { this.previewHtml = ''; this.haveHtml = false; this.loadPreview(newValue); } } }, template: ` <div class="script-Oyver-preview"> <div v-if="!haveHtml" class="script-Oyver-preview-loading"> <cdx-progress-bar inline :aria-label="loadingLabel"></cdx-progress-bar> </div> <div v-else class="script-Oyver-preview-content" v-html="previewHtml"></div> </div> ` }; }, showPanel(sectionTitle, pageInfo, $heading, uniqueId, mountId) { const self = this; const rawLiveSectionTitle = $heading.contents().not($heading.find('.mw-editsection')).text().trim() || sectionTitle; const liveSectionTitle = self.sanitizeSectionTitle(rawLiveSectionTitle); const vueAppInstance = self.Vue.createMwApp({ components: { CdxRadio: self.Codex.CdxRadio, CdxCheckbox: self.Codex.CdxCheckbox, CdxTextArea: self.Codex.CdxTextArea, CdxTextInput: self.Codex.CdxTextInput, CdxField: self.Codex.CdxField, CdxAccordion: self.Codex.CdxAccordion, CdxMessage: self.Codex.CdxMessage, CdxButton: self.Codex.CdxButton, CdxIcon: self.Codex.CdxIcon, previewRenderer: self.getPreviewComponent() }, data() { return { version: self.version, messages: self.config.messages, sectionTitle: liveSectionTitle, uniqueId, voteOptions: Object.entries(self.config.voteOptions).map(([value, opt]) => ({ value, label: opt.label, group: opt.group, icon: self.icons[opt.icon] || null, iconUrl: opt.iconUrl, requiresComment: !!opt.requiresComment })), selectedVote: null, comment: '', showSummary: false, editSummaryInput: '/* ' + liveSectionTitle + ' */ ', summaryEdited: false, showPreview: false, submitting: false, submitError: '', showContextPreview: false, contextPreviewWikitext: '', contextPreviewLoading: false, contextPreviewDebounce: null, showPreferences: false, iconSetPref: self.prefs.iconSet, prefsIconSet: self.prefs.iconSet, prefsFullContext: self.prefs.fullContextPreviewDefault, savingPrefs: false, checkIcon: self.icons.cdxIconCheck || null, closeIcon: self.icons.cdxIconClose || null, eyeIcon: self.icons.cdxIconEye || null, eyeClosedIcon: self.icons.cdxIconEyeClosed || null, settingsIcon: self.icons.cdxIconSettings || null }; }, computed: { panelTitle() { return this.messages.windowTitle + ': ' + this.sectionTitle; }, radioName() { return 'script-Oyver-vote-' + this.uniqueId; }, prefsIconSetName() { return 'script-Oyver-prefs-iconset-' + this.uniqueId; }, selectedOption() { return this.voteOptions.find((opt) => opt.value === this.selectedVote) || null; }, defaultSummary() { const base = '/* ' + this.sectionTitle + ' */ '; return this.selectedOption ? base + this.selectedOption.label : base; }, canSubmit() { if (this.submitting || !this.selectedVote) return false; if (this.selectedOption && this.selectedOption.requiresComment && !this.comment.trim()) return false; return true; }, previewToggleIcon() { return this.showPreview ? this.eyeClosedIcon : this.eyeIcon; }, previewToggleLabel() { return this.showPreview ? this.messages.hidePreview : this.messages.showPreview; }, contextToggleIcon() { return this.showContextPreview ? this.eyeClosedIcon : this.eyeIcon; }, previewWikitext() { if (!this.selectedOption) return ''; const templateName = self.config.voteOptions[this.selectedVote].template; const trimmedComment = this.comment.trim(); let voteLine = '* {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC)'; return voteLine; } }, watch: { selectedVote() { if (!this.summaryEdited) { this.editSummaryInput = this.defaultSummary; } if (this.showContextPreview) { this.refreshContextPreview(); } }, comment() { if (!this.showContextPreview) return; clearTimeout(this.contextPreviewDebounce); this.contextPreviewDebounce = setTimeout(() => { this.refreshContextPreview(); }, 400); } }, methods: { cancelPanel() { if (this.submitting) return; vueAppInstance.unmount(); $('#' + mountId).empty(); $('#' + uniqueId).removeClass('script-Oyver-disabled'); }, togglePreferences() { if (this.submitting) return; this.prefsIconSet = self.prefs.iconSet; this.prefsFullContext = self.prefs.fullContextPreviewDefault; this.showPreferences = !this.showPreferences; }, async savePreferences() { this.savingPrefs = true; const newPrefs = { iconSet: this.prefsIconSet, fullContextPreviewDefault: this.prefsFullContext }; await self.savePrefs(newPrefs); self.prefs = Object.assign({}, self.prefs, newPrefs); this.iconSetPref = self.prefs.iconSet; this.savingPrefs = false; this.showPreferences = false; }, async togglePreview() { if (this.showPreview) { this.showPreview = false; return; } if (self.prefs.fullContextPreviewDefault && !this.showContextPreview) { await this.toggleContextPreview(); } this.showPreview = true; }, async toggleContextPreview() { if (this.showContextPreview) { this.showContextPreview = false; return; } if (!this.selectedOption) return; await this.refreshContextPreview(); this.showContextPreview = true; }, buildQueryParams() { const queryParams = { action: 'query', prop: 'revisions', rvprop: 'content', formatversion: 2 }; if (pageInfo.type === 'direct') { queryParams.titles = mw.config.get('wgPageName'); queryParams.rvsection = pageInfo.sectionNum; } else { queryParams.titles = pageInfo.targetPage; queryParams.rvsection = pageInfo.sectionId; } return queryParams; }, buildNewSectionText(sectionText) { const templateName = self.config.voteOptions[this.selectedVote].template; const group = self.config.voteOptions[this.selectedVote].group; const trimmedComment = this.comment.trim(); const headers = self.findGroupHeaders(sectionText.split('\n')); if (headers.length) { const lines = sectionText.split('\n'); const existingHeader = headers.find((h) => h.type === group); const blockEnd = existingHeader ? self.findGroupBlockEnd(lines, existingHeader.index) : -1; const bulletChar = existingHeader ? self.getBlockBulletChar(lines, existingHeader.index, blockEnd) : '*'; let voteLine = bulletChar + ' {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC)'; return { text: self.buildGroupedVoteText(sectionText, group, voteLine) }; } let voteLine = '\n* {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC)'; return { appendtext: voteLine, previewText: sectionText + voteLine }; }, async refreshContextPreview() { if (!this.selectedOption) return; this.contextPreviewLoading = true; try { const api = new mw.Api(); const res = await api.get(this.buildQueryParams()); const sectionText = self.extractContent(res); if (sectionText === null) throw new Error('Bölüm içeriği alınamadı'); const built = this.buildNewSectionText(sectionText); this.contextPreviewWikitext = built.text !== undefined ? built.text : built.previewText; } catch (error) { console.error('Oyver: bölüm önizlemesi başarısız', error); } finally { this.contextPreviewLoading = false; } }, async doVote() { this.submitError = ''; this.submitting = true; try { const api = new mw.Api(); const res = await api.get(this.buildQueryParams()); const sectionText = self.extractContent(res); if (sectionText === null) throw new Error('Bölüm içeriği alınamadı'); const currentUser = mw.config.get('wgUserName'); if (self.hasExistingVote(sectionText, currentUser)) { if (!confirm(this.messages.confirmPossibleDouble)) { this.submitting = false; return; } } const summaryBase = this.editSummaryInput.trim() || this.defaultSummary; const editSummary = summaryBase + self.config.summfix; const editParams = { action: 'edit', summary: editSummary, token: mw.user.tokens.get('csrfToken') }; if (pageInfo.type === 'direct') { editParams.title = mw.config.get('wgPageName'); editParams.section = pageInfo.sectionNum; } else { editParams.title = pageInfo.targetPage; editParams.section = pageInfo.sectionId; } const built = this.buildNewSectionText(sectionText); if (built.text !== undefined) { editParams.text = built.text; } else { editParams.appendtext = built.appendtext; } await api.postWithEditToken(editParams); setTimeout(() => { window.location.reload(); }, 1200); } catch (error) { console.error('Oyver: gönderim başarısız', error); this.submitError = this.messages.editError; this.submitting = false; } } }, template: ` <div class="script-Oyver-panel"> <div class="script-Oyver-panel-header"> <span class="script-Oyver-panel-title"> {{ panelTitle }} <span class="script-Oyver-version">v{{ version }}</span> </span> <div class="script-Oyver-header-actions"> <cdx-button weight="quiet" :disabled="submitting" :aria-label="messages.preferencesButtonLabel" :title="messages.preferencesButtonLabel" @click="togglePreferences"> <cdx-icon v-if="settingsIcon" :icon="settingsIcon"></cdx-icon> </cdx-button> <cdx-button weight="quiet" :disabled="submitting" :aria-label="messages.closeButtonLabel" :title="messages.closeButtonLabel" @click="cancelPanel"> <cdx-icon v-if="closeIcon" :icon="closeIcon"></cdx-icon> </cdx-button> </div> </div> <div v-if="showPreferences" class="script-Oyver-panel-body"> <div class="script-Oyver-prefs"> <p class="script-Oyver-prefs-title">{{ messages.preferencesTitle }}</p> <cdx-field class="script-Oyver-field"> <template #label>{{ messages.preferencesIconSetLabel }}</template> <cdx-radio v-model="prefsIconSet" input-value="codex" :name="prefsIconSetName"> {{ messages.preferencesIconSetCodex }} </cdx-radio> <cdx-radio v-model="prefsIconSet" input-value="onwiki" :name="prefsIconSetName"> {{ messages.preferencesIconSetOnWiki }} </cdx-radio> </cdx-field> <cdx-checkbox v-model="prefsFullContext" class="script-Oyver-field"> {{ messages.preferencesFullContextLabel }} </cdx-checkbox> <div class="script-Oyver-buttons"> <cdx-button weight="primary" action="progressive" :disabled="savingPrefs" @click="savePreferences"> <cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon> {{ messages.preferencesSave }} </cdx-button> <cdx-button :disabled="savingPrefs" @click="showPreferences = false"> {{ messages.cancelButton }} </cdx-button> </div> </div> </div> <div v-else class="script-Oyver-panel-body"> <p>{{ messages.vote }}</p> <div v-for="option in voteOptions" :key="option.value" class="script-Oyver-option"> <cdx-radio v-model="selectedVote" :input-value="option.value" :name="radioName" :disabled="submitting" > <cdx-icon v-if="iconSetPref === 'codex' && option.icon" :icon="option.icon"></cdx-icon> <img v-else-if="iconSetPref === 'onwiki' && option.iconUrl" :src="option.iconUrl" width="20" height="20" alt="" class="script-Oyver-onwiki-icon" > {{ option.label }} </cdx-radio> </div> <cdx-field class="script-Oyver-field"> <template #label>{{ messages.comment }}</template> <cdx-text-area v-model="comment" :rows="3" :disabled="submitting"></cdx-text-area> </cdx-field> <p class="script-Oyver-signature-note">{{ messages.signatureAutoAdd }}</p> <cdx-accordion v-model="showSummary" class="script-Oyver-field"> <template #title>{{ messages.editSummaryLabel }}</template> <cdx-text-input v-model="editSummaryInput" :disabled="submitting" @input="summaryEdited = true" ></cdx-text-input> </cdx-accordion> <cdx-message v-if="submitError" type="error" :allow-user-dismiss="false" class="script-Oyver-status"> {{ submitError }} </cdx-message> <cdx-message v-if="submitting" type="notice" :allow-user-dismiss="false" class="script-Oyver-status"> {{ messages.submitting }} </cdx-message> <div class="script-Oyver-buttons"> <cdx-button weight="primary" action="progressive" :disabled="!canSubmit" @click="doVote"> <cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon> {{ messages.windowButton }} </cdx-button> <cdx-button :disabled="!selectedVote || submitting" @click="togglePreview"> <cdx-icon v-if="previewToggleIcon" :icon="previewToggleIcon"></cdx-icon> {{ previewToggleLabel }} </cdx-button> <cdx-button :disabled="submitting" @click="cancelPanel"> {{ messages.cancelButton }} </cdx-button> </div> <div v-if="showPreview && selectedVote" class="script-Oyver-preview-wrapper"> <div class="script-Oyver-preview-toolbar"> <cdx-button weight="quiet" :disabled="contextPreviewLoading" :aria-label="messages.toggleFullSectionLabel" :title="messages.toggleFullSectionLabel" @click="toggleContextPreview" > <cdx-icon v-if="contextToggleIcon" :icon="contextToggleIcon"></cdx-icon> </cdx-button> </div> <preview-renderer :wikitext="showContextPreview ? contextPreviewWikitext : previewWikitext"></preview-renderer> </div> </div> </div> ` }); vueAppInstance.mount('#' + mountId); } }; Oyver.init(); }); // </nowiki> rcxsqsnbx90vhh6lymn1p3cf9wi24m2 17864 17861 2026-10-24T17:05:58Z Brightt11 1786 yorum simgesi eklendi 17864 javascript text/javascript // Oyver - EasyVoteMeta (Author: Max) betiğinin yerelleştirilmiş versiyonu. // Orijinal betik kaynağı/Original script source: https://meta.wikimedia.org/wiki/User:Max/EasyVoteMeta.js // Bu betik, oylama/görüş bildirme sayfalarında bölüm başlıklarına "hızlı oy ver" bağlantısı ekler. // sürüm: 1.1 beta. // <nowiki> $(() => { 'use strict'; const Oyver = { version: '1.1 beta', config: { messages: { windowTitle: 'Oyver', windowButton: 'Gönder', cancelButton: 'İptal', vote: 'Görüş belirtmek için oy seçin:', comment: 'Yorum:', signatureAutoAdd: 'İmzanız otomatik olarak eklenecektir.', editSummaryLabel: 'Değişiklik özeti', showPreview: 'Önizlemeyi göster', hidePreview: 'Önizlemeyi gizle', preferencesTitle: 'Tercihler', preferencesIconSetLabel: 'Oy simgesi görünümü', preferencesIconSetCodex: 'Codex simgeleri', preferencesIconSetOnWiki: 'Wiki simgeleri', preferencesFullContextLabel: 'Önizlemede daima tüm bölümü göster', preferencesSave: 'Kaydet', preferencesButtonLabel: 'Tercihler', closeButtonLabel: 'Kapat', toggleFullSectionLabel: 'Tüm bölüm önizlemesini açın/kapatın', loadingPreviewLabel: 'Önizleme yükleniyor', submitting: 'Gönderiliyor. Lütfen internet tarayıcınızı açık tutun.', voteLinkText: 'hızlı oy ver', editError: 'Hata: Bir hata oldu. Klavyenizden F5 tuşuna basarak sayfayı yenileyin ve tekrar deneyin', confirmPossibleDouble: 'Bu başvuruda daha önce görüş yapmışsınız. Devam etmek istediğinizden emin misiniz? Lütfen önceki görüş(ler)inizin üstünü çizerek (<nowiki><s></s></nowiki> kullanarak) oyunuzu geri çekin.' }, voteOptions: { support: { label: 'Destek', template: 'Destek', icon: 'cdxIconCheck', group: 'support', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/9/94/Symbol_support_vote.svg' }, halfsupport: { label: 'Kısmi destek', template: 'Kısmi destek', icon: 'cdxIconCheck', group: 'support', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/8c/GA_candidate.svg' }, oppose: { label: 'Karşı', template: 'Karşı', icon: 'cdxIconClose', group: 'oppose', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/7/7f/Symbol_oppose_vote.svg' }, halfoppose: { label: 'Kısmi karşı', template: 'Kısmi karşı', icon: 'cdxIconClose', group: 'oppose', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/f/f5/BA_candidate.svg' }, neutral: { label: 'Tarafsız', template: 'Tarafsız', icon: 'cdxIconInfoFilled', group: 'neutral', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/89/Symbol_neutral_vote.svg' }, comment: { label: 'Yorum', template: 'Yorum', icon: 'cdxIconSpeechBubble', group: 'neutral', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/d/df/Pictogram_voting_comment_%28orange%29.svg?utm_source=commons.wikimedia.org&utm_campaign=index&utm_content=original', requiresComment: true, skipDoubleCheck: true } }, voteAliases: [], groupOrder: ['support', 'oppose', 'neutral'], groupHeaders: { support: ['Destek', 'Evet'], oppose: ['Karşı', 'Hayır'], neutral: ['Tarafsız', 'Yorum'] }, iconNames: ['cdxIconCheck', 'cdxIconClose', 'cdxIconInfoFilled', 'cdxIconAlert', 'cdxIconSpeechBubble', 'cdxIconEye', 'cdxIconEyeClosed', 'cdxIconSettings'], summfix: ' ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.)', pageConfigs: { 'Vikigezgin:Kullanıcı hakları adaylıkları': { type: 'auto', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [] } }, namespaceConfigs: { 8: { type: 'direct', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [], dbNames: [] }, 828: { type: 'direct', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [], dbNames: [] } } }, icons: {}, prefsPrefix: 'userjs-oyver-', prefsDefaults: { iconSet: 'codex', fullContextPreviewDefault: false }, prefsCache: {}, prefs: {}, currentPageConfig: null, init() { const currentPage = mw.config.get('wgPageName').replace(/_/g, ' '); this.currentPageConfig = this.config.pageConfigs[currentPage] || (this.config.namespaceConfigs || {})[mw.config.get('wgNamespaceNumber')]; if (!this.currentPageConfig) return; const dbNames = this.currentPageConfig.dbNames; if (dbNames && dbNames.length && !dbNames.includes(mw.config.get('wgDBname'))) return; if (mw.user.isAnon()) return; mw.loader.using(['vue', '@wikimedia/codex', 'mediawiki.util', 'mediawiki.api', 'mediawiki.user']).then((require) => { this.Vue = require('vue'); this.Codex = require('@wikimedia/codex'); this.run(); }); }, async loadIcons() { try { const api = new mw.Api(); const data = await api.get({ action: 'query', list: 'codexicons', names: this.config.iconNames.join('|'), formatversion: 2 }); this.icons = (data && data.query && data.query.codexicons) || {}; } catch (error) { this.icons = {}; } }, getPrefFullKey(key) { return this.prefsPrefix + key; }, loadPref(key) { const fullKey = this.getPrefFullKey(key); const fallback = this.prefsDefaults[key]; let value; if (Object.prototype.hasOwnProperty.call(this.prefsCache, fullKey)) { value = this.prefsCache[fullKey]; } else { try { value = window.localStorage ? window.localStorage.getItem(fullKey) : null; } catch (error) { value = null; } } if (value !== null && value !== undefined) { try { return JSON.parse(value); } catch (error) { return fallback; } } try { const serverValue = mw.user.options.get(fullKey, JSON.stringify(fallback)); return JSON.parse(serverValue); } catch (error) { return fallback; } }, loadAllPrefs() { const prefs = {}; Object.keys(this.prefsDefaults).forEach((key) => { prefs[key] = this.loadPref(key); }); return prefs; }, async savePref(key, value) { const fullKey = this.getPrefFullKey(key); const fullValue = JSON.stringify(value); this.prefsCache[fullKey] = fullValue; try { if (window.localStorage) window.localStorage.setItem(fullKey, fullValue); } catch (error) { } try { const api = new mw.Api(); await api.postWithEditToken({ action: 'options', optionname: fullKey, optionvalue: fullValue }); } catch (error) { console.error('Oyver: Hata. Tercih kaydedilemedi.', key, error); } }, async savePrefs(newPrefs) { const keys = Object.keys(newPrefs); for (const key of keys) { await this.savePref(key, newPrefs[key]); } }, async run() { this.addStyle(); this.prefs = this.loadAllPrefs(); await this.loadIcons(); const pageConfig = this.currentPageConfig; const skipPrefixes = (pageConfig.skipPrefixes || []).map((p) => p.toLocaleLowerCase('tr')); $(pageConfig.headingSelector || 'div.mw-heading.mw-heading3').each((_, heading) => { const $heading = $(heading); const editLinks = $heading.find('.mw-editsection'); const headingId = $heading.find('h1,h2,h3,h4,h5,h6').first().attr('id'); if (!headingId) return; const sectionTitle = headingId.replace(/_/g, ' '); const normalizedTitle = sectionTitle.toLocaleLowerCase('tr'); if (skipPrefixes.some((prefix) => normalizedTitle.startsWith(prefix))) return; if (pageConfig.type === 'direct') { this.handleDirectPage(editLinks, sectionTitle, $heading); } else if (pageConfig.type === 'transcluded') { this.handleTranscludedPage(editLinks, sectionTitle, $heading); } else if (pageConfig.type === 'auto') { this.handleAutoPage(editLinks, sectionTitle, $heading); } }); }, handleAutoPage(editLinks, sectionTitle, $heading) { const info = this.getTargetPageInfo(editLinks); if (!info) return; const currentPage = mw.config.get('wgPageName').replace(/_/g, ' '); if (info.targetPage === currentPage && /^\d+$/.test(info.sectionId)) { this.addVoteLink(editLinks, sectionTitle, { type: 'direct', sectionNum: parseInt(info.sectionId, 10) }, $heading); } else { this.addVoteLink(editLinks, sectionTitle, Object.assign({ type: 'transcluded' }, info), $heading); } }, handleDirectPage(editLinks, sectionTitle, $heading) { const sectionNum = this.getSectionNumber(editLinks); if (sectionNum !== -1) { this.addVoteLink(editLinks, sectionTitle, { type: 'direct', sectionNum }, $heading); } }, handleTranscludedPage(editLinks, sectionTitle, $heading) { const targetPageInfo = this.getTargetPageInfo(editLinks); if (targetPageInfo && targetPageInfo.targetPage && targetPageInfo.sectionId) { this.addVoteLink(editLinks, sectionTitle, Object.assign({ type: 'transcluded' }, targetPageInfo), $heading); } }, addStyle() { mw.util.addCSS(` .script-Oyver-link { color: var(--color-progressive, #36c); text-decoration: none; font-weight: bold; } .script-Oyver-link:hover { text-decoration: underline; } .script-Oyver-disabled { color: var(--color-disabled, #a2a9b1) !important; pointer-events: none; } .script-Oyver-divider { border: none; border-top: 1px solid var(--border-color-subtle, #eaecf0); margin: 8px 0 4px; } .script-Oyver-panel { border: 1px solid var(--border-color-base, #a2a9b1); background: var(--background-color-base, #fff); padding: 16px; margin: 8px 0 16px; } .script-Oyver-panel-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } .script-Oyver-panel-title { font-weight: bold; font-size: 15px; } .script-Oyver-version { font-size: 0.7em; font-weight: normal; color: var(--color-subtle, #54595d); vertical-align: middle; margin-left: 2px; } .script-Oyver-header-actions { display: flex; gap: 4px; } .script-Oyver-panel-body p:first-child { margin-top: 0; } .script-Oyver-option { margin: 8px 0; } .script-Oyver-option .cdx-icon { margin-right: 4px; } .script-Oyver-onwiki-icon { margin-right: 4px; vertical-align: middle; } .script-Oyver-field { margin-top: 14px; } .script-Oyver-signature-note { font-style: italic; color: var(--color-subtle, #54595d); font-size: 0.9em; margin-top: 8px; } .script-Oyver-status { margin-top: 12px; } .script-Oyver-buttons { margin-top: 16px; display: flex; gap: 8px; flex-wrap: wrap; } .script-Oyver-preview-wrapper { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border-color-subtle, #eaecf0); } .script-Oyver-preview-toolbar { display: flex; justify-content: flex-end; } .script-Oyver-preview { margin-top: 0; } .script-Oyver-preview-content { font-size: 0.95em; line-height: 1.6; } .script-Oyver-preview-loading { display: flex; align-items: center; gap: 8px; color: var(--color-subtle, #54595d); font-size: 0.88em; } .script-Oyver-prefs { border: 1px solid var(--border-color-subtle, #eaecf0); padding: 12px; } .script-Oyver-prefs-title { font-weight: bold; margin: 0 0 8px; } `); }, getSectionNumber(editLinks) { const editSectionUrl = editLinks.find('a:first').attr('href'); const sectionMatch = editSectionUrl && editSectionUrl.match(/[?&]section=(\d+)(?:&|$)/); return sectionMatch ? parseInt(sectionMatch[1], 10) : -1; }, getTargetPageInfo(editLinks) { const editSectionUrl = editLinks.find('a:first').attr('href'); if (!editSectionUrl) return null; const titleMatch = editSectionUrl.match(/[?&]title=([^&]+)/); const sectionMatch = editSectionUrl.match(/[?&]section=([^&]+)/); if (!titleMatch || !sectionMatch) return null; return { targetPage: decodeURIComponent(titleMatch[1]).replace(/_/g, ' '), sectionId: sectionMatch[1] }; }, extractContent(apiData) { const pages = apiData && apiData.query && apiData.query.pages; if (!pages || !pages[0] || !pages[0].revisions || !pages[0].revisions[0]) return null; return pages[0].revisions[0].content; }, sanitizeSectionTitle(rawTitle) { if (!rawTitle) return ''; const emojiPattern = /(\p{Extended_Pictographic}|\p{Emoji_Presentation}|[\u{1F1E6}-\u{1F1FF}]|\u{FE0F}|\u{200D})/gu; let text = rawTitle.replace(emojiPattern, ''); let cutIndex = -1; for (let i = 0; i < text.length; i++) { if (text[i] === '(') { cutIndex = i; break; } if (text[i] === '[') { if (text[i + 1] === '[') { i++; continue; } cutIndex = i; break; } } if (cutIndex !== -1) { text = text.substring(0, cutIndex); } return text.replace(/\s+/g, ' ').trim(); }, detectGroupHeaderType(line) { const match = line.match(/^;\s*(\S.*)$/); if (!match) return null; const text = match[1].toLocaleLowerCase('tr'); for (const key of this.config.groupOrder) { const words = this.config.groupHeaders[key] || []; for (const word of words) { const w = word.toLocaleLowerCase('tr'); if (text.startsWith(w) && !/^\p{L}/u.test(text.slice(w.length))) { return key; } } } return null; }, findGroupHeaders(lines) { return lines.reduce((acc, line, idx) => { const type = this.detectGroupHeaderType(line); if (type) acc.push({ type, index: idx }); return acc; }, []); }, findGroupBlockEnd(lines, headerIdx) { for (let i = headerIdx + 1; i < lines.length; i++) { if (this.detectGroupHeaderType(lines[i])) return i; } return lines.length; }, getBlockBulletChar(lines, headerIdx, blockEnd) { for (let i = headerIdx + 1; i < blockEnd; i++) { const match = lines[i].match(/^\s*([#*:]+)/); if (match) return match[1]; } return '*'; }, getGroupHeaderText(group) { const words = this.config.groupHeaders[group]; return (words && words[0]) || group; }, buildGroupedVoteText(sectionText, group, voteLine) { const lines = sectionText.split('\n'); const headers = this.findGroupHeaders(lines); const existing = headers.find((h) => h.type === group); const headerLine = ';' + this.getGroupHeaderText(group); if (existing) { const blockEnd = this.findGroupBlockEnd(lines, existing.index); let insertIdx = blockEnd; while (insertIdx - 1 > existing.index && lines[insertIdx - 1].trim() === '') insertIdx--; lines.splice(insertIdx, 0, voteLine); return lines.join('\n'); } const order = this.config.groupOrder; const orderIdx = order.indexOf(group); for (let o = orderIdx + 1; o < order.length; o++) { const laterHeader = headers.find((h) => h.type === order[o]); if (laterHeader) { lines.splice(laterHeader.index, 0, headerLine, voteLine, ''); return lines.join('\n'); } } for (let o = orderIdx - 1; o >= 0; o--) { const earlierHeader = headers.find((h) => h.type === order[o]); if (earlierHeader) { const blockEnd = this.findGroupBlockEnd(lines, earlierHeader.index); lines.splice(blockEnd, 0, '', headerLine, voteLine); return lines.join('\n'); } } let lastNonBlank = lines.length - 1; while (lastNonBlank >= 0 && lines[lastNonBlank].trim() === '') lastNonBlank--; lines.splice(lastNonBlank + 1, 0, '', headerLine, voteLine); return lines.join('\n'); }, hasExistingVote(sectionText, currentUser) { const templates = Object.values(this.config.voteOptions).filter((opt) => !opt.skipDoubleCheck).map((opt) => opt.template); const aliases = this.config.voteAliases || []; const allNames = [...templates, ...aliases].map((name) => mw.util.escapeRegExp(name)); const escapedUser = mw.util.escapeRegExp(currentUser); const pattern = new RegExp('\\{\\{(?:' + allNames.join('|') + ')\\}\\}[^\\n~]{0,300}?' + escapedUser, 'i'); return pattern.test(sectionText); }, addVoteLink(editLinks, sectionTitle, pageInfo, $heading) { const uniqueId = 'script-Oyver-launch-' + Math.random().toString(36).slice(2, 11); const mountId = 'script-Oyver-mount-' + uniqueId; const voteLink = $('<a>') .attr('id', uniqueId) .addClass('script-Oyver-link') .text(this.config.messages.voteLinkText) .attr('href', '#') .click((event) => { event.preventDefault(); if (!voteLink.hasClass('script-Oyver-disabled')) { voteLink.addClass('script-Oyver-disabled'); if (!$('#' + mountId).length) { $heading.after( $('<div>').attr('id', mountId), $('<hr>').addClass('script-Oyver-divider') ); } this.showPanel(sectionTitle, pageInfo, $heading, uniqueId, mountId); } }); editLinks.children().last().before(' | ', voteLink); }, getPreviewComponent() { const loadingLabel = this.config.messages.loadingPreviewLabel; return { components: { CdxProgressBar: this.Codex.CdxProgressBar }, props: { wikitext: { type: String, default: '' } }, data() { return { previewHtml: '', haveHtml: false, loadingLabel }; }, methods: { loadPreview(wikitextToPreview) { new mw.Api().get({ action: 'parse', formatversion: 2, title: mw.config.get('wgPageName'), text: wikitextToPreview, prop: 'text', pst: true, disablelimitreport: true, disableeditsection: true, sectionpreview: true }).then((res) => { if (res && res.parse && res.parse.text) { this.previewHtml = res.parse.text; this.haveHtml = true; } }); } }, watch: { wikitext: { immediate: true, handler(newValue) { this.previewHtml = ''; this.haveHtml = false; this.loadPreview(newValue); } } }, template: ` <div class="script-Oyver-preview"> <div v-if="!haveHtml" class="script-Oyver-preview-loading"> <cdx-progress-bar inline :aria-label="loadingLabel"></cdx-progress-bar> </div> <div v-else class="script-Oyver-preview-content" v-html="previewHtml"></div> </div> ` }; }, showPanel(sectionTitle, pageInfo, $heading, uniqueId, mountId) { const self = this; const rawLiveSectionTitle = $heading.contents().not($heading.find('.mw-editsection')).text().trim() || sectionTitle; const liveSectionTitle = self.sanitizeSectionTitle(rawLiveSectionTitle); const vueAppInstance = self.Vue.createMwApp({ components: { CdxRadio: self.Codex.CdxRadio, CdxCheckbox: self.Codex.CdxCheckbox, CdxTextArea: self.Codex.CdxTextArea, CdxTextInput: self.Codex.CdxTextInput, CdxField: self.Codex.CdxField, CdxAccordion: self.Codex.CdxAccordion, CdxMessage: self.Codex.CdxMessage, CdxButton: self.Codex.CdxButton, CdxIcon: self.Codex.CdxIcon, previewRenderer: self.getPreviewComponent() }, data() { return { version: self.version, messages: self.config.messages, sectionTitle: liveSectionTitle, uniqueId, voteOptions: Object.entries(self.config.voteOptions).map(([value, opt]) => ({ value, label: opt.label, group: opt.group, icon: self.icons[opt.icon] || null, iconUrl: opt.iconUrl, requiresComment: !!opt.requiresComment })), selectedVote: null, comment: '', showSummary: false, editSummaryInput: '/* ' + liveSectionTitle + ' */ ', summaryEdited: false, showPreview: false, submitting: false, submitError: '', showContextPreview: false, contextPreviewWikitext: '', contextPreviewLoading: false, contextPreviewDebounce: null, showPreferences: false, iconSetPref: self.prefs.iconSet, prefsIconSet: self.prefs.iconSet, prefsFullContext: self.prefs.fullContextPreviewDefault, savingPrefs: false, checkIcon: self.icons.cdxIconCheck || null, closeIcon: self.icons.cdxIconClose || null, eyeIcon: self.icons.cdxIconEye || null, eyeClosedIcon: self.icons.cdxIconEyeClosed || null, settingsIcon: self.icons.cdxIconSettings || null }; }, computed: { panelTitle() { return this.messages.windowTitle + ': ' + this.sectionTitle; }, radioName() { return 'script-Oyver-vote-' + this.uniqueId; }, prefsIconSetName() { return 'script-Oyver-prefs-iconset-' + this.uniqueId; }, selectedOption() { return this.voteOptions.find((opt) => opt.value === this.selectedVote) || null; }, defaultSummary() { const base = '/* ' + this.sectionTitle + ' */ '; return this.selectedOption ? base + this.selectedOption.label : base; }, canSubmit() { if (this.submitting || !this.selectedVote) return false; if (this.selectedOption && this.selectedOption.requiresComment && !this.comment.trim()) return false; return true; }, previewToggleIcon() { return this.showPreview ? this.eyeClosedIcon : this.eyeIcon; }, previewToggleLabel() { return this.showPreview ? this.messages.hidePreview : this.messages.showPreview; }, contextToggleIcon() { return this.showContextPreview ? this.eyeClosedIcon : this.eyeIcon; }, previewWikitext() { if (!this.selectedOption) return ''; const templateName = self.config.voteOptions[this.selectedVote].template; const trimmedComment = this.comment.trim(); let voteLine = '* {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC)'; return voteLine; } }, watch: { selectedVote() { if (!this.summaryEdited) { this.editSummaryInput = this.defaultSummary; } if (this.showContextPreview) { this.refreshContextPreview(); } }, comment() { if (!this.showContextPreview) return; clearTimeout(this.contextPreviewDebounce); this.contextPreviewDebounce = setTimeout(() => { this.refreshContextPreview(); }, 400); } }, methods: { cancelPanel() { if (this.submitting) return; vueAppInstance.unmount(); $('#' + mountId).empty(); $('#' + uniqueId).removeClass('script-Oyver-disabled'); }, togglePreferences() { if (this.submitting) return; this.prefsIconSet = self.prefs.iconSet; this.prefsFullContext = self.prefs.fullContextPreviewDefault; this.showPreferences = !this.showPreferences; }, async savePreferences() { this.savingPrefs = true; const newPrefs = { iconSet: this.prefsIconSet, fullContextPreviewDefault: this.prefsFullContext }; await self.savePrefs(newPrefs); self.prefs = Object.assign({}, self.prefs, newPrefs); this.iconSetPref = self.prefs.iconSet; this.savingPrefs = false; this.showPreferences = false; }, async togglePreview() { if (this.showPreview) { this.showPreview = false; return; } if (self.prefs.fullContextPreviewDefault && !this.showContextPreview) { await this.toggleContextPreview(); } this.showPreview = true; }, async toggleContextPreview() { if (this.showContextPreview) { this.showContextPreview = false; return; } if (!this.selectedOption) return; await this.refreshContextPreview(); this.showContextPreview = true; }, buildQueryParams() { const queryParams = { action: 'query', prop: 'revisions', rvprop: 'content', formatversion: 2 }; if (pageInfo.type === 'direct') { queryParams.titles = mw.config.get('wgPageName'); queryParams.rvsection = pageInfo.sectionNum; } else { queryParams.titles = pageInfo.targetPage; queryParams.rvsection = pageInfo.sectionId; } return queryParams; }, buildNewSectionText(sectionText) { const templateName = self.config.voteOptions[this.selectedVote].template; const group = self.config.voteOptions[this.selectedVote].group; const trimmedComment = this.comment.trim(); const headers = self.findGroupHeaders(sectionText.split('\n')); if (headers.length) { const lines = sectionText.split('\n'); const existingHeader = headers.find((h) => h.type === group); const blockEnd = existingHeader ? self.findGroupBlockEnd(lines, existingHeader.index) : -1; const bulletChar = existingHeader ? self.getBlockBulletChar(lines, existingHeader.index, blockEnd) : '*'; let voteLine = bulletChar + ' {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC)'; return { text: self.buildGroupedVoteText(sectionText, group, voteLine) }; } let voteLine = '\n* {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC)'; return { appendtext: voteLine, previewText: sectionText + voteLine }; }, async refreshContextPreview() { if (!this.selectedOption) return; this.contextPreviewLoading = true; try { const api = new mw.Api(); const res = await api.get(this.buildQueryParams()); const sectionText = self.extractContent(res); if (sectionText === null) throw new Error('Bölüm içeriği alınamadı'); const built = this.buildNewSectionText(sectionText); this.contextPreviewWikitext = built.text !== undefined ? built.text : built.previewText; } catch (error) { console.error('Oyver: bölüm önizlemesi başarısız', error); } finally { this.contextPreviewLoading = false; } }, async doVote() { this.submitError = ''; this.submitting = true; try { const api = new mw.Api(); const res = await api.get(this.buildQueryParams()); const sectionText = self.extractContent(res); if (sectionText === null) throw new Error('Bölüm içeriği alınamadı'); const currentUser = mw.config.get('wgUserName'); if (self.hasExistingVote(sectionText, currentUser)) { if (!confirm(this.messages.confirmPossibleDouble)) { this.submitting = false; return; } } const summaryBase = this.editSummaryInput.trim() || this.defaultSummary; const editSummary = summaryBase + self.config.summfix; const editParams = { action: 'edit', summary: editSummary, token: mw.user.tokens.get('csrfToken') }; if (pageInfo.type === 'direct') { editParams.title = mw.config.get('wgPageName'); editParams.section = pageInfo.sectionNum; } else { editParams.title = pageInfo.targetPage; editParams.section = pageInfo.sectionId; } const built = this.buildNewSectionText(sectionText); if (built.text !== undefined) { editParams.text = built.text; } else { editParams.appendtext = built.appendtext; } await api.postWithEditToken(editParams); setTimeout(() => { window.location.reload(); }, 1200); } catch (error) { console.error('Oyver: gönderim başarısız', error); this.submitError = this.messages.editError; this.submitting = false; } } }, template: ` <div class="script-Oyver-panel"> <div class="script-Oyver-panel-header"> <span class="script-Oyver-panel-title"> {{ panelTitle }} <span class="script-Oyver-version">v{{ version }}</span> </span> <div class="script-Oyver-header-actions"> <cdx-button weight="quiet" :disabled="submitting" :aria-label="messages.preferencesButtonLabel" :title="messages.preferencesButtonLabel" @click="togglePreferences"> <cdx-icon v-if="settingsIcon" :icon="settingsIcon"></cdx-icon> </cdx-button> <cdx-button weight="quiet" :disabled="submitting" :aria-label="messages.closeButtonLabel" :title="messages.closeButtonLabel" @click="cancelPanel"> <cdx-icon v-if="closeIcon" :icon="closeIcon"></cdx-icon> </cdx-button> </div> </div> <div v-if="showPreferences" class="script-Oyver-panel-body"> <div class="script-Oyver-prefs"> <p class="script-Oyver-prefs-title">{{ messages.preferencesTitle }}</p> <cdx-field class="script-Oyver-field"> <template #label>{{ messages.preferencesIconSetLabel }}</template> <cdx-radio v-model="prefsIconSet" input-value="codex" :name="prefsIconSetName"> {{ messages.preferencesIconSetCodex }} </cdx-radio> <cdx-radio v-model="prefsIconSet" input-value="onwiki" :name="prefsIconSetName"> {{ messages.preferencesIconSetOnWiki }} </cdx-radio> </cdx-field> <cdx-checkbox v-model="prefsFullContext" class="script-Oyver-field"> {{ messages.preferencesFullContextLabel }} </cdx-checkbox> <div class="script-Oyver-buttons"> <cdx-button weight="primary" action="progressive" :disabled="savingPrefs" @click="savePreferences"> <cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon> {{ messages.preferencesSave }} </cdx-button> <cdx-button :disabled="savingPrefs" @click="showPreferences = false"> {{ messages.cancelButton }} </cdx-button> </div> </div> </div> <div v-else class="script-Oyver-panel-body"> <p>{{ messages.vote }}</p> <div v-for="option in voteOptions" :key="option.value" class="script-Oyver-option"> <cdx-radio v-model="selectedVote" :input-value="option.value" :name="radioName" :disabled="submitting" > <cdx-icon v-if="iconSetPref === 'codex' && option.icon" :icon="option.icon"></cdx-icon> <img v-else-if="iconSetPref === 'onwiki' && option.iconUrl" :src="option.iconUrl" width="20" height="20" alt="" class="script-Oyver-onwiki-icon" > {{ option.label }} </cdx-radio> </div> <cdx-field class="script-Oyver-field"> <template #label>{{ messages.comment }}</template> <cdx-text-area v-model="comment" :rows="3" :disabled="submitting"></cdx-text-area> </cdx-field> <p class="script-Oyver-signature-note">{{ messages.signatureAutoAdd }}</p> <cdx-accordion v-model="showSummary" class="script-Oyver-field"> <template #title>{{ messages.editSummaryLabel }}</template> <cdx-text-input v-model="editSummaryInput" :disabled="submitting" @input="summaryEdited = true" ></cdx-text-input> </cdx-accordion> <cdx-message v-if="submitError" type="error" :allow-user-dismiss="false" class="script-Oyver-status"> {{ submitError }} </cdx-message> <cdx-message v-if="submitting" type="notice" :allow-user-dismiss="false" class="script-Oyver-status"> {{ messages.submitting }} </cdx-message> <div class="script-Oyver-buttons"> <cdx-button weight="primary" action="progressive" :disabled="!canSubmit" @click="doVote"> <cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon> {{ messages.windowButton }} </cdx-button> <cdx-button :disabled="!selectedVote || submitting" @click="togglePreview"> <cdx-icon v-if="previewToggleIcon" :icon="previewToggleIcon"></cdx-icon> {{ previewToggleLabel }} </cdx-button> <cdx-button :disabled="submitting" @click="cancelPanel"> {{ messages.cancelButton }} </cdx-button> </div> <div v-if="showPreview && selectedVote" class="script-Oyver-preview-wrapper"> <div class="script-Oyver-preview-toolbar"> <cdx-button weight="quiet" :disabled="contextPreviewLoading" :aria-label="messages.toggleFullSectionLabel" :title="messages.toggleFullSectionLabel" @click="toggleContextPreview" > <cdx-icon v-if="contextToggleIcon" :icon="contextToggleIcon"></cdx-icon> </cdx-button> </div> <preview-renderer :wikitext="showContextPreview ? contextPreviewWikitext : previewWikitext"></preview-renderer> </div> </div> </div> ` }); vueAppInstance.mount('#' + mountId); } }; Oyver.init(); }); // </nowiki> 6m9b4kyvsaw3mmgn7nm57m7pabdhcm5 17872 17864 2026-10-24T17:14:27Z Brightt11 1786 kısmi karşı -> kısmen karşı 17872 javascript text/javascript // Oyver - EasyVoteMeta (Author: Max) betiğinin yerelleştirilmiş versiyonu. // Orijinal betik kaynağı/Original script source: https://meta.wikimedia.org/wiki/User:Max/EasyVoteMeta.js // Bu betik, oylama/görüş bildirme sayfalarında bölüm başlıklarına "hızlı oy ver" bağlantısı ekler. // sürüm: 1.1.1 beta. // <nowiki> $(() => { 'use strict'; const Oyver = { version: '1.1.1 beta', config: { messages: { windowTitle: 'Oyver', windowButton: 'Gönder', cancelButton: 'İptal', vote: 'Görüş belirtmek için oy seçin:', comment: 'Yorum:', signatureAutoAdd: 'İmzanız otomatik olarak eklenecektir.', editSummaryLabel: 'Değişiklik özeti', showPreview: 'Önizlemeyi göster', hidePreview: 'Önizlemeyi gizle', preferencesTitle: 'Tercihler', preferencesIconSetLabel: 'Oy simgesi görünümü', preferencesIconSetCodex: 'Codex simgeleri', preferencesIconSetOnWiki: 'Wiki simgeleri', preferencesFullContextLabel: 'Önizlemede daima tüm bölümü göster', preferencesSave: 'Kaydet', preferencesButtonLabel: 'Tercihler', closeButtonLabel: 'Kapat', toggleFullSectionLabel: 'Tüm bölüm önizlemesini açın/kapatın', loadingPreviewLabel: 'Önizleme yükleniyor', submitting: 'Gönderiliyor. Lütfen internet tarayıcınızı açık tutun.', voteLinkText: 'hızlı oy ver', editError: 'Hata: Bir hata oldu. Klavyenizden F5 tuşuna basarak sayfayı yenileyin ve tekrar deneyin', confirmPossibleDouble: 'Bu başvuruda daha önce görüş yapmışsınız. Devam etmek istediğinizden emin misiniz? Lütfen önceki görüş(ler)inizin üstünü çizerek (<nowiki><s></s></nowiki> kullanarak) oyunuzu geri çekin.' }, voteOptions: { support: { label: 'Destek', template: 'Destek', icon: 'cdxIconCheck', group: 'support', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/9/94/Symbol_support_vote.svg' }, halfsupport: { label: 'Kısmi destek', template: 'Kısmi destek', icon: 'cdxIconCheck', group: 'support', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/8c/GA_candidate.svg' }, oppose: { label: 'Karşı', template: 'Karşı', icon: 'cdxIconClose', group: 'oppose', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/7/7f/Symbol_oppose_vote.svg' }, halfoppose: { label: 'Kısmen karşı', template: 'Kısmen karşı', icon: 'cdxIconClose', group: 'oppose', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/f/f5/BA_candidate.svg' }, neutral: { label: 'Tarafsız', template: 'Tarafsız', icon: 'cdxIconInfoFilled', group: 'neutral', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/89/Symbol_neutral_vote.svg' }, comment: { label: 'Yorum', template: 'Yorum', icon: 'cdxIconSpeechBubble', group: 'neutral', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/d/df/Pictogram_voting_comment_%28orange%29.svg?utm_source=commons.wikimedia.org&utm_campaign=index&utm_content=original', requiresComment: true, skipDoubleCheck: true } }, voteAliases: [], groupOrder: ['support', 'oppose', 'neutral'], groupHeaders: { support: ['Destek', 'Evet'], oppose: ['Karşı', 'Hayır'], neutral: ['Tarafsız', 'Yorum'] }, iconNames: ['cdxIconCheck', 'cdxIconClose', 'cdxIconInfoFilled', 'cdxIconAlert', 'cdxIconSpeechBubble', 'cdxIconEye', 'cdxIconEyeClosed', 'cdxIconSettings'], summfix: ' ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.)', pageConfigs: { 'Vikigezgin:Kullanıcı hakları adaylıkları': { type: 'auto', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [] } }, namespaceConfigs: { 8: { type: 'direct', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [], dbNames: [] }, 828: { type: 'direct', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [], dbNames: [] } } }, icons: {}, prefsPrefix: 'userjs-oyver-', prefsDefaults: { iconSet: 'codex', fullContextPreviewDefault: false }, prefsCache: {}, prefs: {}, currentPageConfig: null, init() { const currentPage = mw.config.get('wgPageName').replace(/_/g, ' '); this.currentPageConfig = this.config.pageConfigs[currentPage] || (this.config.namespaceConfigs || {})[mw.config.get('wgNamespaceNumber')]; if (!this.currentPageConfig) return; const dbNames = this.currentPageConfig.dbNames; if (dbNames && dbNames.length && !dbNames.includes(mw.config.get('wgDBname'))) return; if (mw.user.isAnon()) return; mw.loader.using(['vue', '@wikimedia/codex', 'mediawiki.util', 'mediawiki.api', 'mediawiki.user']).then((require) => { this.Vue = require('vue'); this.Codex = require('@wikimedia/codex'); this.run(); }); }, async loadIcons() { try { const api = new mw.Api(); const data = await api.get({ action: 'query', list: 'codexicons', names: this.config.iconNames.join('|'), formatversion: 2 }); this.icons = (data && data.query && data.query.codexicons) || {}; } catch (error) { this.icons = {}; } }, getPrefFullKey(key) { return this.prefsPrefix + key; }, loadPref(key) { const fullKey = this.getPrefFullKey(key); const fallback = this.prefsDefaults[key]; let value; if (Object.prototype.hasOwnProperty.call(this.prefsCache, fullKey)) { value = this.prefsCache[fullKey]; } else { try { value = window.localStorage ? window.localStorage.getItem(fullKey) : null; } catch (error) { value = null; } } if (value !== null && value !== undefined) { try { return JSON.parse(value); } catch (error) { return fallback; } } try { const serverValue = mw.user.options.get(fullKey, JSON.stringify(fallback)); return JSON.parse(serverValue); } catch (error) { return fallback; } }, loadAllPrefs() { const prefs = {}; Object.keys(this.prefsDefaults).forEach((key) => { prefs[key] = this.loadPref(key); }); return prefs; }, async savePref(key, value) { const fullKey = this.getPrefFullKey(key); const fullValue = JSON.stringify(value); this.prefsCache[fullKey] = fullValue; try { if (window.localStorage) window.localStorage.setItem(fullKey, fullValue); } catch (error) { } try { const api = new mw.Api(); await api.postWithEditToken({ action: 'options', optionname: fullKey, optionvalue: fullValue }); } catch (error) { console.error('Oyver: Hata. Tercih kaydedilemedi.', key, error); } }, async savePrefs(newPrefs) { const keys = Object.keys(newPrefs); for (const key of keys) { await this.savePref(key, newPrefs[key]); } }, async run() { this.addStyle(); this.prefs = this.loadAllPrefs(); await this.loadIcons(); const pageConfig = this.currentPageConfig; const skipPrefixes = (pageConfig.skipPrefixes || []).map((p) => p.toLocaleLowerCase('tr')); $(pageConfig.headingSelector || 'div.mw-heading.mw-heading3').each((_, heading) => { const $heading = $(heading); const editLinks = $heading.find('.mw-editsection'); const headingId = $heading.find('h1,h2,h3,h4,h5,h6').first().attr('id'); if (!headingId) return; const sectionTitle = headingId.replace(/_/g, ' '); const normalizedTitle = sectionTitle.toLocaleLowerCase('tr'); if (skipPrefixes.some((prefix) => normalizedTitle.startsWith(prefix))) return; if (pageConfig.type === 'direct') { this.handleDirectPage(editLinks, sectionTitle, $heading); } else if (pageConfig.type === 'transcluded') { this.handleTranscludedPage(editLinks, sectionTitle, $heading); } else if (pageConfig.type === 'auto') { this.handleAutoPage(editLinks, sectionTitle, $heading); } }); }, handleAutoPage(editLinks, sectionTitle, $heading) { const info = this.getTargetPageInfo(editLinks); if (!info) return; const currentPage = mw.config.get('wgPageName').replace(/_/g, ' '); if (info.targetPage === currentPage && /^\d+$/.test(info.sectionId)) { this.addVoteLink(editLinks, sectionTitle, { type: 'direct', sectionNum: parseInt(info.sectionId, 10) }, $heading); } else { this.addVoteLink(editLinks, sectionTitle, Object.assign({ type: 'transcluded' }, info), $heading); } }, handleDirectPage(editLinks, sectionTitle, $heading) { const sectionNum = this.getSectionNumber(editLinks); if (sectionNum !== -1) { this.addVoteLink(editLinks, sectionTitle, { type: 'direct', sectionNum }, $heading); } }, handleTranscludedPage(editLinks, sectionTitle, $heading) { const targetPageInfo = this.getTargetPageInfo(editLinks); if (targetPageInfo && targetPageInfo.targetPage && targetPageInfo.sectionId) { this.addVoteLink(editLinks, sectionTitle, Object.assign({ type: 'transcluded' }, targetPageInfo), $heading); } }, addStyle() { mw.util.addCSS(` .script-Oyver-link { color: var(--color-progressive, #36c); text-decoration: none; font-weight: bold; } .script-Oyver-link:hover { text-decoration: underline; } .script-Oyver-disabled { color: var(--color-disabled, #a2a9b1) !important; pointer-events: none; } .script-Oyver-divider { border: none; border-top: 1px solid var(--border-color-subtle, #eaecf0); margin: 8px 0 4px; } .script-Oyver-panel { border: 1px solid var(--border-color-base, #a2a9b1); background: var(--background-color-base, #fff); padding: 16px; margin: 8px 0 16px; } .script-Oyver-panel-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } .script-Oyver-panel-title { font-weight: bold; font-size: 15px; } .script-Oyver-version { font-size: 0.7em; font-weight: normal; color: var(--color-subtle, #54595d); vertical-align: middle; margin-left: 2px; } .script-Oyver-header-actions { display: flex; gap: 4px; } .script-Oyver-panel-body p:first-child { margin-top: 0; } .script-Oyver-option { margin: 8px 0; } .script-Oyver-option .cdx-icon { margin-right: 4px; } .script-Oyver-onwiki-icon { margin-right: 4px; vertical-align: middle; } .script-Oyver-field { margin-top: 14px; } .script-Oyver-signature-note { font-style: italic; color: var(--color-subtle, #54595d); font-size: 0.9em; margin-top: 8px; } .script-Oyver-status { margin-top: 12px; } .script-Oyver-buttons { margin-top: 16px; display: flex; gap: 8px; flex-wrap: wrap; } .script-Oyver-preview-wrapper { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border-color-subtle, #eaecf0); } .script-Oyver-preview-toolbar { display: flex; justify-content: flex-end; } .script-Oyver-preview { margin-top: 0; } .script-Oyver-preview-content { font-size: 0.95em; line-height: 1.6; } .script-Oyver-preview-loading { display: flex; align-items: center; gap: 8px; color: var(--color-subtle, #54595d); font-size: 0.88em; } .script-Oyver-prefs { border: 1px solid var(--border-color-subtle, #eaecf0); padding: 12px; } .script-Oyver-prefs-title { font-weight: bold; margin: 0 0 8px; } `); }, getSectionNumber(editLinks) { const editSectionUrl = editLinks.find('a:first').attr('href'); const sectionMatch = editSectionUrl && editSectionUrl.match(/[?&]section=(\d+)(?:&|$)/); return sectionMatch ? parseInt(sectionMatch[1], 10) : -1; }, getTargetPageInfo(editLinks) { const editSectionUrl = editLinks.find('a:first').attr('href'); if (!editSectionUrl) return null; const titleMatch = editSectionUrl.match(/[?&]title=([^&]+)/); const sectionMatch = editSectionUrl.match(/[?&]section=([^&]+)/); if (!titleMatch || !sectionMatch) return null; return { targetPage: decodeURIComponent(titleMatch[1]).replace(/_/g, ' '), sectionId: sectionMatch[1] }; }, extractContent(apiData) { const pages = apiData && apiData.query && apiData.query.pages; if (!pages || !pages[0] || !pages[0].revisions || !pages[0].revisions[0]) return null; return pages[0].revisions[0].content; }, sanitizeSectionTitle(rawTitle) { if (!rawTitle) return ''; const emojiPattern = /(\p{Extended_Pictographic}|\p{Emoji_Presentation}|[\u{1F1E6}-\u{1F1FF}]|\u{FE0F}|\u{200D})/gu; let text = rawTitle.replace(emojiPattern, ''); let cutIndex = -1; for (let i = 0; i < text.length; i++) { if (text[i] === '(') { cutIndex = i; break; } if (text[i] === '[') { if (text[i + 1] === '[') { i++; continue; } cutIndex = i; break; } } if (cutIndex !== -1) { text = text.substring(0, cutIndex); } return text.replace(/\s+/g, ' ').trim(); }, detectGroupHeaderType(line) { const match = line.match(/^;\s*(\S.*)$/); if (!match) return null; const text = match[1].toLocaleLowerCase('tr'); for (const key of this.config.groupOrder) { const words = this.config.groupHeaders[key] || []; for (const word of words) { const w = word.toLocaleLowerCase('tr'); if (text.startsWith(w) && !/^\p{L}/u.test(text.slice(w.length))) { return key; } } } return null; }, findGroupHeaders(lines) { return lines.reduce((acc, line, idx) => { const type = this.detectGroupHeaderType(line); if (type) acc.push({ type, index: idx }); return acc; }, []); }, findGroupBlockEnd(lines, headerIdx) { for (let i = headerIdx + 1; i < lines.length; i++) { if (this.detectGroupHeaderType(lines[i])) return i; } return lines.length; }, getBlockBulletChar(lines, headerIdx, blockEnd) { for (let i = headerIdx + 1; i < blockEnd; i++) { const match = lines[i].match(/^\s*([#*:]+)/); if (match) return match[1]; } return '*'; }, getGroupHeaderText(group) { const words = this.config.groupHeaders[group]; return (words && words[0]) || group; }, buildGroupedVoteText(sectionText, group, voteLine) { const lines = sectionText.split('\n'); const headers = this.findGroupHeaders(lines); const existing = headers.find((h) => h.type === group); const headerLine = ';' + this.getGroupHeaderText(group); if (existing) { const blockEnd = this.findGroupBlockEnd(lines, existing.index); let insertIdx = blockEnd; while (insertIdx - 1 > existing.index && lines[insertIdx - 1].trim() === '') insertIdx--; lines.splice(insertIdx, 0, voteLine); return lines.join('\n'); } const order = this.config.groupOrder; const orderIdx = order.indexOf(group); for (let o = orderIdx + 1; o < order.length; o++) { const laterHeader = headers.find((h) => h.type === order[o]); if (laterHeader) { lines.splice(laterHeader.index, 0, headerLine, voteLine, ''); return lines.join('\n'); } } for (let o = orderIdx - 1; o >= 0; o--) { const earlierHeader = headers.find((h) => h.type === order[o]); if (earlierHeader) { const blockEnd = this.findGroupBlockEnd(lines, earlierHeader.index); lines.splice(blockEnd, 0, '', headerLine, voteLine); return lines.join('\n'); } } let lastNonBlank = lines.length - 1; while (lastNonBlank >= 0 && lines[lastNonBlank].trim() === '') lastNonBlank--; lines.splice(lastNonBlank + 1, 0, '', headerLine, voteLine); return lines.join('\n'); }, hasExistingVote(sectionText, currentUser) { const templates = Object.values(this.config.voteOptions).filter((opt) => !opt.skipDoubleCheck).map((opt) => opt.template); const aliases = this.config.voteAliases || []; const allNames = [...templates, ...aliases].map((name) => mw.util.escapeRegExp(name)); const escapedUser = mw.util.escapeRegExp(currentUser); const pattern = new RegExp('\\{\\{(?:' + allNames.join('|') + ')\\}\\}[^\\n~]{0,300}?' + escapedUser, 'i'); return pattern.test(sectionText); }, addVoteLink(editLinks, sectionTitle, pageInfo, $heading) { const uniqueId = 'script-Oyver-launch-' + Math.random().toString(36).slice(2, 11); const mountId = 'script-Oyver-mount-' + uniqueId; const voteLink = $('<a>') .attr('id', uniqueId) .addClass('script-Oyver-link') .text(this.config.messages.voteLinkText) .attr('href', '#') .click((event) => { event.preventDefault(); if (!voteLink.hasClass('script-Oyver-disabled')) { voteLink.addClass('script-Oyver-disabled'); if (!$('#' + mountId).length) { $heading.after( $('<div>').attr('id', mountId), $('<hr>').addClass('script-Oyver-divider') ); } this.showPanel(sectionTitle, pageInfo, $heading, uniqueId, mountId); } }); editLinks.children().last().before(' | ', voteLink); }, getPreviewComponent() { const loadingLabel = this.config.messages.loadingPreviewLabel; return { components: { CdxProgressBar: this.Codex.CdxProgressBar }, props: { wikitext: { type: String, default: '' } }, data() { return { previewHtml: '', haveHtml: false, loadingLabel }; }, methods: { loadPreview(wikitextToPreview) { new mw.Api().get({ action: 'parse', formatversion: 2, title: mw.config.get('wgPageName'), text: wikitextToPreview, prop: 'text', pst: true, disablelimitreport: true, disableeditsection: true, sectionpreview: true }).then((res) => { if (res && res.parse && res.parse.text) { this.previewHtml = res.parse.text; this.haveHtml = true; } }); } }, watch: { wikitext: { immediate: true, handler(newValue) { this.previewHtml = ''; this.haveHtml = false; this.loadPreview(newValue); } } }, template: ` <div class="script-Oyver-preview"> <div v-if="!haveHtml" class="script-Oyver-preview-loading"> <cdx-progress-bar inline :aria-label="loadingLabel"></cdx-progress-bar> </div> <div v-else class="script-Oyver-preview-content" v-html="previewHtml"></div> </div> ` }; }, showPanel(sectionTitle, pageInfo, $heading, uniqueId, mountId) { const self = this; const rawLiveSectionTitle = $heading.contents().not($heading.find('.mw-editsection')).text().trim() || sectionTitle; const liveSectionTitle = self.sanitizeSectionTitle(rawLiveSectionTitle); const vueAppInstance = self.Vue.createMwApp({ components: { CdxRadio: self.Codex.CdxRadio, CdxCheckbox: self.Codex.CdxCheckbox, CdxTextArea: self.Codex.CdxTextArea, CdxTextInput: self.Codex.CdxTextInput, CdxField: self.Codex.CdxField, CdxAccordion: self.Codex.CdxAccordion, CdxMessage: self.Codex.CdxMessage, CdxButton: self.Codex.CdxButton, CdxIcon: self.Codex.CdxIcon, previewRenderer: self.getPreviewComponent() }, data() { return { version: self.version, messages: self.config.messages, sectionTitle: liveSectionTitle, uniqueId, voteOptions: Object.entries(self.config.voteOptions).map(([value, opt]) => ({ value, label: opt.label, group: opt.group, icon: self.icons[opt.icon] || null, iconUrl: opt.iconUrl, requiresComment: !!opt.requiresComment })), selectedVote: null, comment: '', showSummary: false, editSummaryInput: '/* ' + liveSectionTitle + ' */ ', summaryEdited: false, showPreview: false, submitting: false, submitError: '', showContextPreview: false, contextPreviewWikitext: '', contextPreviewLoading: false, contextPreviewDebounce: null, showPreferences: false, iconSetPref: self.prefs.iconSet, prefsIconSet: self.prefs.iconSet, prefsFullContext: self.prefs.fullContextPreviewDefault, savingPrefs: false, checkIcon: self.icons.cdxIconCheck || null, closeIcon: self.icons.cdxIconClose || null, eyeIcon: self.icons.cdxIconEye || null, eyeClosedIcon: self.icons.cdxIconEyeClosed || null, settingsIcon: self.icons.cdxIconSettings || null }; }, computed: { panelTitle() { return this.messages.windowTitle + ': ' + this.sectionTitle; }, radioName() { return 'script-Oyver-vote-' + this.uniqueId; }, prefsIconSetName() { return 'script-Oyver-prefs-iconset-' + this.uniqueId; }, selectedOption() { return this.voteOptions.find((opt) => opt.value === this.selectedVote) || null; }, defaultSummary() { const base = '/* ' + this.sectionTitle + ' */ '; return this.selectedOption ? base + this.selectedOption.label : base; }, canSubmit() { if (this.submitting || !this.selectedVote) return false; if (this.selectedOption && this.selectedOption.requiresComment && !this.comment.trim()) return false; return true; }, previewToggleIcon() { return this.showPreview ? this.eyeClosedIcon : this.eyeIcon; }, previewToggleLabel() { return this.showPreview ? this.messages.hidePreview : this.messages.showPreview; }, contextToggleIcon() { return this.showContextPreview ? this.eyeClosedIcon : this.eyeIcon; }, previewWikitext() { if (!this.selectedOption) return ''; const templateName = self.config.voteOptions[this.selectedVote].template; const trimmedComment = this.comment.trim(); let voteLine = '* {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC)'; return voteLine; } }, watch: { selectedVote() { if (!this.summaryEdited) { this.editSummaryInput = this.defaultSummary; } if (this.showContextPreview) { this.refreshContextPreview(); } }, comment() { if (!this.showContextPreview) return; clearTimeout(this.contextPreviewDebounce); this.contextPreviewDebounce = setTimeout(() => { this.refreshContextPreview(); }, 400); } }, methods: { cancelPanel() { if (this.submitting) return; vueAppInstance.unmount(); $('#' + mountId).empty(); $('#' + uniqueId).removeClass('script-Oyver-disabled'); }, togglePreferences() { if (this.submitting) return; this.prefsIconSet = self.prefs.iconSet; this.prefsFullContext = self.prefs.fullContextPreviewDefault; this.showPreferences = !this.showPreferences; }, async savePreferences() { this.savingPrefs = true; const newPrefs = { iconSet: this.prefsIconSet, fullContextPreviewDefault: this.prefsFullContext }; await self.savePrefs(newPrefs); self.prefs = Object.assign({}, self.prefs, newPrefs); this.iconSetPref = self.prefs.iconSet; this.savingPrefs = false; this.showPreferences = false; }, async togglePreview() { if (this.showPreview) { this.showPreview = false; return; } if (self.prefs.fullContextPreviewDefault && !this.showContextPreview) { await this.toggleContextPreview(); } this.showPreview = true; }, async toggleContextPreview() { if (this.showContextPreview) { this.showContextPreview = false; return; } if (!this.selectedOption) return; await this.refreshContextPreview(); this.showContextPreview = true; }, buildQueryParams() { const queryParams = { action: 'query', prop: 'revisions', rvprop: 'content', formatversion: 2 }; if (pageInfo.type === 'direct') { queryParams.titles = mw.config.get('wgPageName'); queryParams.rvsection = pageInfo.sectionNum; } else { queryParams.titles = pageInfo.targetPage; queryParams.rvsection = pageInfo.sectionId; } return queryParams; }, buildNewSectionText(sectionText) { const templateName = self.config.voteOptions[this.selectedVote].template; const group = self.config.voteOptions[this.selectedVote].group; const trimmedComment = this.comment.trim(); const headers = self.findGroupHeaders(sectionText.split('\n')); if (headers.length) { const lines = sectionText.split('\n'); const existingHeader = headers.find((h) => h.type === group); const blockEnd = existingHeader ? self.findGroupBlockEnd(lines, existingHeader.index) : -1; const bulletChar = existingHeader ? self.getBlockBulletChar(lines, existingHeader.index, blockEnd) : '*'; let voteLine = bulletChar + ' {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC)'; return { text: self.buildGroupedVoteText(sectionText, group, voteLine) }; } let voteLine = '\n* {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC)'; return { appendtext: voteLine, previewText: sectionText + voteLine }; }, async refreshContextPreview() { if (!this.selectedOption) return; this.contextPreviewLoading = true; try { const api = new mw.Api(); const res = await api.get(this.buildQueryParams()); const sectionText = self.extractContent(res); if (sectionText === null) throw new Error('Bölüm içeriği alınamadı'); const built = this.buildNewSectionText(sectionText); this.contextPreviewWikitext = built.text !== undefined ? built.text : built.previewText; } catch (error) { console.error('Oyver: bölüm önizlemesi başarısız', error); } finally { this.contextPreviewLoading = false; } }, async doVote() { this.submitError = ''; this.submitting = true; try { const api = new mw.Api(); const res = await api.get(this.buildQueryParams()); const sectionText = self.extractContent(res); if (sectionText === null) throw new Error('Bölüm içeriği alınamadı'); const currentUser = mw.config.get('wgUserName'); if (self.hasExistingVote(sectionText, currentUser)) { if (!confirm(this.messages.confirmPossibleDouble)) { this.submitting = false; return; } } const summaryBase = this.editSummaryInput.trim() || this.defaultSummary; const editSummary = summaryBase + self.config.summfix; const editParams = { action: 'edit', summary: editSummary, token: mw.user.tokens.get('csrfToken') }; if (pageInfo.type === 'direct') { editParams.title = mw.config.get('wgPageName'); editParams.section = pageInfo.sectionNum; } else { editParams.title = pageInfo.targetPage; editParams.section = pageInfo.sectionId; } const built = this.buildNewSectionText(sectionText); if (built.text !== undefined) { editParams.text = built.text; } else { editParams.appendtext = built.appendtext; } await api.postWithEditToken(editParams); setTimeout(() => { window.location.reload(); }, 1200); } catch (error) { console.error('Oyver: gönderim başarısız', error); this.submitError = this.messages.editError; this.submitting = false; } } }, template: ` <div class="script-Oyver-panel"> <div class="script-Oyver-panel-header"> <span class="script-Oyver-panel-title"> {{ panelTitle }} <span class="script-Oyver-version">v{{ version }}</span> </span> <div class="script-Oyver-header-actions"> <cdx-button weight="quiet" :disabled="submitting" :aria-label="messages.preferencesButtonLabel" :title="messages.preferencesButtonLabel" @click="togglePreferences"> <cdx-icon v-if="settingsIcon" :icon="settingsIcon"></cdx-icon> </cdx-button> <cdx-button weight="quiet" :disabled="submitting" :aria-label="messages.closeButtonLabel" :title="messages.closeButtonLabel" @click="cancelPanel"> <cdx-icon v-if="closeIcon" :icon="closeIcon"></cdx-icon> </cdx-button> </div> </div> <div v-if="showPreferences" class="script-Oyver-panel-body"> <div class="script-Oyver-prefs"> <p class="script-Oyver-prefs-title">{{ messages.preferencesTitle }}</p> <cdx-field class="script-Oyver-field"> <template #label>{{ messages.preferencesIconSetLabel }}</template> <cdx-radio v-model="prefsIconSet" input-value="codex" :name="prefsIconSetName"> {{ messages.preferencesIconSetCodex }} </cdx-radio> <cdx-radio v-model="prefsIconSet" input-value="onwiki" :name="prefsIconSetName"> {{ messages.preferencesIconSetOnWiki }} </cdx-radio> </cdx-field> <cdx-checkbox v-model="prefsFullContext" class="script-Oyver-field"> {{ messages.preferencesFullContextLabel }} </cdx-checkbox> <div class="script-Oyver-buttons"> <cdx-button weight="primary" action="progressive" :disabled="savingPrefs" @click="savePreferences"> <cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon> {{ messages.preferencesSave }} </cdx-button> <cdx-button :disabled="savingPrefs" @click="showPreferences = false"> {{ messages.cancelButton }} </cdx-button> </div> </div> </div> <div v-else class="script-Oyver-panel-body"> <p>{{ messages.vote }}</p> <div v-for="option in voteOptions" :key="option.value" class="script-Oyver-option"> <cdx-radio v-model="selectedVote" :input-value="option.value" :name="radioName" :disabled="submitting" > <cdx-icon v-if="iconSetPref === 'codex' && option.icon" :icon="option.icon"></cdx-icon> <img v-else-if="iconSetPref === 'onwiki' && option.iconUrl" :src="option.iconUrl" width="20" height="20" alt="" class="script-Oyver-onwiki-icon" > {{ option.label }} </cdx-radio> </div> <cdx-field class="script-Oyver-field"> <template #label>{{ messages.comment }}</template> <cdx-text-area v-model="comment" :rows="3" :disabled="submitting"></cdx-text-area> </cdx-field> <p class="script-Oyver-signature-note">{{ messages.signatureAutoAdd }}</p> <cdx-accordion v-model="showSummary" class="script-Oyver-field"> <template #title>{{ messages.editSummaryLabel }}</template> <cdx-text-input v-model="editSummaryInput" :disabled="submitting" @input="summaryEdited = true" ></cdx-text-input> </cdx-accordion> <cdx-message v-if="submitError" type="error" :allow-user-dismiss="false" class="script-Oyver-status"> {{ submitError }} </cdx-message> <cdx-message v-if="submitting" type="notice" :allow-user-dismiss="false" class="script-Oyver-status"> {{ messages.submitting }} </cdx-message> <div class="script-Oyver-buttons"> <cdx-button weight="primary" action="progressive" :disabled="!canSubmit" @click="doVote"> <cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon> {{ messages.windowButton }} </cdx-button> <cdx-button :disabled="!selectedVote || submitting" @click="togglePreview"> <cdx-icon v-if="previewToggleIcon" :icon="previewToggleIcon"></cdx-icon> {{ previewToggleLabel }} </cdx-button> <cdx-button :disabled="submitting" @click="cancelPanel"> {{ messages.cancelButton }} </cdx-button> </div> <div v-if="showPreview && selectedVote" class="script-Oyver-preview-wrapper"> <div class="script-Oyver-preview-toolbar"> <cdx-button weight="quiet" :disabled="contextPreviewLoading" :aria-label="messages.toggleFullSectionLabel" :title="messages.toggleFullSectionLabel" @click="toggleContextPreview" > <cdx-icon v-if="contextToggleIcon" :icon="contextToggleIcon"></cdx-icon> </cdx-button> </div> <preview-renderer :wikitext="showContextPreview ? contextPreviewWikitext : previewWikitext"></preview-renderer> </div> </div> </div> ` }); vueAppInstance.mount('#' + mountId); } }; Oyver.init(); }); // </nowiki> 9jxbyhjj4lhxc39725r1x8z6gihxub9 17877 17872 2026-10-24T19:12:46Z Brightt11 1786 - fixed signature bug 17877 javascript text/javascript // Oyver - EasyVoteMeta (Author: Max) betiğinin yerelleştirilmiş versiyonu. // Orijinal betik kaynağı/Original script source: https://meta.wikimedia.org/wiki/User:Max/EasyVoteMeta.js // Bu betik, oylama/görüş bildirme sayfalarında bölüm başlıklarına "hızlı oy ver" bağlantısı ekler. // sürüm: 1.1.1 beta. // <nowiki> $(() => { 'use strict'; const Oyver = { version: '1.2 beta', signature: '~~' + '~~', config: { messages: { windowTitle: 'Oyver', windowButton: 'Gönder', cancelButton: 'İptal', vote: 'Görüş belirtmek için oy seçin:', comment: 'Yorum:', signatureAutoAdd: 'İmzanız otomatik olarak eklenecektir.', editSummaryLabel: 'Değişiklik özeti', showPreview: 'Önizlemeyi göster', hidePreview: 'Önizlemeyi gizle', preferencesTitle: 'Tercihler', preferencesIconSetLabel: 'Oy simgesi görünümü', preferencesIconSetCodex: 'Codex simgeleri', preferencesIconSetOnWiki: 'Wiki simgeleri', preferencesFullContextLabel: 'Önizlemede daima tüm bölümü göster', preferencesSave: 'Kaydet', preferencesButtonLabel: 'Tercihler', closeButtonLabel: 'Kapat', toggleFullSectionLabel: 'Tüm bölüm önizlemesini açın/kapatın', loadingPreviewLabel: 'Önizleme yükleniyor', submitting: 'Gönderiliyor. Lütfen internet tarayıcınızı açık tutun.', voteLinkText: 'hızlı oy ver', editError: 'Hata: Bir hata oldu. Klavyenizden F5 tuşuna basarak sayfayı yenileyin ve tekrar deneyin', confirmPossibleDouble: 'Bu başvuruda daha önce görüş yapmışsınız. Devam etmek istediğinizden emin misiniz? Lütfen önceki görüş(ler)inizin üstünü çizerek (<nowiki><s></s></nowiki> kullanarak) oyunuzu geri çekin.' }, voteOptions: { support: { label: 'Destek', template: 'Destek', icon: 'cdxIconCheck', group: 'support', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/9/94/Symbol_support_vote.svg' }, halfsupport: { label: 'Kısmi destek', template: 'Kısmi destek', icon: 'cdxIconCheck', group: 'support', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/8c/GA_candidate.svg' }, oppose: { label: 'Karşı', template: 'Karşı', icon: 'cdxIconClose', group: 'oppose', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/7/7f/Symbol_oppose_vote.svg' }, halfoppose: { label: 'Kısmen karşı', template: 'Kısmen karşı', icon: 'cdxIconClose', group: 'oppose', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/f/f5/BA_candidate.svg' }, neutral: { label: 'Tarafsız', template: 'Tarafsız', icon: 'cdxIconInfoFilled', group: 'neutral', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/89/Symbol_neutral_vote.svg' }, comment: { label: 'Yorum', template: 'Yorum', icon: 'cdxIconSpeechBubble', group: 'neutral', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/d/df/Pictogram_voting_comment_%28orange%29.svg?utm_source=commons.wikimedia.org&utm_campaign=index&utm_content=original', requiresComment: true, skipDoubleCheck: true } }, voteAliases: [], groupOrder: ['support', 'oppose', 'neutral'], groupHeaders: { support: ['Destek', 'Evet'], oppose: ['Karşı', 'Hayır'], neutral: ['Tarafsız', 'Yorum'] }, iconNames: ['cdxIconCheck', 'cdxIconClose', 'cdxIconInfoFilled', 'cdxIconAlert', 'cdxIconSpeechBubble', 'cdxIconEye', 'cdxIconEyeClosed', 'cdxIconSettings'], summfix: ' ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.)', pageConfigs: { 'Vikigezgin:Kullanıcı hakları adaylıkları': { type: 'auto', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [] } }, namespaceConfigs: { 8: { type: 'direct', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [], dbNames: [] }, 828: { type: 'direct', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [], dbNames: [] } } }, icons: {}, prefsPrefix: 'userjs-oyver-', prefsDefaults: { iconSet: 'codex', fullContextPreviewDefault: false }, prefsCache: {}, prefs: {}, currentPageConfig: null, init() { const currentPage = mw.config.get('wgPageName').replace(/_/g, ' '); this.currentPageConfig = this.config.pageConfigs[currentPage] || (this.config.namespaceConfigs || {})[mw.config.get('wgNamespaceNumber')]; if (!this.currentPageConfig) return; const dbNames = this.currentPageConfig.dbNames; if (dbNames && dbNames.length && !dbNames.includes(mw.config.get('wgDBname'))) return; if (mw.user.isAnon()) return; mw.loader.using(['vue', '@wikimedia/codex', 'mediawiki.util', 'mediawiki.api', 'mediawiki.user']).then((require) => { this.Vue = require('vue'); this.Codex = require('@wikimedia/codex'); this.run(); }); }, async loadIcons() { try { const api = new mw.Api(); const data = await api.get({ action: 'query', list: 'codexicons', names: this.config.iconNames.join('|'), formatversion: 2 }); this.icons = (data && data.query && data.query.codexicons) || {}; } catch (error) { this.icons = {}; } }, getPrefFullKey(key) { return this.prefsPrefix + key; }, loadPref(key) { const fullKey = this.getPrefFullKey(key); const fallback = this.prefsDefaults[key]; let value; if (Object.prototype.hasOwnProperty.call(this.prefsCache, fullKey)) { value = this.prefsCache[fullKey]; } else { try { value = window.localStorage ? window.localStorage.getItem(fullKey) : null; } catch (error) { value = null; } } if (value !== null && value !== undefined) { try { return JSON.parse(value); } catch (error) { return fallback; } } try { const serverValue = mw.user.options.get(fullKey, JSON.stringify(fallback)); return JSON.parse(serverValue); } catch (error) { return fallback; } }, loadAllPrefs() { const prefs = {}; Object.keys(this.prefsDefaults).forEach((key) => { prefs[key] = this.loadPref(key); }); return prefs; }, async savePref(key, value) { const fullKey = this.getPrefFullKey(key); const fullValue = JSON.stringify(value); this.prefsCache[fullKey] = fullValue; try { if (window.localStorage) window.localStorage.setItem(fullKey, fullValue); } catch (error) { } try { const api = new mw.Api(); await api.postWithEditToken({ action: 'options', optionname: fullKey, optionvalue: fullValue }); } catch (error) { console.error('Oyver: Hata. Tercih kaydedilemedi.', key, error); } }, async savePrefs(newPrefs) { const keys = Object.keys(newPrefs); for (const key of keys) { await this.savePref(key, newPrefs[key]); } }, async run() { this.addStyle(); this.prefs = this.loadAllPrefs(); await this.loadIcons(); const pageConfig = this.currentPageConfig; const skipPrefixes = (pageConfig.skipPrefixes || []).map((p) => p.toLocaleLowerCase('tr')); $(pageConfig.headingSelector || 'div.mw-heading.mw-heading3').each((_, heading) => { const $heading = $(heading); const editLinks = $heading.find('.mw-editsection'); const headingId = $heading.find('h1,h2,h3,h4,h5,h6').first().attr('id'); if (!headingId) return; const sectionTitle = headingId.replace(/_/g, ' '); const normalizedTitle = sectionTitle.toLocaleLowerCase('tr'); if (skipPrefixes.some((prefix) => normalizedTitle.startsWith(prefix))) return; if (pageConfig.type === 'direct') { this.handleDirectPage(editLinks, sectionTitle, $heading); } else if (pageConfig.type === 'transcluded') { this.handleTranscludedPage(editLinks, sectionTitle, $heading); } else if (pageConfig.type === 'auto') { this.handleAutoPage(editLinks, sectionTitle, $heading); } }); }, handleAutoPage(editLinks, sectionTitle, $heading) { const info = this.getTargetPageInfo(editLinks); if (!info) return; const currentPage = mw.config.get('wgPageName').replace(/_/g, ' '); if (info.targetPage === currentPage && /^\d+$/.test(info.sectionId)) { this.addVoteLink(editLinks, sectionTitle, { type: 'direct', sectionNum: parseInt(info.sectionId, 10) }, $heading); } else { this.addVoteLink(editLinks, sectionTitle, Object.assign({ type: 'transcluded' }, info), $heading); } }, handleDirectPage(editLinks, sectionTitle, $heading) { const sectionNum = this.getSectionNumber(editLinks); if (sectionNum !== -1) { this.addVoteLink(editLinks, sectionTitle, { type: 'direct', sectionNum }, $heading); } }, handleTranscludedPage(editLinks, sectionTitle, $heading) { const targetPageInfo = this.getTargetPageInfo(editLinks); if (targetPageInfo && targetPageInfo.targetPage && targetPageInfo.sectionId) { this.addVoteLink(editLinks, sectionTitle, Object.assign({ type: 'transcluded' }, targetPageInfo), $heading); } }, addStyle() { mw.util.addCSS(` .script-Oyver-link { color: var(--color-progressive, #36c); text-decoration: none; font-weight: bold; } .script-Oyver-link:hover { text-decoration: underline; } .script-Oyver-disabled { color: var(--color-disabled, #a2a9b1) !important; pointer-events: none; } .script-Oyver-divider { border: none; border-top: 1px solid var(--border-color-subtle, #eaecf0); margin: 8px 0 4px; } .script-Oyver-panel { border: 1px solid var(--border-color-base, #a2a9b1); background: var(--background-color-base, #fff); padding: 16px; margin: 8px 0 16px; } .script-Oyver-panel-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } .script-Oyver-panel-title { font-weight: bold; font-size: 15px; } .script-Oyver-version { font-size: 0.7em; font-weight: normal; color: var(--color-subtle, #54595d); vertical-align: middle; margin-left: 2px; } .script-Oyver-header-actions { display: flex; gap: 4px; } .script-Oyver-panel-body p:first-child { margin-top: 0; } .script-Oyver-option { margin: 8px 0; } .script-Oyver-option .cdx-icon { margin-right: 4px; } .script-Oyver-onwiki-icon { margin-right: 4px; vertical-align: middle; } .script-Oyver-field { margin-top: 14px; } .script-Oyver-signature-note { font-style: italic; color: var(--color-subtle, #54595d); font-size: 0.9em; margin-top: 8px; } .script-Oyver-status { margin-top: 12px; } .script-Oyver-buttons { margin-top: 16px; display: flex; gap: 8px; flex-wrap: wrap; } .script-Oyver-preview-wrapper { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border-color-subtle, #eaecf0); } .script-Oyver-preview-toolbar { display: flex; justify-content: flex-end; } .script-Oyver-preview { margin-top: 0; } .script-Oyver-preview-content { font-size: 0.95em; line-height: 1.6; } .script-Oyver-preview-loading { display: flex; align-items: center; gap: 8px; color: var(--color-subtle, #54595d); font-size: 0.88em; } .script-Oyver-prefs { border: 1px solid var(--border-color-subtle, #eaecf0); padding: 12px; } .script-Oyver-prefs-title { font-weight: bold; margin: 0 0 8px; } `); }, getSectionNumber(editLinks) { const editSectionUrl = editLinks.find('a:first').attr('href'); const sectionMatch = editSectionUrl && editSectionUrl.match(/[?&]section=(\d+)(?:&|$)/); return sectionMatch ? parseInt(sectionMatch[1], 10) : -1; }, getTargetPageInfo(editLinks) { const editSectionUrl = editLinks.find('a:first').attr('href'); if (!editSectionUrl) return null; const titleMatch = editSectionUrl.match(/[?&]title=([^&]+)/); const sectionMatch = editSectionUrl.match(/[?&]section=([^&]+)/); if (!titleMatch || !sectionMatch) return null; return { targetPage: decodeURIComponent(titleMatch[1]).replace(/_/g, ' '), sectionId: sectionMatch[1] }; }, extractContent(apiData) { const pages = apiData && apiData.query && apiData.query.pages; if (!pages || !pages[0] || !pages[0].revisions || !pages[0].revisions[0]) return null; return pages[0].revisions[0].content; }, sanitizeSectionTitle(rawTitle) { if (!rawTitle) return ''; const emojiPattern = /(\p{Extended_Pictographic}|\p{Emoji_Presentation}|[\u{1F1E6}-\u{1F1FF}]|\u{FE0F}|\u{200D})/gu; let text = rawTitle.replace(emojiPattern, ''); let cutIndex = -1; for (let i = 0; i < text.length; i++) { if (text[i] === '(') { cutIndex = i; break; } if (text[i] === '[') { if (text[i + 1] === '[') { i++; continue; } cutIndex = i; break; } } if (cutIndex !== -1) { text = text.substring(0, cutIndex); } return text.replace(/\s+/g, ' ').trim(); }, detectGroupHeaderType(line) { const match = line.match(/^;\s*(\S.*)$/); if (!match) return null; const text = match[1].toLocaleLowerCase('tr'); for (const key of this.config.groupOrder) { const words = this.config.groupHeaders[key] || []; for (const word of words) { const w = word.toLocaleLowerCase('tr'); if (text.startsWith(w) && !/^\p{L}/u.test(text.slice(w.length))) { return key; } } } return null; }, findGroupHeaders(lines) { return lines.reduce((acc, line, idx) => { const type = this.detectGroupHeaderType(line); if (type) acc.push({ type, index: idx }); return acc; }, []); }, findGroupBlockEnd(lines, headerIdx) { for (let i = headerIdx + 1; i < lines.length; i++) { if (this.detectGroupHeaderType(lines[i])) return i; } return lines.length; }, getBlockBulletChar(lines, headerIdx, blockEnd) { for (let i = headerIdx + 1; i < blockEnd; i++) { const match = lines[i].match(/^\s*([#*:]+)/); if (match) return match[1]; } return '*'; }, getGroupHeaderText(group) { const words = this.config.groupHeaders[group]; return (words && words[0]) || group; }, buildGroupedVoteText(sectionText, group, voteLine) { const lines = sectionText.split('\n'); const headers = this.findGroupHeaders(lines); const existing = headers.find((h) => h.type === group); const headerLine = ';' + this.getGroupHeaderText(group); if (existing) { const blockEnd = this.findGroupBlockEnd(lines, existing.index); let insertIdx = blockEnd; while (insertIdx - 1 > existing.index && lines[insertIdx - 1].trim() === '') insertIdx--; lines.splice(insertIdx, 0, voteLine); return lines.join('\n'); } const order = this.config.groupOrder; const orderIdx = order.indexOf(group); for (let o = orderIdx + 1; o < order.length; o++) { const laterHeader = headers.find((h) => h.type === order[o]); if (laterHeader) { lines.splice(laterHeader.index, 0, headerLine, voteLine, ''); return lines.join('\n'); } } for (let o = orderIdx - 1; o >= 0; o--) { const earlierHeader = headers.find((h) => h.type === order[o]); if (earlierHeader) { const blockEnd = this.findGroupBlockEnd(lines, earlierHeader.index); lines.splice(blockEnd, 0, '', headerLine, voteLine); return lines.join('\n'); } } let lastNonBlank = lines.length - 1; while (lastNonBlank >= 0 && lines[lastNonBlank].trim() === '') lastNonBlank--; lines.splice(lastNonBlank + 1, 0, '', headerLine, voteLine); return lines.join('\n'); }, hasExistingVote(sectionText, currentUser) { const templates = Object.values(this.config.voteOptions).filter((opt) => !opt.skipDoubleCheck).map((opt) => opt.template); const aliases = this.config.voteAliases || []; const allNames = [...templates, ...aliases].map((name) => mw.util.escapeRegExp(name)); const escapedUser = mw.util.escapeRegExp(currentUser); const pattern = new RegExp('\\{\\{(?:' + allNames.join('|') + ')\\}\\}[^\\n~]{0,300}?' + escapedUser, 'i'); return pattern.test(sectionText); }, addVoteLink(editLinks, sectionTitle, pageInfo, $heading) { const uniqueId = 'script-Oyver-launch-' + Math.random().toString(36).slice(2, 11); const mountId = 'script-Oyver-mount-' + uniqueId; const voteLink = $('<a>') .attr('id', uniqueId) .addClass('script-Oyver-link') .text(this.config.messages.voteLinkText) .attr('href', '#') .click((event) => { event.preventDefault(); if (!voteLink.hasClass('script-Oyver-disabled')) { voteLink.addClass('script-Oyver-disabled'); if (!$('#' + mountId).length) { $heading.after( $('<div>').attr('id', mountId), $('<hr>').addClass('script-Oyver-divider') ); } this.showPanel(sectionTitle, pageInfo, $heading, uniqueId, mountId); } }); editLinks.children().last().before(' | ', voteLink); }, getPreviewComponent() { const loadingLabel = this.config.messages.loadingPreviewLabel; return { components: { CdxProgressBar: this.Codex.CdxProgressBar }, props: { wikitext: { type: String, default: '' } }, data() { return { previewHtml: '', haveHtml: false, loadingLabel }; }, methods: { loadPreview(wikitextToPreview) { new mw.Api().get({ action: 'parse', formatversion: 2, title: mw.config.get('wgPageName'), text: wikitextToPreview, prop: 'text', pst: true, disablelimitreport: true, disableeditsection: true, sectionpreview: true }).then((res) => { if (res && res.parse && res.parse.text) { this.previewHtml = res.parse.text; this.haveHtml = true; } }); } }, watch: { wikitext: { immediate: true, handler(newValue) { this.previewHtml = ''; this.haveHtml = false; this.loadPreview(newValue); } } }, template: ` <div class="script-Oyver-preview"> <div v-if="!haveHtml" class="script-Oyver-preview-loading"> <cdx-progress-bar inline :aria-label="loadingLabel"></cdx-progress-bar> </div> <div v-else class="script-Oyver-preview-content" v-html="previewHtml"></div> </div> ` }; }, showPanel(sectionTitle, pageInfo, $heading, uniqueId, mountId) { const self = this; const rawLiveSectionTitle = $heading.contents().not($heading.find('.mw-editsection')).text().trim() || sectionTitle; const liveSectionTitle = self.sanitizeSectionTitle(rawLiveSectionTitle); const vueAppInstance = self.Vue.createMwApp({ components: { CdxRadio: self.Codex.CdxRadio, CdxCheckbox: self.Codex.CdxCheckbox, CdxTextArea: self.Codex.CdxTextArea, CdxTextInput: self.Codex.CdxTextInput, CdxField: self.Codex.CdxField, CdxAccordion: self.Codex.CdxAccordion, CdxMessage: self.Codex.CdxMessage, CdxButton: self.Codex.CdxButton, CdxIcon: self.Codex.CdxIcon, previewRenderer: self.getPreviewComponent() }, data() { return { version: self.version, messages: self.config.messages, sectionTitle: liveSectionTitle, uniqueId, voteOptions: Object.entries(self.config.voteOptions).map(([value, opt]) => ({ value, label: opt.label, group: opt.group, icon: self.icons[opt.icon] || null, iconUrl: opt.iconUrl, requiresComment: !!opt.requiresComment })), selectedVote: null, comment: '', showSummary: false, editSummaryInput: '/* ' + liveSectionTitle + ' */ ', summaryEdited: false, showPreview: false, submitting: false, submitError: '', showContextPreview: false, contextPreviewWikitext: '', contextPreviewLoading: false, contextPreviewDebounce: null, showPreferences: false, iconSetPref: self.prefs.iconSet, prefsIconSet: self.prefs.iconSet, prefsFullContext: self.prefs.fullContextPreviewDefault, savingPrefs: false, checkIcon: self.icons.cdxIconCheck || null, closeIcon: self.icons.cdxIconClose || null, eyeIcon: self.icons.cdxIconEye || null, eyeClosedIcon: self.icons.cdxIconEyeClosed || null, settingsIcon: self.icons.cdxIconSettings || null }; }, computed: { panelTitle() { return this.messages.windowTitle + ': ' + this.sectionTitle; }, radioName() { return 'script-Oyver-vote-' + this.uniqueId; }, prefsIconSetName() { return 'script-Oyver-prefs-iconset-' + this.uniqueId; }, selectedOption() { return this.voteOptions.find((opt) => opt.value === this.selectedVote) || null; }, defaultSummary() { const base = '/* ' + this.sectionTitle + ' */ '; return this.selectedOption ? base + this.selectedOption.label : base; }, canSubmit() { if (this.submitting || !this.selectedVote) return false; if (this.selectedOption && this.selectedOption.requiresComment && !this.comment.trim()) return false; return true; }, previewToggleIcon() { return this.showPreview ? this.eyeClosedIcon : this.eyeIcon; }, previewToggleLabel() { return this.showPreview ? this.messages.hidePreview : this.messages.showPreview; }, contextToggleIcon() { return this.showContextPreview ? this.eyeClosedIcon : this.eyeIcon; }, previewWikitext() { if (!this.selectedOption) return ''; const templateName = self.config.voteOptions[this.selectedVote].template; const trimmedComment = this.comment.trim(); let voteLine = '* {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' voteLine += ' ' + self.signature;'; return voteLine; } }, watch: { selectedVote() { if (!this.summaryEdited) { this.editSummaryInput = this.defaultSummary; } if (this.showContextPreview) { this.refreshContextPreview(); } }, comment() { if (!this.showContextPreview) return; clearTimeout(this.contextPreviewDebounce); this.contextPreviewDebounce = setTimeout(() => { this.refreshContextPreview(); }, 400); } }, methods: { cancelPanel() { if (this.submitting) return; vueAppInstance.unmount(); $('#' + mountId).empty(); $('#' + uniqueId).removeClass('script-Oyver-disabled'); }, togglePreferences() { if (this.submitting) return; this.prefsIconSet = self.prefs.iconSet; this.prefsFullContext = self.prefs.fullContextPreviewDefault; this.showPreferences = !this.showPreferences; }, async savePreferences() { this.savingPrefs = true; const newPrefs = { iconSet: this.prefsIconSet, fullContextPreviewDefault: this.prefsFullContext }; await self.savePrefs(newPrefs); self.prefs = Object.assign({}, self.prefs, newPrefs); this.iconSetPref = self.prefs.iconSet; this.savingPrefs = false; this.showPreferences = false; }, async togglePreview() { if (this.showPreview) { this.showPreview = false; return; } if (self.prefs.fullContextPreviewDefault && !this.showContextPreview) { await this.toggleContextPreview(); } this.showPreview = true; }, async toggleContextPreview() { if (this.showContextPreview) { this.showContextPreview = false; return; } if (!this.selectedOption) return; await this.refreshContextPreview(); this.showContextPreview = true; }, buildQueryParams() { const queryParams = { action: 'query', prop: 'revisions', rvprop: 'content', formatversion: 2 }; if (pageInfo.type === 'direct') { queryParams.titles = mw.config.get('wgPageName'); queryParams.rvsection = pageInfo.sectionNum; } else { queryParams.titles = pageInfo.targetPage; queryParams.rvsection = pageInfo.sectionId; } return queryParams; }, buildNewSectionText(sectionText) { const templateName = self.config.voteOptions[this.selectedVote].template; const group = self.config.voteOptions[this.selectedVote].group; const trimmedComment = this.comment.trim(); const headers = self.findGroupHeaders(sectionText.split('\n')); if (headers.length) { const lines = sectionText.split('\n'); const existingHeader = headers.find((h) => h.type === group); const blockEnd = existingHeader ? self.findGroupBlockEnd(lines, existingHeader.index) : -1; const bulletChar = existingHeader ? self.getBlockBulletChar(lines, existingHeader.index, blockEnd) : '*'; let voteLine = bulletChar + ' {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC)'; return { text: self.buildGroupedVoteText(sectionText, group, voteLine) }; } let voteLine = '\n* {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC)'; return { appendtext: voteLine, previewText: sectionText + voteLine }; }, async refreshContextPreview() { if (!this.selectedOption) return; this.contextPreviewLoading = true; try { const api = new mw.Api(); const res = await api.get(this.buildQueryParams()); const sectionText = self.extractContent(res); if (sectionText === null) throw new Error('Bölüm içeriği alınamadı'); const built = this.buildNewSectionText(sectionText); this.contextPreviewWikitext = built.text !== undefined ? built.text : built.previewText; } catch (error) { console.error('Oyver: bölüm önizlemesi başarısız', error); } finally { this.contextPreviewLoading = false; } }, async doVote() { this.submitError = ''; this.submitting = true; try { const api = new mw.Api(); const res = await api.get(this.buildQueryParams()); const sectionText = self.extractContent(res); if (sectionText === null) throw new Error('Bölüm içeriği alınamadı'); const currentUser = mw.config.get('wgUserName'); if (self.hasExistingVote(sectionText, currentUser)) { if (!confirm(this.messages.confirmPossibleDouble)) { this.submitting = false; return; } } const summaryBase = this.editSummaryInput.trim() || this.defaultSummary; const editSummary = summaryBase + self.config.summfix; const editParams = { action: 'edit', summary: editSummary, token: mw.user.tokens.get('csrfToken') }; if (pageInfo.type === 'direct') { editParams.title = mw.config.get('wgPageName'); editParams.section = pageInfo.sectionNum; } else { editParams.title = pageInfo.targetPage; editParams.section = pageInfo.sectionId; } const built = this.buildNewSectionText(sectionText); if (built.text !== undefined) { editParams.text = built.text; } else { editParams.appendtext = built.appendtext; } await api.postWithEditToken(editParams); setTimeout(() => { window.location.reload(); }, 1200); } catch (error) { console.error('Oyver: gönderim başarısız', error); this.submitError = this.messages.editError; this.submitting = false; } } }, template: ` <div class="script-Oyver-panel"> <div class="script-Oyver-panel-header"> <span class="script-Oyver-panel-title"> {{ panelTitle }} <span class="script-Oyver-version">v{{ version }}</span> </span> <div class="script-Oyver-header-actions"> <cdx-button weight="quiet" :disabled="submitting" :aria-label="messages.preferencesButtonLabel" :title="messages.preferencesButtonLabel" @click="togglePreferences"> <cdx-icon v-if="settingsIcon" :icon="settingsIcon"></cdx-icon> </cdx-button> <cdx-button weight="quiet" :disabled="submitting" :aria-label="messages.closeButtonLabel" :title="messages.closeButtonLabel" @click="cancelPanel"> <cdx-icon v-if="closeIcon" :icon="closeIcon"></cdx-icon> </cdx-button> </div> </div> <div v-if="showPreferences" class="script-Oyver-panel-body"> <div class="script-Oyver-prefs"> <p class="script-Oyver-prefs-title">{{ messages.preferencesTitle }}</p> <cdx-field class="script-Oyver-field"> <template #label>{{ messages.preferencesIconSetLabel }}</template> <cdx-radio v-model="prefsIconSet" input-value="codex" :name="prefsIconSetName"> {{ messages.preferencesIconSetCodex }} </cdx-radio> <cdx-radio v-model="prefsIconSet" input-value="onwiki" :name="prefsIconSetName"> {{ messages.preferencesIconSetOnWiki }} </cdx-radio> </cdx-field> <cdx-checkbox v-model="prefsFullContext" class="script-Oyver-field"> {{ messages.preferencesFullContextLabel }} </cdx-checkbox> <div class="script-Oyver-buttons"> <cdx-button weight="primary" action="progressive" :disabled="savingPrefs" @click="savePreferences"> <cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon> {{ messages.preferencesSave }} </cdx-button> <cdx-button :disabled="savingPrefs" @click="showPreferences = false"> {{ messages.cancelButton }} </cdx-button> </div> </div> </div> <div v-else class="script-Oyver-panel-body"> <p>{{ messages.vote }}</p> <div v-for="option in voteOptions" :key="option.value" class="script-Oyver-option"> <cdx-radio v-model="selectedVote" :input-value="option.value" :name="radioName" :disabled="submitting" > <cdx-icon v-if="iconSetPref === 'codex' && option.icon" :icon="option.icon"></cdx-icon> <img v-else-if="iconSetPref === 'onwiki' && option.iconUrl" :src="option.iconUrl" width="20" height="20" alt="" class="script-Oyver-onwiki-icon" > {{ option.label }} </cdx-radio> </div> <cdx-field class="script-Oyver-field"> <template #label>{{ messages.comment }}</template> <cdx-text-area v-model="comment" :rows="3" :disabled="submitting"></cdx-text-area> </cdx-field> <p class="script-Oyver-signature-note">{{ messages.signatureAutoAdd }}</p> <cdx-accordion v-model="showSummary" class="script-Oyver-field"> <template #title>{{ messages.editSummaryLabel }}</template> <cdx-text-input v-model="editSummaryInput" :disabled="submitting" @input="summaryEdited = true" ></cdx-text-input> </cdx-accordion> <cdx-message v-if="submitError" type="error" :allow-user-dismiss="false" class="script-Oyver-status"> {{ submitError }} </cdx-message> <cdx-message v-if="submitting" type="notice" :allow-user-dismiss="false" class="script-Oyver-status"> {{ messages.submitting }} </cdx-message> <div class="script-Oyver-buttons"> <cdx-button weight="primary" action="progressive" :disabled="!canSubmit" @click="doVote"> <cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon> {{ messages.windowButton }} </cdx-button> <cdx-button :disabled="!selectedVote || submitting" @click="togglePreview"> <cdx-icon v-if="previewToggleIcon" :icon="previewToggleIcon"></cdx-icon> {{ previewToggleLabel }} </cdx-button> <cdx-button :disabled="submitting" @click="cancelPanel"> {{ messages.cancelButton }} </cdx-button> </div> <div v-if="showPreview && selectedVote" class="script-Oyver-preview-wrapper"> <div class="script-Oyver-preview-toolbar"> <cdx-button weight="quiet" :disabled="contextPreviewLoading" :aria-label="messages.toggleFullSectionLabel" :title="messages.toggleFullSectionLabel" @click="toggleContextPreview" > <cdx-icon v-if="contextToggleIcon" :icon="contextToggleIcon"></cdx-icon> </cdx-button> </div> <preview-renderer :wikitext="showContextPreview ? contextPreviewWikitext : previewWikitext"></preview-renderer> </div> </div> </div> ` }); vueAppInstance.mount('#' + mountId); } }; Oyver.init(); }); // </nowiki> bll6hopx56tgwg7wrf15p9x7vsrp98m 17878 17877 2026-10-24T19:14:18Z Brightt11 1786 fix again 17878 javascript text/javascript // Oyver - EasyVoteMeta (Author: Max) betiğinin yerelleştirilmiş versiyonu. // Orijinal betik kaynağı/Original script source: https://meta.wikimedia.org/wiki/User:Max/EasyVoteMeta.js // Bu betik, oylama/görüş bildirme sayfalarında bölüm başlıklarına "hızlı oy ver" bağlantısı ekler. // sürüm: 1.1.1 beta. // <nowiki> $(() => { 'use strict'; const Oyver = { version: '1.2 beta', signature: '~~' + '~~', config: { messages: { windowTitle: 'Oyver', windowButton: 'Gönder', cancelButton: 'İptal', vote: 'Görüş belirtmek için oy seçin:', comment: 'Yorum:', signatureAutoAdd: 'İmzanız otomatik olarak eklenecektir.', editSummaryLabel: 'Değişiklik özeti', showPreview: 'Önizlemeyi göster', hidePreview: 'Önizlemeyi gizle', preferencesTitle: 'Tercihler', preferencesIconSetLabel: 'Oy simgesi görünümü', preferencesIconSetCodex: 'Codex simgeleri', preferencesIconSetOnWiki: 'Wiki simgeleri', preferencesFullContextLabel: 'Önizlemede daima tüm bölümü göster', preferencesSave: 'Kaydet', preferencesButtonLabel: 'Tercihler', closeButtonLabel: 'Kapat', toggleFullSectionLabel: 'Tüm bölüm önizlemesini açın/kapatın', loadingPreviewLabel: 'Önizleme yükleniyor', submitting: 'Gönderiliyor. Lütfen internet tarayıcınızı açık tutun.', voteLinkText: 'hızlı oy ver', editError: 'Hata: Bir hata oldu. Klavyenizden F5 tuşuna basarak sayfayı yenileyin ve tekrar deneyin', confirmPossibleDouble: 'Bu başvuruda daha önce görüş yapmışsınız. Devam etmek istediğinizden emin misiniz? Lütfen önceki görüş(ler)inizin üstünü çizerek (<nowiki><s></s></nowiki> kullanarak) oyunuzu geri çekin.' }, voteOptions: { support: { label: 'Destek', template: 'Destek', icon: 'cdxIconCheck', group: 'support', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/9/94/Symbol_support_vote.svg' }, halfsupport: { label: 'Kısmi destek', template: 'Kısmi destek', icon: 'cdxIconCheck', group: 'support', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/8c/GA_candidate.svg' }, oppose: { label: 'Karşı', template: 'Karşı', icon: 'cdxIconClose', group: 'oppose', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/7/7f/Symbol_oppose_vote.svg' }, halfoppose: { label: 'Kısmen karşı', template: 'Kısmen karşı', icon: 'cdxIconClose', group: 'oppose', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/f/f5/BA_candidate.svg' }, neutral: { label: 'Tarafsız', template: 'Tarafsız', icon: 'cdxIconInfoFilled', group: 'neutral', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/89/Symbol_neutral_vote.svg' }, comment: { label: 'Yorum', template: 'Yorum', icon: 'cdxIconSpeechBubble', group: 'neutral', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/d/df/Pictogram_voting_comment_%28orange%29.svg?utm_source=commons.wikimedia.org&utm_campaign=index&utm_content=original', requiresComment: true, skipDoubleCheck: true } }, voteAliases: [], groupOrder: ['support', 'oppose', 'neutral'], groupHeaders: { support: ['Destek', 'Evet'], oppose: ['Karşı', 'Hayır'], neutral: ['Tarafsız', 'Yorum'] }, iconNames: ['cdxIconCheck', 'cdxIconClose', 'cdxIconInfoFilled', 'cdxIconAlert', 'cdxIconSpeechBubble', 'cdxIconEye', 'cdxIconEyeClosed', 'cdxIconSettings'], summfix: ' ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.)', pageConfigs: { 'Vikigezgin:Kullanıcı hakları adaylıkları': { type: 'auto', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [] } }, namespaceConfigs: { 8: { type: 'direct', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [], dbNames: [] }, 828: { type: 'direct', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [], dbNames: [] } } }, icons: {}, prefsPrefix: 'userjs-oyver-', prefsDefaults: { iconSet: 'codex', fullContextPreviewDefault: false }, prefsCache: {}, prefs: {}, currentPageConfig: null, init() { const currentPage = mw.config.get('wgPageName').replace(/_/g, ' '); this.currentPageConfig = this.config.pageConfigs[currentPage] || (this.config.namespaceConfigs || {})[mw.config.get('wgNamespaceNumber')]; if (!this.currentPageConfig) return; const dbNames = this.currentPageConfig.dbNames; if (dbNames && dbNames.length && !dbNames.includes(mw.config.get('wgDBname'))) return; if (mw.user.isAnon()) return; mw.loader.using(['vue', '@wikimedia/codex', 'mediawiki.util', 'mediawiki.api', 'mediawiki.user']).then((require) => { this.Vue = require('vue'); this.Codex = require('@wikimedia/codex'); this.run(); }); }, async loadIcons() { try { const api = new mw.Api(); const data = await api.get({ action: 'query', list: 'codexicons', names: this.config.iconNames.join('|'), formatversion: 2 }); this.icons = (data && data.query && data.query.codexicons) || {}; } catch (error) { this.icons = {}; } }, getPrefFullKey(key) { return this.prefsPrefix + key; }, loadPref(key) { const fullKey = this.getPrefFullKey(key); const fallback = this.prefsDefaults[key]; let value; if (Object.prototype.hasOwnProperty.call(this.prefsCache, fullKey)) { value = this.prefsCache[fullKey]; } else { try { value = window.localStorage ? window.localStorage.getItem(fullKey) : null; } catch (error) { value = null; } } if (value !== null && value !== undefined) { try { return JSON.parse(value); } catch (error) { return fallback; } } try { const serverValue = mw.user.options.get(fullKey, JSON.stringify(fallback)); return JSON.parse(serverValue); } catch (error) { return fallback; } }, loadAllPrefs() { const prefs = {}; Object.keys(this.prefsDefaults).forEach((key) => { prefs[key] = this.loadPref(key); }); return prefs; }, async savePref(key, value) { const fullKey = this.getPrefFullKey(key); const fullValue = JSON.stringify(value); this.prefsCache[fullKey] = fullValue; try { if (window.localStorage) window.localStorage.setItem(fullKey, fullValue); } catch (error) { } try { const api = new mw.Api(); await api.postWithEditToken({ action: 'options', optionname: fullKey, optionvalue: fullValue }); } catch (error) { console.error('Oyver: Hata. Tercih kaydedilemedi.', key, error); } }, async savePrefs(newPrefs) { const keys = Object.keys(newPrefs); for (const key of keys) { await this.savePref(key, newPrefs[key]); } }, async run() { this.addStyle(); this.prefs = this.loadAllPrefs(); await this.loadIcons(); const pageConfig = this.currentPageConfig; const skipPrefixes = (pageConfig.skipPrefixes || []).map((p) => p.toLocaleLowerCase('tr')); $(pageConfig.headingSelector || 'div.mw-heading.mw-heading3').each((_, heading) => { const $heading = $(heading); const editLinks = $heading.find('.mw-editsection'); const headingId = $heading.find('h1,h2,h3,h4,h5,h6').first().attr('id'); if (!headingId) return; const sectionTitle = headingId.replace(/_/g, ' '); const normalizedTitle = sectionTitle.toLocaleLowerCase('tr'); if (skipPrefixes.some((prefix) => normalizedTitle.startsWith(prefix))) return; if (pageConfig.type === 'direct') { this.handleDirectPage(editLinks, sectionTitle, $heading); } else if (pageConfig.type === 'transcluded') { this.handleTranscludedPage(editLinks, sectionTitle, $heading); } else if (pageConfig.type === 'auto') { this.handleAutoPage(editLinks, sectionTitle, $heading); } }); }, handleAutoPage(editLinks, sectionTitle, $heading) { const info = this.getTargetPageInfo(editLinks); if (!info) return; const currentPage = mw.config.get('wgPageName').replace(/_/g, ' '); if (info.targetPage === currentPage && /^\d+$/.test(info.sectionId)) { this.addVoteLink(editLinks, sectionTitle, { type: 'direct', sectionNum: parseInt(info.sectionId, 10) }, $heading); } else { this.addVoteLink(editLinks, sectionTitle, Object.assign({ type: 'transcluded' }, info), $heading); } }, handleDirectPage(editLinks, sectionTitle, $heading) { const sectionNum = this.getSectionNumber(editLinks); if (sectionNum !== -1) { this.addVoteLink(editLinks, sectionTitle, { type: 'direct', sectionNum }, $heading); } }, handleTranscludedPage(editLinks, sectionTitle, $heading) { const targetPageInfo = this.getTargetPageInfo(editLinks); if (targetPageInfo && targetPageInfo.targetPage && targetPageInfo.sectionId) { this.addVoteLink(editLinks, sectionTitle, Object.assign({ type: 'transcluded' }, targetPageInfo), $heading); } }, addStyle() { mw.util.addCSS(` .script-Oyver-link { color: var(--color-progressive, #36c); text-decoration: none; font-weight: bold; } .script-Oyver-link:hover { text-decoration: underline; } .script-Oyver-disabled { color: var(--color-disabled, #a2a9b1) !important; pointer-events: none; } .script-Oyver-divider { border: none; border-top: 1px solid var(--border-color-subtle, #eaecf0); margin: 8px 0 4px; } .script-Oyver-panel { border: 1px solid var(--border-color-base, #a2a9b1); background: var(--background-color-base, #fff); padding: 16px; margin: 8px 0 16px; } .script-Oyver-panel-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } .script-Oyver-panel-title { font-weight: bold; font-size: 15px; } .script-Oyver-version { font-size: 0.7em; font-weight: normal; color: var(--color-subtle, #54595d); vertical-align: middle; margin-left: 2px; } .script-Oyver-header-actions { display: flex; gap: 4px; } .script-Oyver-panel-body p:first-child { margin-top: 0; } .script-Oyver-option { margin: 8px 0; } .script-Oyver-option .cdx-icon { margin-right: 4px; } .script-Oyver-onwiki-icon { margin-right: 4px; vertical-align: middle; } .script-Oyver-field { margin-top: 14px; } .script-Oyver-signature-note { font-style: italic; color: var(--color-subtle, #54595d); font-size: 0.9em; margin-top: 8px; } .script-Oyver-status { margin-top: 12px; } .script-Oyver-buttons { margin-top: 16px; display: flex; gap: 8px; flex-wrap: wrap; } .script-Oyver-preview-wrapper { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border-color-subtle, #eaecf0); } .script-Oyver-preview-toolbar { display: flex; justify-content: flex-end; } .script-Oyver-preview { margin-top: 0; } .script-Oyver-preview-content { font-size: 0.95em; line-height: 1.6; } .script-Oyver-preview-loading { display: flex; align-items: center; gap: 8px; color: var(--color-subtle, #54595d); font-size: 0.88em; } .script-Oyver-prefs { border: 1px solid var(--border-color-subtle, #eaecf0); padding: 12px; } .script-Oyver-prefs-title { font-weight: bold; margin: 0 0 8px; } `); }, getSectionNumber(editLinks) { const editSectionUrl = editLinks.find('a:first').attr('href'); const sectionMatch = editSectionUrl && editSectionUrl.match(/[?&]section=(\d+)(?:&|$)/); return sectionMatch ? parseInt(sectionMatch[1], 10) : -1; }, getTargetPageInfo(editLinks) { const editSectionUrl = editLinks.find('a:first').attr('href'); if (!editSectionUrl) return null; const titleMatch = editSectionUrl.match(/[?&]title=([^&]+)/); const sectionMatch = editSectionUrl.match(/[?&]section=([^&]+)/); if (!titleMatch || !sectionMatch) return null; return { targetPage: decodeURIComponent(titleMatch[1]).replace(/_/g, ' '), sectionId: sectionMatch[1] }; }, extractContent(apiData) { const pages = apiData && apiData.query && apiData.query.pages; if (!pages || !pages[0] || !pages[0].revisions || !pages[0].revisions[0]) return null; return pages[0].revisions[0].content; }, sanitizeSectionTitle(rawTitle) { if (!rawTitle) return ''; const emojiPattern = /(\p{Extended_Pictographic}|\p{Emoji_Presentation}|[\u{1F1E6}-\u{1F1FF}]|\u{FE0F}|\u{200D})/gu; let text = rawTitle.replace(emojiPattern, ''); let cutIndex = -1; for (let i = 0; i < text.length; i++) { if (text[i] === '(') { cutIndex = i; break; } if (text[i] === '[') { if (text[i + 1] === '[') { i++; continue; } cutIndex = i; break; } } if (cutIndex !== -1) { text = text.substring(0, cutIndex); } return text.replace(/\s+/g, ' ').trim(); }, detectGroupHeaderType(line) { const match = line.match(/^;\s*(\S.*)$/); if (!match) return null; const text = match[1].toLocaleLowerCase('tr'); for (const key of this.config.groupOrder) { const words = this.config.groupHeaders[key] || []; for (const word of words) { const w = word.toLocaleLowerCase('tr'); if (text.startsWith(w) && !/^\p{L}/u.test(text.slice(w.length))) { return key; } } } return null; }, findGroupHeaders(lines) { return lines.reduce((acc, line, idx) => { const type = this.detectGroupHeaderType(line); if (type) acc.push({ type, index: idx }); return acc; }, []); }, findGroupBlockEnd(lines, headerIdx) { for (let i = headerIdx + 1; i < lines.length; i++) { if (this.detectGroupHeaderType(lines[i])) return i; } return lines.length; }, getBlockBulletChar(lines, headerIdx, blockEnd) { for (let i = headerIdx + 1; i < blockEnd; i++) { const match = lines[i].match(/^\s*([#*:]+)/); if (match) return match[1]; } return '*'; }, getGroupHeaderText(group) { const words = this.config.groupHeaders[group]; return (words && words[0]) || group; }, buildGroupedVoteText(sectionText, group, voteLine) { const lines = sectionText.split('\n'); const headers = this.findGroupHeaders(lines); const existing = headers.find((h) => h.type === group); const headerLine = ';' + this.getGroupHeaderText(group); if (existing) { const blockEnd = this.findGroupBlockEnd(lines, existing.index); let insertIdx = blockEnd; while (insertIdx - 1 > existing.index && lines[insertIdx - 1].trim() === '') insertIdx--; lines.splice(insertIdx, 0, voteLine); return lines.join('\n'); } const order = this.config.groupOrder; const orderIdx = order.indexOf(group); for (let o = orderIdx + 1; o < order.length; o++) { const laterHeader = headers.find((h) => h.type === order[o]); if (laterHeader) { lines.splice(laterHeader.index, 0, headerLine, voteLine, ''); return lines.join('\n'); } } for (let o = orderIdx - 1; o >= 0; o--) { const earlierHeader = headers.find((h) => h.type === order[o]); if (earlierHeader) { const blockEnd = this.findGroupBlockEnd(lines, earlierHeader.index); lines.splice(blockEnd, 0, '', headerLine, voteLine); return lines.join('\n'); } } let lastNonBlank = lines.length - 1; while (lastNonBlank >= 0 && lines[lastNonBlank].trim() === '') lastNonBlank--; lines.splice(lastNonBlank + 1, 0, '', headerLine, voteLine); return lines.join('\n'); }, hasExistingVote(sectionText, currentUser) { const templates = Object.values(this.config.voteOptions).filter((opt) => !opt.skipDoubleCheck).map((opt) => opt.template); const aliases = this.config.voteAliases || []; const allNames = [...templates, ...aliases].map((name) => mw.util.escapeRegExp(name)); const escapedUser = mw.util.escapeRegExp(currentUser); const pattern = new RegExp('\\{\\{(?:' + allNames.join('|') + ')\\}\\}[^\\n~]{0,300}?' + escapedUser, 'i'); return pattern.test(sectionText); }, addVoteLink(editLinks, sectionTitle, pageInfo, $heading) { const uniqueId = 'script-Oyver-launch-' + Math.random().toString(36).slice(2, 11); const mountId = 'script-Oyver-mount-' + uniqueId; const voteLink = $('<a>') .attr('id', uniqueId) .addClass('script-Oyver-link') .text(this.config.messages.voteLinkText) .attr('href', '#') .click((event) => { event.preventDefault(); if (!voteLink.hasClass('script-Oyver-disabled')) { voteLink.addClass('script-Oyver-disabled'); if (!$('#' + mountId).length) { $heading.after( $('<div>').attr('id', mountId), $('<hr>').addClass('script-Oyver-divider') ); } this.showPanel(sectionTitle, pageInfo, $heading, uniqueId, mountId); } }); editLinks.children().last().before(' | ', voteLink); }, getPreviewComponent() { const loadingLabel = this.config.messages.loadingPreviewLabel; return { components: { CdxProgressBar: this.Codex.CdxProgressBar }, props: { wikitext: { type: String, default: '' } }, data() { return { previewHtml: '', haveHtml: false, loadingLabel }; }, methods: { loadPreview(wikitextToPreview) { new mw.Api().get({ action: 'parse', formatversion: 2, title: mw.config.get('wgPageName'), text: wikitextToPreview, prop: 'text', pst: true, disablelimitreport: true, disableeditsection: true, sectionpreview: true }).then((res) => { if (res && res.parse && res.parse.text) { this.previewHtml = res.parse.text; this.haveHtml = true; } }); } }, watch: { wikitext: { immediate: true, handler(newValue) { this.previewHtml = ''; this.haveHtml = false; this.loadPreview(newValue); } } }, template: ` <div class="script-Oyver-preview"> <div v-if="!haveHtml" class="script-Oyver-preview-loading"> <cdx-progress-bar inline :aria-label="loadingLabel"></cdx-progress-bar> </div> <div v-else class="script-Oyver-preview-content" v-html="previewHtml"></div> </div> ` }; }, showPanel(sectionTitle, pageInfo, $heading, uniqueId, mountId) { const self = this; const rawLiveSectionTitle = $heading.contents().not($heading.find('.mw-editsection')).text().trim() || sectionTitle; const liveSectionTitle = self.sanitizeSectionTitle(rawLiveSectionTitle); const vueAppInstance = self.Vue.createMwApp({ components: { CdxRadio: self.Codex.CdxRadio, CdxCheckbox: self.Codex.CdxCheckbox, CdxTextArea: self.Codex.CdxTextArea, CdxTextInput: self.Codex.CdxTextInput, CdxField: self.Codex.CdxField, CdxAccordion: self.Codex.CdxAccordion, CdxMessage: self.Codex.CdxMessage, CdxButton: self.Codex.CdxButton, CdxIcon: self.Codex.CdxIcon, previewRenderer: self.getPreviewComponent() }, data() { return { version: self.version, messages: self.config.messages, sectionTitle: liveSectionTitle, uniqueId, voteOptions: Object.entries(self.config.voteOptions).map(([value, opt]) => ({ value, label: opt.label, group: opt.group, icon: self.icons[opt.icon] || null, iconUrl: opt.iconUrl, requiresComment: !!opt.requiresComment })), selectedVote: null, comment: '', showSummary: false, editSummaryInput: '/* ' + liveSectionTitle + ' */ ', summaryEdited: false, showPreview: false, submitting: false, submitError: '', showContextPreview: false, contextPreviewWikitext: '', contextPreviewLoading: false, contextPreviewDebounce: null, showPreferences: false, iconSetPref: self.prefs.iconSet, prefsIconSet: self.prefs.iconSet, prefsFullContext: self.prefs.fullContextPreviewDefault, savingPrefs: false, checkIcon: self.icons.cdxIconCheck || null, closeIcon: self.icons.cdxIconClose || null, eyeIcon: self.icons.cdxIconEye || null, eyeClosedIcon: self.icons.cdxIconEyeClosed || null, settingsIcon: self.icons.cdxIconSettings || null }; }, computed: { panelTitle() { return this.messages.windowTitle + ': ' + this.sectionTitle; }, radioName() { return 'script-Oyver-vote-' + this.uniqueId; }, prefsIconSetName() { return 'script-Oyver-prefs-iconset-' + this.uniqueId; }, selectedOption() { return this.voteOptions.find((opt) => opt.value === this.selectedVote) || null; }, defaultSummary() { const base = '/* ' + this.sectionTitle + ' */ '; return this.selectedOption ? base + this.selectedOption.label : base; }, canSubmit() { if (this.submitting || !this.selectedVote) return false; if (this.selectedOption && this.selectedOption.requiresComment && !this.comment.trim()) return false; return true; }, previewToggleIcon() { return this.showPreview ? this.eyeClosedIcon : this.eyeIcon; }, previewToggleLabel() { return this.showPreview ? this.messages.hidePreview : this.messages.showPreview; }, contextToggleIcon() { return this.showContextPreview ? this.eyeClosedIcon : this.eyeIcon; }, previewWikitext() { if (!this.selectedOption) return ''; const templateName = self.config.voteOptions[this.selectedVote].template; const trimmedComment = this.comment.trim(); let voteLine = '* {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' ' + self.signature; return voteLine; } }, watch: { selectedVote() { if (!this.summaryEdited) { this.editSummaryInput = this.defaultSummary; } if (this.showContextPreview) { this.refreshContextPreview(); } }, comment() { if (!this.showContextPreview) return; clearTimeout(this.contextPreviewDebounce); this.contextPreviewDebounce = setTimeout(() => { this.refreshContextPreview(); }, 400); } }, methods: { cancelPanel() { if (this.submitting) return; vueAppInstance.unmount(); $('#' + mountId).empty(); $('#' + uniqueId).removeClass('script-Oyver-disabled'); }, togglePreferences() { if (this.submitting) return; this.prefsIconSet = self.prefs.iconSet; this.prefsFullContext = self.prefs.fullContextPreviewDefault; this.showPreferences = !this.showPreferences; }, async savePreferences() { this.savingPrefs = true; const newPrefs = { iconSet: this.prefsIconSet, fullContextPreviewDefault: this.prefsFullContext }; await self.savePrefs(newPrefs); self.prefs = Object.assign({}, self.prefs, newPrefs); this.iconSetPref = self.prefs.iconSet; this.savingPrefs = false; this.showPreferences = false; }, async togglePreview() { if (this.showPreview) { this.showPreview = false; return; } if (self.prefs.fullContextPreviewDefault && !this.showContextPreview) { await this.toggleContextPreview(); } this.showPreview = true; }, async toggleContextPreview() { if (this.showContextPreview) { this.showContextPreview = false; return; } if (!this.selectedOption) return; await this.refreshContextPreview(); this.showContextPreview = true; }, buildQueryParams() { const queryParams = { action: 'query', prop: 'revisions', rvprop: 'content', formatversion: 2 }; if (pageInfo.type === 'direct') { queryParams.titles = mw.config.get('wgPageName'); queryParams.rvsection = pageInfo.sectionNum; } else { queryParams.titles = pageInfo.targetPage; queryParams.rvsection = pageInfo.sectionId; } return queryParams; }, buildNewSectionText(sectionText) { const templateName = self.config.voteOptions[this.selectedVote].template; const group = self.config.voteOptions[this.selectedVote].group; const trimmedComment = this.comment.trim(); const headers = self.findGroupHeaders(sectionText.split('\n')); if (headers.length) { const lines = sectionText.split('\n'); const existingHeader = headers.find((h) => h.type === group); const blockEnd = existingHeader ? self.findGroupBlockEnd(lines, existingHeader.index) : -1; const bulletChar = existingHeader ? self.getBlockBulletChar(lines, existingHeader.index, blockEnd) : '*'; let voteLine = bulletChar + ' {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC)'; return { text: self.buildGroupedVoteText(sectionText, group, voteLine) }; } let voteLine = '\n* {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' [[Kullanıcı:Brightt11|Brightt11]] ([[Kullanıcı mesaj:Brightt11|mesaj]]) 23.25, 23 Ekim 2026 (UTC)'; return { appendtext: voteLine, previewText: sectionText + voteLine }; }, async refreshContextPreview() { if (!this.selectedOption) return; this.contextPreviewLoading = true; try { const api = new mw.Api(); const res = await api.get(this.buildQueryParams()); const sectionText = self.extractContent(res); if (sectionText === null) throw new Error('Bölüm içeriği alınamadı'); const built = this.buildNewSectionText(sectionText); this.contextPreviewWikitext = built.text !== undefined ? built.text : built.previewText; } catch (error) { console.error('Oyver: bölüm önizlemesi başarısız', error); } finally { this.contextPreviewLoading = false; } }, async doVote() { this.submitError = ''; this.submitting = true; try { const api = new mw.Api(); const res = await api.get(this.buildQueryParams()); const sectionText = self.extractContent(res); if (sectionText === null) throw new Error('Bölüm içeriği alınamadı'); const currentUser = mw.config.get('wgUserName'); if (self.hasExistingVote(sectionText, currentUser)) { if (!confirm(this.messages.confirmPossibleDouble)) { this.submitting = false; return; } } const summaryBase = this.editSummaryInput.trim() || this.defaultSummary; const editSummary = summaryBase + self.config.summfix; const editParams = { action: 'edit', summary: editSummary, token: mw.user.tokens.get('csrfToken') }; if (pageInfo.type === 'direct') { editParams.title = mw.config.get('wgPageName'); editParams.section = pageInfo.sectionNum; } else { editParams.title = pageInfo.targetPage; editParams.section = pageInfo.sectionId; } const built = this.buildNewSectionText(sectionText); if (built.text !== undefined) { editParams.text = built.text; } else { editParams.appendtext = built.appendtext; } await api.postWithEditToken(editParams); setTimeout(() => { window.location.reload(); }, 1200); } catch (error) { console.error('Oyver: gönderim başarısız', error); this.submitError = this.messages.editError; this.submitting = false; } } }, template: ` <div class="script-Oyver-panel"> <div class="script-Oyver-panel-header"> <span class="script-Oyver-panel-title"> {{ panelTitle }} <span class="script-Oyver-version">v{{ version }}</span> </span> <div class="script-Oyver-header-actions"> <cdx-button weight="quiet" :disabled="submitting" :aria-label="messages.preferencesButtonLabel" :title="messages.preferencesButtonLabel" @click="togglePreferences"> <cdx-icon v-if="settingsIcon" :icon="settingsIcon"></cdx-icon> </cdx-button> <cdx-button weight="quiet" :disabled="submitting" :aria-label="messages.closeButtonLabel" :title="messages.closeButtonLabel" @click="cancelPanel"> <cdx-icon v-if="closeIcon" :icon="closeIcon"></cdx-icon> </cdx-button> </div> </div> <div v-if="showPreferences" class="script-Oyver-panel-body"> <div class="script-Oyver-prefs"> <p class="script-Oyver-prefs-title">{{ messages.preferencesTitle }}</p> <cdx-field class="script-Oyver-field"> <template #label>{{ messages.preferencesIconSetLabel }}</template> <cdx-radio v-model="prefsIconSet" input-value="codex" :name="prefsIconSetName"> {{ messages.preferencesIconSetCodex }} </cdx-radio> <cdx-radio v-model="prefsIconSet" input-value="onwiki" :name="prefsIconSetName"> {{ messages.preferencesIconSetOnWiki }} </cdx-radio> </cdx-field> <cdx-checkbox v-model="prefsFullContext" class="script-Oyver-field"> {{ messages.preferencesFullContextLabel }} </cdx-checkbox> <div class="script-Oyver-buttons"> <cdx-button weight="primary" action="progressive" :disabled="savingPrefs" @click="savePreferences"> <cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon> {{ messages.preferencesSave }} </cdx-button> <cdx-button :disabled="savingPrefs" @click="showPreferences = false"> {{ messages.cancelButton }} </cdx-button> </div> </div> </div> <div v-else class="script-Oyver-panel-body"> <p>{{ messages.vote }}</p> <div v-for="option in voteOptions" :key="option.value" class="script-Oyver-option"> <cdx-radio v-model="selectedVote" :input-value="option.value" :name="radioName" :disabled="submitting" > <cdx-icon v-if="iconSetPref === 'codex' && option.icon" :icon="option.icon"></cdx-icon> <img v-else-if="iconSetPref === 'onwiki' && option.iconUrl" :src="option.iconUrl" width="20" height="20" alt="" class="script-Oyver-onwiki-icon" > {{ option.label }} </cdx-radio> </div> <cdx-field class="script-Oyver-field"> <template #label>{{ messages.comment }}</template> <cdx-text-area v-model="comment" :rows="3" :disabled="submitting"></cdx-text-area> </cdx-field> <p class="script-Oyver-signature-note">{{ messages.signatureAutoAdd }}</p> <cdx-accordion v-model="showSummary" class="script-Oyver-field"> <template #title>{{ messages.editSummaryLabel }}</template> <cdx-text-input v-model="editSummaryInput" :disabled="submitting" @input="summaryEdited = true" ></cdx-text-input> </cdx-accordion> <cdx-message v-if="submitError" type="error" :allow-user-dismiss="false" class="script-Oyver-status"> {{ submitError }} </cdx-message> <cdx-message v-if="submitting" type="notice" :allow-user-dismiss="false" class="script-Oyver-status"> {{ messages.submitting }} </cdx-message> <div class="script-Oyver-buttons"> <cdx-button weight="primary" action="progressive" :disabled="!canSubmit" @click="doVote"> <cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon> {{ messages.windowButton }} </cdx-button> <cdx-button :disabled="!selectedVote || submitting" @click="togglePreview"> <cdx-icon v-if="previewToggleIcon" :icon="previewToggleIcon"></cdx-icon> {{ previewToggleLabel }} </cdx-button> <cdx-button :disabled="submitting" @click="cancelPanel"> {{ messages.cancelButton }} </cdx-button> </div> <div v-if="showPreview && selectedVote" class="script-Oyver-preview-wrapper"> <div class="script-Oyver-preview-toolbar"> <cdx-button weight="quiet" :disabled="contextPreviewLoading" :aria-label="messages.toggleFullSectionLabel" :title="messages.toggleFullSectionLabel" @click="toggleContextPreview" > <cdx-icon v-if="contextToggleIcon" :icon="contextToggleIcon"></cdx-icon> </cdx-button> </div> <preview-renderer :wikitext="showContextPreview ? contextPreviewWikitext : previewWikitext"></preview-renderer> </div> </div> </div> ` }); vueAppInstance.mount('#' + mountId); } }; Oyver.init(); }); // </nowiki> rc504l7fbjh0yewruee2wxfg428w6i6 17881 17878 2026-10-24T19:18:18Z Brightt11 1786 fixed signature bug. again. 17881 javascript text/javascript // Oyver - EasyVoteMeta (Author: Max) betiğinin yerelleştirilmiş versiyonu. // Orijinal betik kaynağı/Original script source: https://meta.wikimedia.org/wiki/User:Max/EasyVoteMeta.js // Bu betik, oylama/görüş bildirme sayfalarında bölüm başlıklarına "hızlı oy ver" bağlantısı ekler. // sürüm: 1.3 beta. // <nowiki> $(() => { 'use strict'; const Oyver = { version: '1.3 beta', signature: '~~' + '~~', config: { messages: { windowTitle: 'Oyver', windowButton: 'Gönder', cancelButton: 'İptal', vote: 'Görüş belirtmek için oy seçin:', comment: 'Yorum:', signatureAutoAdd: 'İmzanız otomatik olarak eklenecektir.', editSummaryLabel: 'Değişiklik özeti', showPreview: 'Önizlemeyi göster', hidePreview: 'Önizlemeyi gizle', preferencesTitle: 'Tercihler', preferencesIconSetLabel: 'Oy simgesi görünümü', preferencesIconSetCodex: 'Codex simgeleri', preferencesIconSetOnWiki: 'Wiki simgeleri', preferencesFullContextLabel: 'Önizlemede daima tüm bölümü göster', preferencesSave: 'Kaydet', preferencesButtonLabel: 'Tercihler', closeButtonLabel: 'Kapat', toggleFullSectionLabel: 'Tüm bölüm önizlemesini açın/kapatın', loadingPreviewLabel: 'Önizleme yükleniyor', submitting: 'Gönderiliyor. Lütfen internet tarayıcınızı açık tutun.', voteLinkText: 'hızlı oy ver', editError: 'Hata: Bir hata oldu. Klavyenizden F5 tuşuna basarak sayfayı yenileyin ve tekrar deneyin', confirmPossibleDouble: 'Bu başvuruda daha önce görüş yapmışsınız. Devam etmek istediğinizden emin misiniz? Lütfen önceki görüş(ler)inizin üstünü çizerek (<nowiki><s></s></nowiki> kullanarak) oyunuzu geri çekin.' }, voteOptions: { support: { label: 'Destek', template: 'Destek', icon: 'cdxIconCheck', group: 'support', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/9/94/Symbol_support_vote.svg' }, halfsupport: { label: 'Kısmi destek', template: 'Kısmi destek', icon: 'cdxIconCheck', group: 'support', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/8c/GA_candidate.svg' }, oppose: { label: 'Karşı', template: 'Karşı', icon: 'cdxIconClose', group: 'oppose', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/7/7f/Symbol_oppose_vote.svg' }, halfoppose: { label: 'Kısmen karşı', template: 'Kısmen karşı', icon: 'cdxIconClose', group: 'oppose', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/f/f5/BA_candidate.svg' }, neutral: { label: 'Tarafsız', template: 'Tarafsız', icon: 'cdxIconInfoFilled', group: 'neutral', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/89/Symbol_neutral_vote.svg' }, comment: { label: 'Yorum', template: 'Yorum', icon: 'cdxIconSpeechBubble', group: 'neutral', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/d/df/Pictogram_voting_comment_%28orange%29.svg?utm_source=commons.wikimedia.org&utm_campaign=index&utm_content=original', requiresComment: true, skipDoubleCheck: true } }, voteAliases: [], groupOrder: ['support', 'oppose', 'neutral'], groupHeaders: { support: ['Destek', 'Evet'], oppose: ['Karşı', 'Hayır'], neutral: ['Tarafsız', 'Yorum'] }, iconNames: ['cdxIconCheck', 'cdxIconClose', 'cdxIconInfoFilled', 'cdxIconAlert', 'cdxIconSpeechBubble', 'cdxIconEye', 'cdxIconEyeClosed', 'cdxIconSettings'], summfix: ' ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.)', pageConfigs: { 'Vikigezgin:Kullanıcı hakları adaylıkları': { type: 'auto', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [] } }, namespaceConfigs: { 8: { type: 'direct', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [], dbNames: [] }, 828: { type: 'direct', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [], dbNames: [] } } }, icons: {}, prefsPrefix: 'userjs-oyver-', prefsDefaults: { iconSet: 'codex', fullContextPreviewDefault: false }, prefsCache: {}, prefs: {}, currentPageConfig: null, init() { const currentPage = mw.config.get('wgPageName').replace(/_/g, ' '); this.currentPageConfig = this.config.pageConfigs[currentPage] || (this.config.namespaceConfigs || {})[mw.config.get('wgNamespaceNumber')]; if (!this.currentPageConfig) return; const dbNames = this.currentPageConfig.dbNames; if (dbNames && dbNames.length && !dbNames.includes(mw.config.get('wgDBname'))) return; if (mw.user.isAnon()) return; mw.loader.using(['vue', '@wikimedia/codex', 'mediawiki.util', 'mediawiki.api', 'mediawiki.user']).then((require) => { this.Vue = require('vue'); this.Codex = require('@wikimedia/codex'); this.run(); }); }, async loadIcons() { try { const api = new mw.Api(); const data = await api.get({ action: 'query', list: 'codexicons', names: this.config.iconNames.join('|'), formatversion: 2 }); this.icons = (data && data.query && data.query.codexicons) || {}; } catch (error) { this.icons = {}; } }, getPrefFullKey(key) { return this.prefsPrefix + key; }, loadPref(key) { const fullKey = this.getPrefFullKey(key); const fallback = this.prefsDefaults[key]; let value; if (Object.prototype.hasOwnProperty.call(this.prefsCache, fullKey)) { value = this.prefsCache[fullKey]; } else { try { value = window.localStorage ? window.localStorage.getItem(fullKey) : null; } catch (error) { value = null; } } if (value !== null && value !== undefined) { try { return JSON.parse(value); } catch (error) { return fallback; } } try { const serverValue = mw.user.options.get(fullKey, JSON.stringify(fallback)); return JSON.parse(serverValue); } catch (error) { return fallback; } }, loadAllPrefs() { const prefs = {}; Object.keys(this.prefsDefaults).forEach((key) => { prefs[key] = this.loadPref(key); }); return prefs; }, async savePref(key, value) { const fullKey = this.getPrefFullKey(key); const fullValue = JSON.stringify(value); this.prefsCache[fullKey] = fullValue; try { if (window.localStorage) window.localStorage.setItem(fullKey, fullValue); } catch (error) { } try { const api = new mw.Api(); await api.postWithEditToken({ action: 'options', optionname: fullKey, optionvalue: fullValue }); } catch (error) { console.error('Oyver: Hata. Tercih kaydedilemedi.', key, error); } }, async savePrefs(newPrefs) { const keys = Object.keys(newPrefs); for (const key of keys) { await this.savePref(key, newPrefs[key]); } }, async run() { this.addStyle(); this.prefs = this.loadAllPrefs(); await this.loadIcons(); const pageConfig = this.currentPageConfig; const skipPrefixes = (pageConfig.skipPrefixes || []).map((p) => p.toLocaleLowerCase('tr')); $(pageConfig.headingSelector || 'div.mw-heading.mw-heading3').each((_, heading) => { const $heading = $(heading); const editLinks = $heading.find('.mw-editsection'); const headingId = $heading.find('h1,h2,h3,h4,h5,h6').first().attr('id'); if (!headingId) return; const sectionTitle = headingId.replace(/_/g, ' '); const normalizedTitle = sectionTitle.toLocaleLowerCase('tr'); if (skipPrefixes.some((prefix) => normalizedTitle.startsWith(prefix))) return; if (pageConfig.type === 'direct') { this.handleDirectPage(editLinks, sectionTitle, $heading); } else if (pageConfig.type === 'transcluded') { this.handleTranscludedPage(editLinks, sectionTitle, $heading); } else if (pageConfig.type === 'auto') { this.handleAutoPage(editLinks, sectionTitle, $heading); } }); }, handleAutoPage(editLinks, sectionTitle, $heading) { const info = this.getTargetPageInfo(editLinks); if (!info) return; const currentPage = mw.config.get('wgPageName').replace(/_/g, ' '); if (info.targetPage === currentPage && /^\d+$/.test(info.sectionId)) { this.addVoteLink(editLinks, sectionTitle, { type: 'direct', sectionNum: parseInt(info.sectionId, 10) }, $heading); } else { this.addVoteLink(editLinks, sectionTitle, Object.assign({ type: 'transcluded' }, info), $heading); } }, handleDirectPage(editLinks, sectionTitle, $heading) { const sectionNum = this.getSectionNumber(editLinks); if (sectionNum !== -1) { this.addVoteLink(editLinks, sectionTitle, { type: 'direct', sectionNum }, $heading); } }, handleTranscludedPage(editLinks, sectionTitle, $heading) { const targetPageInfo = this.getTargetPageInfo(editLinks); if (targetPageInfo && targetPageInfo.targetPage && targetPageInfo.sectionId) { this.addVoteLink(editLinks, sectionTitle, Object.assign({ type: 'transcluded' }, targetPageInfo), $heading); } }, addStyle() { mw.util.addCSS(` .script-Oyver-link { color: var(--color-progressive, #36c); text-decoration: none; font-weight: bold; } .script-Oyver-link:hover { text-decoration: underline; } .script-Oyver-disabled { color: var(--color-disabled, #a2a9b1) !important; pointer-events: none; } .script-Oyver-divider { border: none; border-top: 1px solid var(--border-color-subtle, #eaecf0); margin: 8px 0 4px; } .script-Oyver-panel { border: 1px solid var(--border-color-base, #a2a9b1); background: var(--background-color-base, #fff); padding: 16px; margin: 8px 0 16px; } .script-Oyver-panel-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } .script-Oyver-panel-title { font-weight: bold; font-size: 15px; } .script-Oyver-version { font-size: 0.7em; font-weight: normal; color: var(--color-subtle, #54595d); vertical-align: middle; margin-left: 2px; } .script-Oyver-header-actions { display: flex; gap: 4px; } .script-Oyver-panel-body p:first-child { margin-top: 0; } .script-Oyver-option { margin: 8px 0; } .script-Oyver-option .cdx-icon { margin-right: 4px; } .script-Oyver-onwiki-icon { margin-right: 4px; vertical-align: middle; } .script-Oyver-field { margin-top: 14px; } .script-Oyver-signature-note { font-style: italic; color: var(--color-subtle, #54595d); font-size: 0.9em; margin-top: 8px; } .script-Oyver-status { margin-top: 12px; } .script-Oyver-buttons { margin-top: 16px; display: flex; gap: 8px; flex-wrap: wrap; } .script-Oyver-preview-wrapper { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border-color-subtle, #eaecf0); } .script-Oyver-preview-toolbar { display: flex; justify-content: flex-end; } .script-Oyver-preview { margin-top: 0; } .script-Oyver-preview-content { font-size: 0.95em; line-height: 1.6; } .script-Oyver-preview-loading { display: flex; align-items: center; gap: 8px; color: var(--color-subtle, #54595d); font-size: 0.88em; } .script-Oyver-prefs { border: 1px solid var(--border-color-subtle, #eaecf0); padding: 12px; } .script-Oyver-prefs-title { font-weight: bold; margin: 0 0 8px; } `); }, getSectionNumber(editLinks) { const editSectionUrl = editLinks.find('a:first').attr('href'); const sectionMatch = editSectionUrl && editSectionUrl.match(/[?&]section=(\d+)(?:&|$)/); return sectionMatch ? parseInt(sectionMatch[1], 10) : -1; }, getTargetPageInfo(editLinks) { const editSectionUrl = editLinks.find('a:first').attr('href'); if (!editSectionUrl) return null; const titleMatch = editSectionUrl.match(/[?&]title=([^&]+)/); const sectionMatch = editSectionUrl.match(/[?&]section=([^&]+)/); if (!titleMatch || !sectionMatch) return null; return { targetPage: decodeURIComponent(titleMatch[1]).replace(/_/g, ' '), sectionId: sectionMatch[1] }; }, extractContent(apiData) { const pages = apiData && apiData.query && apiData.query.pages; if (!pages || !pages[0] || !pages[0].revisions || !pages[0].revisions[0]) return null; return pages[0].revisions[0].content; }, sanitizeSectionTitle(rawTitle) { if (!rawTitle) return ''; const emojiPattern = /(\p{Extended_Pictographic}|\p{Emoji_Presentation}|[\u{1F1E6}-\u{1F1FF}]|\u{FE0F}|\u{200D})/gu; let text = rawTitle.replace(emojiPattern, ''); let cutIndex = -1; for (let i = 0; i < text.length; i++) { if (text[i] === '(') { cutIndex = i; break; } if (text[i] === '[') { if (text[i + 1] === '[') { i++; continue; } cutIndex = i; break; } } if (cutIndex !== -1) { text = text.substring(0, cutIndex); } return text.replace(/\s+/g, ' ').trim(); }, detectGroupHeaderType(line) { const match = line.match(/^;\s*(\S.*)$/); if (!match) return null; const text = match[1].toLocaleLowerCase('tr'); for (const key of this.config.groupOrder) { const words = this.config.groupHeaders[key] || []; for (const word of words) { const w = word.toLocaleLowerCase('tr'); if (text.startsWith(w) && !/^\p{L}/u.test(text.slice(w.length))) { return key; } } } return null; }, findGroupHeaders(lines) { return lines.reduce((acc, line, idx) => { const type = this.detectGroupHeaderType(line); if (type) acc.push({ type, index: idx }); return acc; }, []); }, findGroupBlockEnd(lines, headerIdx) { for (let i = headerIdx + 1; i < lines.length; i++) { if (this.detectGroupHeaderType(lines[i])) return i; } return lines.length; }, getBlockBulletChar(lines, headerIdx, blockEnd) { for (let i = headerIdx + 1; i < blockEnd; i++) { const match = lines[i].match(/^\s*([#*:]+)/); if (match) return match[1]; } return '*'; }, getGroupHeaderText(group) { const words = this.config.groupHeaders[group]; return (words && words[0]) || group; }, buildGroupedVoteText(sectionText, group, voteLine) { const lines = sectionText.split('\n'); const headers = this.findGroupHeaders(lines); const existing = headers.find((h) => h.type === group); const headerLine = ';' + this.getGroupHeaderText(group); if (existing) { const blockEnd = this.findGroupBlockEnd(lines, existing.index); let insertIdx = blockEnd; while (insertIdx - 1 > existing.index && lines[insertIdx - 1].trim() === '') insertIdx--; lines.splice(insertIdx, 0, voteLine); return lines.join('\n'); } const order = this.config.groupOrder; const orderIdx = order.indexOf(group); for (let o = orderIdx + 1; o < order.length; o++) { const laterHeader = headers.find((h) => h.type === order[o]); if (laterHeader) { lines.splice(laterHeader.index, 0, headerLine, voteLine, ''); return lines.join('\n'); } } for (let o = orderIdx - 1; o >= 0; o--) { const earlierHeader = headers.find((h) => h.type === order[o]); if (earlierHeader) { const blockEnd = this.findGroupBlockEnd(lines, earlierHeader.index); lines.splice(blockEnd, 0, '', headerLine, voteLine); return lines.join('\n'); } } let lastNonBlank = lines.length - 1; while (lastNonBlank >= 0 && lines[lastNonBlank].trim() === '') lastNonBlank--; lines.splice(lastNonBlank + 1, 0, '', headerLine, voteLine); return lines.join('\n'); }, hasExistingVote(sectionText, currentUser) { const templates = Object.values(this.config.voteOptions).filter((opt) => !opt.skipDoubleCheck).map((opt) => opt.template); const aliases = this.config.voteAliases || []; const allNames = [...templates, ...aliases].map((name) => mw.util.escapeRegExp(name)); const escapedUser = mw.util.escapeRegExp(currentUser); const pattern = new RegExp('\\{\\{(?:' + allNames.join('|') + ')\\}\\}[^\\n~]{0,300}?' + escapedUser, 'i'); return pattern.test(sectionText); }, addVoteLink(editLinks, sectionTitle, pageInfo, $heading) { const uniqueId = 'script-Oyver-launch-' + Math.random().toString(36).slice(2, 11); const mountId = 'script-Oyver-mount-' + uniqueId; const voteLink = $('<a>') .attr('id', uniqueId) .addClass('script-Oyver-link') .text(this.config.messages.voteLinkText) .attr('href', '#') .click((event) => { event.preventDefault(); if (!voteLink.hasClass('script-Oyver-disabled')) { voteLink.addClass('script-Oyver-disabled'); if (!$('#' + mountId).length) { $heading.after( $('<div>').attr('id', mountId), $('<hr>').addClass('script-Oyver-divider') ); } this.showPanel(sectionTitle, pageInfo, $heading, uniqueId, mountId); } }); editLinks.children().last().before(' | ', voteLink); }, getPreviewComponent() { const loadingLabel = this.config.messages.loadingPreviewLabel; return { components: { CdxProgressBar: this.Codex.CdxProgressBar }, props: { wikitext: { type: String, default: '' } }, data() { return { previewHtml: '', haveHtml: false, loadingLabel }; }, methods: { loadPreview(wikitextToPreview) { new mw.Api().get({ action: 'parse', formatversion: 2, title: mw.config.get('wgPageName'), text: wikitextToPreview, prop: 'text', pst: true, disablelimitreport: true, disableeditsection: true, sectionpreview: true }).then((res) => { if (res && res.parse && res.parse.text) { this.previewHtml = res.parse.text; this.haveHtml = true; } }); } }, watch: { wikitext: { immediate: true, handler(newValue) { this.previewHtml = ''; this.haveHtml = false; this.loadPreview(newValue); } } }, template: ` <div class="script-Oyver-preview"> <div v-if="!haveHtml" class="script-Oyver-preview-loading"> <cdx-progress-bar inline :aria-label="loadingLabel"></cdx-progress-bar> </div> <div v-else class="script-Oyver-preview-content" v-html="previewHtml"></div> </div> ` }; }, showPanel(sectionTitle, pageInfo, $heading, uniqueId, mountId) { const self = this; const rawLiveSectionTitle = $heading.contents().not($heading.find('.mw-editsection')).text().trim() || sectionTitle; const liveSectionTitle = self.sanitizeSectionTitle(rawLiveSectionTitle); const vueAppInstance = self.Vue.createMwApp({ components: { CdxRadio: self.Codex.CdxRadio, CdxCheckbox: self.Codex.CdxCheckbox, CdxTextArea: self.Codex.CdxTextArea, CdxTextInput: self.Codex.CdxTextInput, CdxField: self.Codex.CdxField, CdxAccordion: self.Codex.CdxAccordion, CdxMessage: self.Codex.CdxMessage, CdxButton: self.Codex.CdxButton, CdxIcon: self.Codex.CdxIcon, previewRenderer: self.getPreviewComponent() }, data() { return { version: self.version, messages: self.config.messages, sectionTitle: liveSectionTitle, uniqueId, voteOptions: Object.entries(self.config.voteOptions).map(([value, opt]) => ({ value, label: opt.label, group: opt.group, icon: self.icons[opt.icon] || null, iconUrl: opt.iconUrl, requiresComment: !!opt.requiresComment })), selectedVote: null, comment: '', showSummary: false, editSummaryInput: '/* ' + liveSectionTitle + ' */ ', summaryEdited: false, showPreview: false, submitting: false, submitError: '', showContextPreview: false, contextPreviewWikitext: '', contextPreviewLoading: false, contextPreviewDebounce: null, showPreferences: false, iconSetPref: self.prefs.iconSet, prefsIconSet: self.prefs.iconSet, prefsFullContext: self.prefs.fullContextPreviewDefault, savingPrefs: false, checkIcon: self.icons.cdxIconCheck || null, closeIcon: self.icons.cdxIconClose || null, eyeIcon: self.icons.cdxIconEye || null, eyeClosedIcon: self.icons.cdxIconEyeClosed || null, settingsIcon: self.icons.cdxIconSettings || null }; }, computed: { panelTitle() { return this.messages.windowTitle + ': ' + this.sectionTitle; }, radioName() { return 'script-Oyver-vote-' + this.uniqueId; }, prefsIconSetName() { return 'script-Oyver-prefs-iconset-' + this.uniqueId; }, selectedOption() { return this.voteOptions.find((opt) => opt.value === this.selectedVote) || null; }, defaultSummary() { const base = '/* ' + this.sectionTitle + ' */ '; return this.selectedOption ? base + this.selectedOption.label : base; }, canSubmit() { if (this.submitting || !this.selectedVote) return false; if (this.selectedOption && this.selectedOption.requiresComment && !this.comment.trim()) return false; return true; }, previewToggleIcon() { return this.showPreview ? this.eyeClosedIcon : this.eyeIcon; }, previewToggleLabel() { return this.showPreview ? this.messages.hidePreview : this.messages.showPreview; }, contextToggleIcon() { return this.showContextPreview ? this.eyeClosedIcon : this.eyeIcon; }, previewWikitext() { if (!this.selectedOption) return ''; const templateName = self.config.voteOptions[this.selectedVote].template; const trimmedComment = this.comment.trim(); let voteLine = '* {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' ' + self.signature; return voteLine; } }, watch: { selectedVote() { if (!this.summaryEdited) { this.editSummaryInput = this.defaultSummary; } if (this.showContextPreview) { this.refreshContextPreview(); } }, comment() { if (!this.showContextPreview) return; clearTimeout(this.contextPreviewDebounce); this.contextPreviewDebounce = setTimeout(() => { this.refreshContextPreview(); }, 400); } }, methods: { cancelPanel() { if (this.submitting) return; vueAppInstance.unmount(); $('#' + mountId).empty(); $('#' + uniqueId).removeClass('script-Oyver-disabled'); }, togglePreferences() { if (this.submitting) return; this.prefsIconSet = self.prefs.iconSet; this.prefsFullContext = self.prefs.fullContextPreviewDefault; this.showPreferences = !this.showPreferences; }, async savePreferences() { this.savingPrefs = true; const newPrefs = { iconSet: this.prefsIconSet, fullContextPreviewDefault: this.prefsFullContext }; await self.savePrefs(newPrefs); self.prefs = Object.assign({}, self.prefs, newPrefs); this.iconSetPref = self.prefs.iconSet; this.savingPrefs = false; this.showPreferences = false; }, async togglePreview() { if (this.showPreview) { this.showPreview = false; return; } if (self.prefs.fullContextPreviewDefault && !this.showContextPreview) { await this.toggleContextPreview(); } this.showPreview = true; }, async toggleContextPreview() { if (this.showContextPreview) { this.showContextPreview = false; return; } if (!this.selectedOption) return; await this.refreshContextPreview(); this.showContextPreview = true; }, buildQueryParams() { const queryParams = { action: 'query', prop: 'revisions', rvprop: 'content', formatversion: 2 }; if (pageInfo.type === 'direct') { queryParams.titles = mw.config.get('wgPageName'); queryParams.rvsection = pageInfo.sectionNum; } else { queryParams.titles = pageInfo.targetPage; queryParams.rvsection = pageInfo.sectionId; } return queryParams; }, buildNewSectionText(sectionText) { const templateName = self.config.voteOptions[this.selectedVote].template; const group = self.config.voteOptions[this.selectedVote].group; const trimmedComment = this.comment.trim(); const headers = self.findGroupHeaders(sectionText.split('\n')); if (headers.length) { const lines = sectionText.split('\n'); const existingHeader = headers.find((h) => h.type === group); const blockEnd = existingHeader ? self.findGroupBlockEnd(lines, existingHeader.index) : -1; const bulletChar = existingHeader ? self.getBlockBulletChar(lines, existingHeader.index, blockEnd) : '*'; let voteLine = bulletChar + ' {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' ' + self.signature; return { text: self.buildGroupedVoteText(sectionText, group, voteLine) }; } let voteLine = '\n* {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' ' + self.signature; return { appendtext: voteLine, previewText: sectionText + voteLine }; }, async refreshContextPreview() { if (!this.selectedOption) return; this.contextPreviewLoading = true; try { const api = new mw.Api(); const res = await api.get(this.buildQueryParams()); const sectionText = self.extractContent(res); if (sectionText === null) throw new Error('Bölüm içeriği alınamadı'); const built = this.buildNewSectionText(sectionText); this.contextPreviewWikitext = built.text !== undefined ? built.text : built.previewText; } catch (error) { console.error('Oyver: bölüm önizlemesi başarısız', error); } finally { this.contextPreviewLoading = false; } }, async doVote() { this.submitError = ''; this.submitting = true; try { const api = new mw.Api(); const res = await api.get(this.buildQueryParams()); const sectionText = self.extractContent(res); if (sectionText === null) throw new Error('Bölüm içeriği alınamadı'); const currentUser = mw.config.get('wgUserName'); if (self.hasExistingVote(sectionText, currentUser)) { if (!confirm(this.messages.confirmPossibleDouble)) { this.submitting = false; return; } } const summaryBase = this.editSummaryInput.trim() || this.defaultSummary; const editSummary = summaryBase + self.config.summfix; const editParams = { action: 'edit', summary: editSummary, token: mw.user.tokens.get('csrfToken') }; if (pageInfo.type === 'direct') { editParams.title = mw.config.get('wgPageName'); editParams.section = pageInfo.sectionNum; } else { editParams.title = pageInfo.targetPage; editParams.section = pageInfo.sectionId; } const built = this.buildNewSectionText(sectionText); if (built.text !== undefined) { editParams.text = built.text; } else { editParams.appendtext = built.appendtext; } await api.postWithEditToken(editParams); setTimeout(() => { window.location.reload(); }, 1200); } catch (error) { console.error('Oyver: gönderim başarısız', error); this.submitError = this.messages.editError; this.submitting = false; } } }, template: ` <div class="script-Oyver-panel"> <div class="script-Oyver-panel-header"> <span class="script-Oyver-panel-title"> {{ panelTitle }} <span class="script-Oyver-version">v{{ version }}</span> </span> <div class="script-Oyver-header-actions"> <cdx-button weight="quiet" :disabled="submitting" :aria-label="messages.preferencesButtonLabel" :title="messages.preferencesButtonLabel" @click="togglePreferences"> <cdx-icon v-if="settingsIcon" :icon="settingsIcon"></cdx-icon> </cdx-button> <cdx-button weight="quiet" :disabled="submitting" :aria-label="messages.closeButtonLabel" :title="messages.closeButtonLabel" @click="cancelPanel"> <cdx-icon v-if="closeIcon" :icon="closeIcon"></cdx-icon> </cdx-button> </div> </div> <div v-if="showPreferences" class="script-Oyver-panel-body"> <div class="script-Oyver-prefs"> <p class="script-Oyver-prefs-title">{{ messages.preferencesTitle }}</p> <cdx-field class="script-Oyver-field"> <template #label>{{ messages.preferencesIconSetLabel }}</template> <cdx-radio v-model="prefsIconSet" input-value="codex" :name="prefsIconSetName"> {{ messages.preferencesIconSetCodex }} </cdx-radio> <cdx-radio v-model="prefsIconSet" input-value="onwiki" :name="prefsIconSetName"> {{ messages.preferencesIconSetOnWiki }} </cdx-radio> </cdx-field> <cdx-checkbox v-model="prefsFullContext" class="script-Oyver-field"> {{ messages.preferencesFullContextLabel }} </cdx-checkbox> <div class="script-Oyver-buttons"> <cdx-button weight="primary" action="progressive" :disabled="savingPrefs" @click="savePreferences"> <cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon> {{ messages.preferencesSave }} </cdx-button> <cdx-button :disabled="savingPrefs" @click="showPreferences = false"> {{ messages.cancelButton }} </cdx-button> </div> </div> </div> <div v-else class="script-Oyver-panel-body"> <p>{{ messages.vote }}</p> <div v-for="option in voteOptions" :key="option.value" class="script-Oyver-option"> <cdx-radio v-model="selectedVote" :input-value="option.value" :name="radioName" :disabled="submitting" > <cdx-icon v-if="iconSetPref === 'codex' && option.icon" :icon="option.icon"></cdx-icon> <img v-else-if="iconSetPref === 'onwiki' && option.iconUrl" :src="option.iconUrl" width="20" height="20" alt="" class="script-Oyver-onwiki-icon" > {{ option.label }} </cdx-radio> </div> <cdx-field class="script-Oyver-field"> <template #label>{{ messages.comment }}</template> <cdx-text-area v-model="comment" :rows="3" :disabled="submitting"></cdx-text-area> </cdx-field> <p class="script-Oyver-signature-note">{{ messages.signatureAutoAdd }}</p> <cdx-accordion v-model="showSummary" class="script-Oyver-field"> <template #title>{{ messages.editSummaryLabel }}</template> <cdx-text-input v-model="editSummaryInput" :disabled="submitting" @input="summaryEdited = true" ></cdx-text-input> </cdx-accordion> <cdx-message v-if="submitError" type="error" :allow-user-dismiss="false" class="script-Oyver-status"> {{ submitError }} </cdx-message> <cdx-message v-if="submitting" type="notice" :allow-user-dismiss="false" class="script-Oyver-status"> {{ messages.submitting }} </cdx-message> <div class="script-Oyver-buttons"> <cdx-button weight="primary" action="progressive" :disabled="!canSubmit" @click="doVote"> <cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon> {{ messages.windowButton }} </cdx-button> <cdx-button :disabled="!selectedVote || submitting" @click="togglePreview"> <cdx-icon v-if="previewToggleIcon" :icon="previewToggleIcon"></cdx-icon> {{ previewToggleLabel }} </cdx-button> <cdx-button :disabled="submitting" @click="cancelPanel"> {{ messages.cancelButton }} </cdx-button> </div> <div v-if="showPreview && selectedVote" class="script-Oyver-preview-wrapper"> <div class="script-Oyver-preview-toolbar"> <cdx-button weight="quiet" :disabled="contextPreviewLoading" :aria-label="messages.toggleFullSectionLabel" :title="messages.toggleFullSectionLabel" @click="toggleContextPreview" > <cdx-icon v-if="contextToggleIcon" :icon="contextToggleIcon"></cdx-icon> </cdx-button> </div> <preview-renderer :wikitext="showContextPreview ? contextPreviewWikitext : previewWikitext"></preview-renderer> </div> </div> </div> ` }); vueAppInstance.mount('#' + mountId); } }; Oyver.init(); }); // </nowiki> opuae1h61pty5kou9a167pi7ue6sjq7 17884 17881 2026-10-24T19:21:04Z Brightt11 1786 version 17884 javascript text/javascript // Oyver - EasyVoteMeta (Author: Max) betiğinin yerelleştirilmiş versiyonu. // Orijinal betik kaynağı/Original script source: https://meta.wikimedia.org/wiki/User:Max/EasyVoteMeta.js // Bu betik, oylama/görüş bildirme sayfalarında bölüm başlıklarına "hızlı oy ver" bağlantısı ekler. // sürüm: 1.0 beta 3. // <nowiki> $(() => { 'use strict'; const Oyver = { version: '1.0 beta 3', signature: '~~' + '~~', config: { messages: { windowTitle: 'Oyver', windowButton: 'Gönder', cancelButton: 'İptal', vote: 'Görüş belirtmek için oy seçin:', comment: 'Yorum:', signatureAutoAdd: 'İmzanız otomatik olarak eklenecektir.', editSummaryLabel: 'Değişiklik özeti', showPreview: 'Önizlemeyi göster', hidePreview: 'Önizlemeyi gizle', preferencesTitle: 'Tercihler', preferencesIconSetLabel: 'Oy simgesi görünümü', preferencesIconSetCodex: 'Codex simgeleri', preferencesIconSetOnWiki: 'Wiki simgeleri', preferencesFullContextLabel: 'Önizlemede daima tüm bölümü göster', preferencesSave: 'Kaydet', preferencesButtonLabel: 'Tercihler', closeButtonLabel: 'Kapat', toggleFullSectionLabel: 'Tüm bölüm önizlemesini açın/kapatın', loadingPreviewLabel: 'Önizleme yükleniyor', submitting: 'Gönderiliyor. Lütfen internet tarayıcınızı açık tutun.', voteLinkText: 'hızlı oy ver', editError: 'Hata: Bir hata oldu. Klavyenizden F5 tuşuna basarak sayfayı yenileyin ve tekrar deneyin', confirmPossibleDouble: 'Bu başvuruda daha önce görüş yapmışsınız. Devam etmek istediğinizden emin misiniz? Lütfen önceki görüş(ler)inizin üstünü çizerek (<nowiki><s></s></nowiki> kullanarak) oyunuzu geri çekin.' }, voteOptions: { support: { label: 'Destek', template: 'Destek', icon: 'cdxIconCheck', group: 'support', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/9/94/Symbol_support_vote.svg' }, halfsupport: { label: 'Kısmi destek', template: 'Kısmi destek', icon: 'cdxIconCheck', group: 'support', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/8c/GA_candidate.svg' }, oppose: { label: 'Karşı', template: 'Karşı', icon: 'cdxIconClose', group: 'oppose', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/7/7f/Symbol_oppose_vote.svg' }, halfoppose: { label: 'Kısmen karşı', template: 'Kısmen karşı', icon: 'cdxIconClose', group: 'oppose', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/f/f5/BA_candidate.svg' }, neutral: { label: 'Tarafsız', template: 'Tarafsız', icon: 'cdxIconInfoFilled', group: 'neutral', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/89/Symbol_neutral_vote.svg' }, comment: { label: 'Yorum', template: 'Yorum', icon: 'cdxIconSpeechBubble', group: 'neutral', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/d/df/Pictogram_voting_comment_%28orange%29.svg?utm_source=commons.wikimedia.org&utm_campaign=index&utm_content=original', requiresComment: true, skipDoubleCheck: true } }, voteAliases: [], groupOrder: ['support', 'oppose', 'neutral'], groupHeaders: { support: ['Destek', 'Evet'], oppose: ['Karşı', 'Hayır'], neutral: ['Tarafsız', 'Yorum'] }, iconNames: ['cdxIconCheck', 'cdxIconClose', 'cdxIconInfoFilled', 'cdxIconAlert', 'cdxIconSpeechBubble', 'cdxIconEye', 'cdxIconEyeClosed', 'cdxIconSettings'], summfix: ' ([[Kullanıcı:Brightt11/Oyver.js|Oyver]] ile görüş belirtildi.)', pageConfigs: { 'Vikigezgin:Kullanıcı hakları adaylıkları': { type: 'auto', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [] } }, namespaceConfigs: { 8: { type: 'direct', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [], dbNames: [] }, 828: { type: 'direct', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [], dbNames: [] } } }, icons: {}, prefsPrefix: 'userjs-oyver-', prefsDefaults: { iconSet: 'codex', fullContextPreviewDefault: false }, prefsCache: {}, prefs: {}, currentPageConfig: null, init() { const currentPage = mw.config.get('wgPageName').replace(/_/g, ' '); this.currentPageConfig = this.config.pageConfigs[currentPage] || (this.config.namespaceConfigs || {})[mw.config.get('wgNamespaceNumber')]; if (!this.currentPageConfig) return; const dbNames = this.currentPageConfig.dbNames; if (dbNames && dbNames.length && !dbNames.includes(mw.config.get('wgDBname'))) return; if (mw.user.isAnon()) return; mw.loader.using(['vue', '@wikimedia/codex', 'mediawiki.util', 'mediawiki.api', 'mediawiki.user']).then((require) => { this.Vue = require('vue'); this.Codex = require('@wikimedia/codex'); this.run(); }); }, async loadIcons() { try { const api = new mw.Api(); const data = await api.get({ action: 'query', list: 'codexicons', names: this.config.iconNames.join('|'), formatversion: 2 }); this.icons = (data && data.query && data.query.codexicons) || {}; } catch (error) { this.icons = {}; } }, getPrefFullKey(key) { return this.prefsPrefix + key; }, loadPref(key) { const fullKey = this.getPrefFullKey(key); const fallback = this.prefsDefaults[key]; let value; if (Object.prototype.hasOwnProperty.call(this.prefsCache, fullKey)) { value = this.prefsCache[fullKey]; } else { try { value = window.localStorage ? window.localStorage.getItem(fullKey) : null; } catch (error) { value = null; } } if (value !== null && value !== undefined) { try { return JSON.parse(value); } catch (error) { return fallback; } } try { const serverValue = mw.user.options.get(fullKey, JSON.stringify(fallback)); return JSON.parse(serverValue); } catch (error) { return fallback; } }, loadAllPrefs() { const prefs = {}; Object.keys(this.prefsDefaults).forEach((key) => { prefs[key] = this.loadPref(key); }); return prefs; }, async savePref(key, value) { const fullKey = this.getPrefFullKey(key); const fullValue = JSON.stringify(value); this.prefsCache[fullKey] = fullValue; try { if (window.localStorage) window.localStorage.setItem(fullKey, fullValue); } catch (error) { } try { const api = new mw.Api(); await api.postWithEditToken({ action: 'options', optionname: fullKey, optionvalue: fullValue }); } catch (error) { console.error('Oyver: Hata. Tercih kaydedilemedi.', key, error); } }, async savePrefs(newPrefs) { const keys = Object.keys(newPrefs); for (const key of keys) { await this.savePref(key, newPrefs[key]); } }, async run() { this.addStyle(); this.prefs = this.loadAllPrefs(); await this.loadIcons(); const pageConfig = this.currentPageConfig; const skipPrefixes = (pageConfig.skipPrefixes || []).map((p) => p.toLocaleLowerCase('tr')); $(pageConfig.headingSelector || 'div.mw-heading.mw-heading3').each((_, heading) => { const $heading = $(heading); const editLinks = $heading.find('.mw-editsection'); const headingId = $heading.find('h1,h2,h3,h4,h5,h6').first().attr('id'); if (!headingId) return; const sectionTitle = headingId.replace(/_/g, ' '); const normalizedTitle = sectionTitle.toLocaleLowerCase('tr'); if (skipPrefixes.some((prefix) => normalizedTitle.startsWith(prefix))) return; if (pageConfig.type === 'direct') { this.handleDirectPage(editLinks, sectionTitle, $heading); } else if (pageConfig.type === 'transcluded') { this.handleTranscludedPage(editLinks, sectionTitle, $heading); } else if (pageConfig.type === 'auto') { this.handleAutoPage(editLinks, sectionTitle, $heading); } }); }, handleAutoPage(editLinks, sectionTitle, $heading) { const info = this.getTargetPageInfo(editLinks); if (!info) return; const currentPage = mw.config.get('wgPageName').replace(/_/g, ' '); if (info.targetPage === currentPage && /^\d+$/.test(info.sectionId)) { this.addVoteLink(editLinks, sectionTitle, { type: 'direct', sectionNum: parseInt(info.sectionId, 10) }, $heading); } else { this.addVoteLink(editLinks, sectionTitle, Object.assign({ type: 'transcluded' }, info), $heading); } }, handleDirectPage(editLinks, sectionTitle, $heading) { const sectionNum = this.getSectionNumber(editLinks); if (sectionNum !== -1) { this.addVoteLink(editLinks, sectionTitle, { type: 'direct', sectionNum }, $heading); } }, handleTranscludedPage(editLinks, sectionTitle, $heading) { const targetPageInfo = this.getTargetPageInfo(editLinks); if (targetPageInfo && targetPageInfo.targetPage && targetPageInfo.sectionId) { this.addVoteLink(editLinks, sectionTitle, Object.assign({ type: 'transcluded' }, targetPageInfo), $heading); } }, addStyle() { mw.util.addCSS(` .script-Oyver-link { color: var(--color-progressive, #36c); text-decoration: none; font-weight: bold; } .script-Oyver-link:hover { text-decoration: underline; } .script-Oyver-disabled { color: var(--color-disabled, #a2a9b1) !important; pointer-events: none; } .script-Oyver-divider { border: none; border-top: 1px solid var(--border-color-subtle, #eaecf0); margin: 8px 0 4px; } .script-Oyver-panel { border: 1px solid var(--border-color-base, #a2a9b1); background: var(--background-color-base, #fff); padding: 16px; margin: 8px 0 16px; } .script-Oyver-panel-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } .script-Oyver-panel-title { font-weight: bold; font-size: 15px; } .script-Oyver-version { font-size: 0.7em; font-weight: normal; color: var(--color-subtle, #54595d); vertical-align: middle; margin-left: 2px; } .script-Oyver-header-actions { display: flex; gap: 4px; } .script-Oyver-panel-body p:first-child { margin-top: 0; } .script-Oyver-option { margin: 8px 0; } .script-Oyver-option .cdx-icon { margin-right: 4px; } .script-Oyver-onwiki-icon { margin-right: 4px; vertical-align: middle; } .script-Oyver-field { margin-top: 14px; } .script-Oyver-signature-note { font-style: italic; color: var(--color-subtle, #54595d); font-size: 0.9em; margin-top: 8px; } .script-Oyver-status { margin-top: 12px; } .script-Oyver-buttons { margin-top: 16px; display: flex; gap: 8px; flex-wrap: wrap; } .script-Oyver-preview-wrapper { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border-color-subtle, #eaecf0); } .script-Oyver-preview-toolbar { display: flex; justify-content: flex-end; } .script-Oyver-preview { margin-top: 0; } .script-Oyver-preview-content { font-size: 0.95em; line-height: 1.6; } .script-Oyver-preview-loading { display: flex; align-items: center; gap: 8px; color: var(--color-subtle, #54595d); font-size: 0.88em; } .script-Oyver-prefs { border: 1px solid var(--border-color-subtle, #eaecf0); padding: 12px; } .script-Oyver-prefs-title { font-weight: bold; margin: 0 0 8px; } `); }, getSectionNumber(editLinks) { const editSectionUrl = editLinks.find('a:first').attr('href'); const sectionMatch = editSectionUrl && editSectionUrl.match(/[?&]section=(\d+)(?:&|$)/); return sectionMatch ? parseInt(sectionMatch[1], 10) : -1; }, getTargetPageInfo(editLinks) { const editSectionUrl = editLinks.find('a:first').attr('href'); if (!editSectionUrl) return null; const titleMatch = editSectionUrl.match(/[?&]title=([^&]+)/); const sectionMatch = editSectionUrl.match(/[?&]section=([^&]+)/); if (!titleMatch || !sectionMatch) return null; return { targetPage: decodeURIComponent(titleMatch[1]).replace(/_/g, ' '), sectionId: sectionMatch[1] }; }, extractContent(apiData) { const pages = apiData && apiData.query && apiData.query.pages; if (!pages || !pages[0] || !pages[0].revisions || !pages[0].revisions[0]) return null; return pages[0].revisions[0].content; }, sanitizeSectionTitle(rawTitle) { if (!rawTitle) return ''; const emojiPattern = /(\p{Extended_Pictographic}|\p{Emoji_Presentation}|[\u{1F1E6}-\u{1F1FF}]|\u{FE0F}|\u{200D})/gu; let text = rawTitle.replace(emojiPattern, ''); let cutIndex = -1; for (let i = 0; i < text.length; i++) { if (text[i] === '(') { cutIndex = i; break; } if (text[i] === '[') { if (text[i + 1] === '[') { i++; continue; } cutIndex = i; break; } } if (cutIndex !== -1) { text = text.substring(0, cutIndex); } return text.replace(/\s+/g, ' ').trim(); }, detectGroupHeaderType(line) { const match = line.match(/^;\s*(\S.*)$/); if (!match) return null; const text = match[1].toLocaleLowerCase('tr'); for (const key of this.config.groupOrder) { const words = this.config.groupHeaders[key] || []; for (const word of words) { const w = word.toLocaleLowerCase('tr'); if (text.startsWith(w) && !/^\p{L}/u.test(text.slice(w.length))) { return key; } } } return null; }, findGroupHeaders(lines) { return lines.reduce((acc, line, idx) => { const type = this.detectGroupHeaderType(line); if (type) acc.push({ type, index: idx }); return acc; }, []); }, findGroupBlockEnd(lines, headerIdx) { for (let i = headerIdx + 1; i < lines.length; i++) { if (this.detectGroupHeaderType(lines[i])) return i; } return lines.length; }, getBlockBulletChar(lines, headerIdx, blockEnd) { for (let i = headerIdx + 1; i < blockEnd; i++) { const match = lines[i].match(/^\s*([#*:]+)/); if (match) return match[1]; } return '*'; }, getGroupHeaderText(group) { const words = this.config.groupHeaders[group]; return (words && words[0]) || group; }, buildGroupedVoteText(sectionText, group, voteLine) { const lines = sectionText.split('\n'); const headers = this.findGroupHeaders(lines); const existing = headers.find((h) => h.type === group); const headerLine = ';' + this.getGroupHeaderText(group); if (existing) { const blockEnd = this.findGroupBlockEnd(lines, existing.index); let insertIdx = blockEnd; while (insertIdx - 1 > existing.index && lines[insertIdx - 1].trim() === '') insertIdx--; lines.splice(insertIdx, 0, voteLine); return lines.join('\n'); } const order = this.config.groupOrder; const orderIdx = order.indexOf(group); for (let o = orderIdx + 1; o < order.length; o++) { const laterHeader = headers.find((h) => h.type === order[o]); if (laterHeader) { lines.splice(laterHeader.index, 0, headerLine, voteLine, ''); return lines.join('\n'); } } for (let o = orderIdx - 1; o >= 0; o--) { const earlierHeader = headers.find((h) => h.type === order[o]); if (earlierHeader) { const blockEnd = this.findGroupBlockEnd(lines, earlierHeader.index); lines.splice(blockEnd, 0, '', headerLine, voteLine); return lines.join('\n'); } } let lastNonBlank = lines.length - 1; while (lastNonBlank >= 0 && lines[lastNonBlank].trim() === '') lastNonBlank--; lines.splice(lastNonBlank + 1, 0, '', headerLine, voteLine); return lines.join('\n'); }, hasExistingVote(sectionText, currentUser) { const templates = Object.values(this.config.voteOptions).filter((opt) => !opt.skipDoubleCheck).map((opt) => opt.template); const aliases = this.config.voteAliases || []; const allNames = [...templates, ...aliases].map((name) => mw.util.escapeRegExp(name)); const escapedUser = mw.util.escapeRegExp(currentUser); const pattern = new RegExp('\\{\\{(?:' + allNames.join('|') + ')\\}\\}[^\\n~]{0,300}?' + escapedUser, 'i'); return pattern.test(sectionText); }, addVoteLink(editLinks, sectionTitle, pageInfo, $heading) { const uniqueId = 'script-Oyver-launch-' + Math.random().toString(36).slice(2, 11); const mountId = 'script-Oyver-mount-' + uniqueId; const voteLink = $('<a>') .attr('id', uniqueId) .addClass('script-Oyver-link') .text(this.config.messages.voteLinkText) .attr('href', '#') .click((event) => { event.preventDefault(); if (!voteLink.hasClass('script-Oyver-disabled')) { voteLink.addClass('script-Oyver-disabled'); if (!$('#' + mountId).length) { $heading.after( $('<div>').attr('id', mountId), $('<hr>').addClass('script-Oyver-divider') ); } this.showPanel(sectionTitle, pageInfo, $heading, uniqueId, mountId); } }); editLinks.children().last().before(' | ', voteLink); }, getPreviewComponent() { const loadingLabel = this.config.messages.loadingPreviewLabel; return { components: { CdxProgressBar: this.Codex.CdxProgressBar }, props: { wikitext: { type: String, default: '' } }, data() { return { previewHtml: '', haveHtml: false, loadingLabel }; }, methods: { loadPreview(wikitextToPreview) { new mw.Api().get({ action: 'parse', formatversion: 2, title: mw.config.get('wgPageName'), text: wikitextToPreview, prop: 'text', pst: true, disablelimitreport: true, disableeditsection: true, sectionpreview: true }).then((res) => { if (res && res.parse && res.parse.text) { this.previewHtml = res.parse.text; this.haveHtml = true; } }); } }, watch: { wikitext: { immediate: true, handler(newValue) { this.previewHtml = ''; this.haveHtml = false; this.loadPreview(newValue); } } }, template: ` <div class="script-Oyver-preview"> <div v-if="!haveHtml" class="script-Oyver-preview-loading"> <cdx-progress-bar inline :aria-label="loadingLabel"></cdx-progress-bar> </div> <div v-else class="script-Oyver-preview-content" v-html="previewHtml"></div> </div> ` }; }, showPanel(sectionTitle, pageInfo, $heading, uniqueId, mountId) { const self = this; const rawLiveSectionTitle = $heading.contents().not($heading.find('.mw-editsection')).text().trim() || sectionTitle; const liveSectionTitle = self.sanitizeSectionTitle(rawLiveSectionTitle); const vueAppInstance = self.Vue.createMwApp({ components: { CdxRadio: self.Codex.CdxRadio, CdxCheckbox: self.Codex.CdxCheckbox, CdxTextArea: self.Codex.CdxTextArea, CdxTextInput: self.Codex.CdxTextInput, CdxField: self.Codex.CdxField, CdxAccordion: self.Codex.CdxAccordion, CdxMessage: self.Codex.CdxMessage, CdxButton: self.Codex.CdxButton, CdxIcon: self.Codex.CdxIcon, previewRenderer: self.getPreviewComponent() }, data() { return { version: self.version, messages: self.config.messages, sectionTitle: liveSectionTitle, uniqueId, voteOptions: Object.entries(self.config.voteOptions).map(([value, opt]) => ({ value, label: opt.label, group: opt.group, icon: self.icons[opt.icon] || null, iconUrl: opt.iconUrl, requiresComment: !!opt.requiresComment })), selectedVote: null, comment: '', showSummary: false, editSummaryInput: '/* ' + liveSectionTitle + ' */ ', summaryEdited: false, showPreview: false, submitting: false, submitError: '', showContextPreview: false, contextPreviewWikitext: '', contextPreviewLoading: false, contextPreviewDebounce: null, showPreferences: false, iconSetPref: self.prefs.iconSet, prefsIconSet: self.prefs.iconSet, prefsFullContext: self.prefs.fullContextPreviewDefault, savingPrefs: false, checkIcon: self.icons.cdxIconCheck || null, closeIcon: self.icons.cdxIconClose || null, eyeIcon: self.icons.cdxIconEye || null, eyeClosedIcon: self.icons.cdxIconEyeClosed || null, settingsIcon: self.icons.cdxIconSettings || null }; }, computed: { panelTitle() { return this.messages.windowTitle + ': ' + this.sectionTitle; }, radioName() { return 'script-Oyver-vote-' + this.uniqueId; }, prefsIconSetName() { return 'script-Oyver-prefs-iconset-' + this.uniqueId; }, selectedOption() { return this.voteOptions.find((opt) => opt.value === this.selectedVote) || null; }, defaultSummary() { const base = '/* ' + this.sectionTitle + ' */ '; return this.selectedOption ? base + this.selectedOption.label : base; }, canSubmit() { if (this.submitting || !this.selectedVote) return false; if (this.selectedOption && this.selectedOption.requiresComment && !this.comment.trim()) return false; return true; }, previewToggleIcon() { return this.showPreview ? this.eyeClosedIcon : this.eyeIcon; }, previewToggleLabel() { return this.showPreview ? this.messages.hidePreview : this.messages.showPreview; }, contextToggleIcon() { return this.showContextPreview ? this.eyeClosedIcon : this.eyeIcon; }, previewWikitext() { if (!this.selectedOption) return ''; const templateName = self.config.voteOptions[this.selectedVote].template; const trimmedComment = this.comment.trim(); let voteLine = '* {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' ' + self.signature; return voteLine; } }, watch: { selectedVote() { if (!this.summaryEdited) { this.editSummaryInput = this.defaultSummary; } if (this.showContextPreview) { this.refreshContextPreview(); } }, comment() { if (!this.showContextPreview) return; clearTimeout(this.contextPreviewDebounce); this.contextPreviewDebounce = setTimeout(() => { this.refreshContextPreview(); }, 400); } }, methods: { cancelPanel() { if (this.submitting) return; vueAppInstance.unmount(); $('#' + mountId).empty(); $('#' + uniqueId).removeClass('script-Oyver-disabled'); }, togglePreferences() { if (this.submitting) return; this.prefsIconSet = self.prefs.iconSet; this.prefsFullContext = self.prefs.fullContextPreviewDefault; this.showPreferences = !this.showPreferences; }, async savePreferences() { this.savingPrefs = true; const newPrefs = { iconSet: this.prefsIconSet, fullContextPreviewDefault: this.prefsFullContext }; await self.savePrefs(newPrefs); self.prefs = Object.assign({}, self.prefs, newPrefs); this.iconSetPref = self.prefs.iconSet; this.savingPrefs = false; this.showPreferences = false; }, async togglePreview() { if (this.showPreview) { this.showPreview = false; return; } if (self.prefs.fullContextPreviewDefault && !this.showContextPreview) { await this.toggleContextPreview(); } this.showPreview = true; }, async toggleContextPreview() { if (this.showContextPreview) { this.showContextPreview = false; return; } if (!this.selectedOption) return; await this.refreshContextPreview(); this.showContextPreview = true; }, buildQueryParams() { const queryParams = { action: 'query', prop: 'revisions', rvprop: 'content', formatversion: 2 }; if (pageInfo.type === 'direct') { queryParams.titles = mw.config.get('wgPageName'); queryParams.rvsection = pageInfo.sectionNum; } else { queryParams.titles = pageInfo.targetPage; queryParams.rvsection = pageInfo.sectionId; } return queryParams; }, buildNewSectionText(sectionText) { const templateName = self.config.voteOptions[this.selectedVote].template; const group = self.config.voteOptions[this.selectedVote].group; const trimmedComment = this.comment.trim(); const headers = self.findGroupHeaders(sectionText.split('\n')); if (headers.length) { const lines = sectionText.split('\n'); const existingHeader = headers.find((h) => h.type === group); const blockEnd = existingHeader ? self.findGroupBlockEnd(lines, existingHeader.index) : -1; const bulletChar = existingHeader ? self.getBlockBulletChar(lines, existingHeader.index, blockEnd) : '*'; let voteLine = bulletChar + ' {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' ' + self.signature; return { text: self.buildGroupedVoteText(sectionText, group, voteLine) }; } let voteLine = '\n* {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' ' + self.signature; return { appendtext: voteLine, previewText: sectionText + voteLine }; }, async refreshContextPreview() { if (!this.selectedOption) return; this.contextPreviewLoading = true; try { const api = new mw.Api(); const res = await api.get(this.buildQueryParams()); const sectionText = self.extractContent(res); if (sectionText === null) throw new Error('Bölüm içeriği alınamadı'); const built = this.buildNewSectionText(sectionText); this.contextPreviewWikitext = built.text !== undefined ? built.text : built.previewText; } catch (error) { console.error('Oyver: bölüm önizlemesi başarısız', error); } finally { this.contextPreviewLoading = false; } }, async doVote() { this.submitError = ''; this.submitting = true; try { const api = new mw.Api(); const res = await api.get(this.buildQueryParams()); const sectionText = self.extractContent(res); if (sectionText === null) throw new Error('Bölüm içeriği alınamadı'); const currentUser = mw.config.get('wgUserName'); if (self.hasExistingVote(sectionText, currentUser)) { if (!confirm(this.messages.confirmPossibleDouble)) { this.submitting = false; return; } } const summaryBase = this.editSummaryInput.trim() || this.defaultSummary; const editSummary = summaryBase + self.config.summfix; const editParams = { action: 'edit', summary: editSummary, token: mw.user.tokens.get('csrfToken') }; if (pageInfo.type === 'direct') { editParams.title = mw.config.get('wgPageName'); editParams.section = pageInfo.sectionNum; } else { editParams.title = pageInfo.targetPage; editParams.section = pageInfo.sectionId; } const built = this.buildNewSectionText(sectionText); if (built.text !== undefined) { editParams.text = built.text; } else { editParams.appendtext = built.appendtext; } await api.postWithEditToken(editParams); setTimeout(() => { window.location.reload(); }, 1200); } catch (error) { console.error('Oyver: gönderim başarısız', error); this.submitError = this.messages.editError; this.submitting = false; } } }, template: ` <div class="script-Oyver-panel"> <div class="script-Oyver-panel-header"> <span class="script-Oyver-panel-title"> {{ panelTitle }} <span class="script-Oyver-version">v{{ version }}</span> </span> <div class="script-Oyver-header-actions"> <cdx-button weight="quiet" :disabled="submitting" :aria-label="messages.preferencesButtonLabel" :title="messages.preferencesButtonLabel" @click="togglePreferences"> <cdx-icon v-if="settingsIcon" :icon="settingsIcon"></cdx-icon> </cdx-button> <cdx-button weight="quiet" :disabled="submitting" :aria-label="messages.closeButtonLabel" :title="messages.closeButtonLabel" @click="cancelPanel"> <cdx-icon v-if="closeIcon" :icon="closeIcon"></cdx-icon> </cdx-button> </div> </div> <div v-if="showPreferences" class="script-Oyver-panel-body"> <div class="script-Oyver-prefs"> <p class="script-Oyver-prefs-title">{{ messages.preferencesTitle }}</p> <cdx-field class="script-Oyver-field"> <template #label>{{ messages.preferencesIconSetLabel }}</template> <cdx-radio v-model="prefsIconSet" input-value="codex" :name="prefsIconSetName"> {{ messages.preferencesIconSetCodex }} </cdx-radio> <cdx-radio v-model="prefsIconSet" input-value="onwiki" :name="prefsIconSetName"> {{ messages.preferencesIconSetOnWiki }} </cdx-radio> </cdx-field> <cdx-checkbox v-model="prefsFullContext" class="script-Oyver-field"> {{ messages.preferencesFullContextLabel }} </cdx-checkbox> <div class="script-Oyver-buttons"> <cdx-button weight="primary" action="progressive" :disabled="savingPrefs" @click="savePreferences"> <cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon> {{ messages.preferencesSave }} </cdx-button> <cdx-button :disabled="savingPrefs" @click="showPreferences = false"> {{ messages.cancelButton }} </cdx-button> </div> </div> </div> <div v-else class="script-Oyver-panel-body"> <p>{{ messages.vote }}</p> <div v-for="option in voteOptions" :key="option.value" class="script-Oyver-option"> <cdx-radio v-model="selectedVote" :input-value="option.value" :name="radioName" :disabled="submitting" > <cdx-icon v-if="iconSetPref === 'codex' && option.icon" :icon="option.icon"></cdx-icon> <img v-else-if="iconSetPref === 'onwiki' && option.iconUrl" :src="option.iconUrl" width="20" height="20" alt="" class="script-Oyver-onwiki-icon" > {{ option.label }} </cdx-radio> </div> <cdx-field class="script-Oyver-field"> <template #label>{{ messages.comment }}</template> <cdx-text-area v-model="comment" :rows="3" :disabled="submitting"></cdx-text-area> </cdx-field> <p class="script-Oyver-signature-note">{{ messages.signatureAutoAdd }}</p> <cdx-accordion v-model="showSummary" class="script-Oyver-field"> <template #title>{{ messages.editSummaryLabel }}</template> <cdx-text-input v-model="editSummaryInput" :disabled="submitting" @input="summaryEdited = true" ></cdx-text-input> </cdx-accordion> <cdx-message v-if="submitError" type="error" :allow-user-dismiss="false" class="script-Oyver-status"> {{ submitError }} </cdx-message> <cdx-message v-if="submitting" type="notice" :allow-user-dismiss="false" class="script-Oyver-status"> {{ messages.submitting }} </cdx-message> <div class="script-Oyver-buttons"> <cdx-button weight="primary" action="progressive" :disabled="!canSubmit" @click="doVote"> <cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon> {{ messages.windowButton }} </cdx-button> <cdx-button :disabled="!selectedVote || submitting" @click="togglePreview"> <cdx-icon v-if="previewToggleIcon" :icon="previewToggleIcon"></cdx-icon> {{ previewToggleLabel }} </cdx-button> <cdx-button :disabled="submitting" @click="cancelPanel"> {{ messages.cancelButton }} </cdx-button> </div> <div v-if="showPreview && selectedVote" class="script-Oyver-preview-wrapper"> <div class="script-Oyver-preview-toolbar"> <cdx-button weight="quiet" :disabled="contextPreviewLoading" :aria-label="messages.toggleFullSectionLabel" :title="messages.toggleFullSectionLabel" @click="toggleContextPreview" > <cdx-icon v-if="contextToggleIcon" :icon="contextToggleIcon"></cdx-icon> </cdx-button> </div> <preview-renderer :wikitext="showContextPreview ? contextPreviewWikitext : previewWikitext"></preview-renderer> </div> </div> </div> ` }); vueAppInstance.mount('#' + mountId); } }; Oyver.init(); }); // </nowiki> 6e9f5qj0qobaf49goj4uogtv2oyc26t 17885 17884 2026-10-24T19:21:26Z Brightt11 1786 17885 javascript text/javascript // Oyver - EasyVoteMeta (Author: Max) betiğinin yerelleştirilmiş versiyonu. // Orijinal betik kaynağı/Original script source: https://meta.wikimedia.org/wiki/User:Max/EasyVoteMeta.js // Bu betik, oylama/görüş bildirme sayfalarında bölüm başlıklarına "hızlı oy ver" bağlantısı ekler. // sürüm: 1.0 beta 3. // <nowiki> $(() => { 'use strict'; const Oyver = { version: '1.0 beta 3', signature: '~~' + '~~', config: { messages: { windowTitle: 'Oyver', windowButton: 'Gönder', cancelButton: 'İptal', vote: 'Görüş belirtmek için oy seçin:', comment: 'Yorum:', signatureAutoAdd: 'İmzanız otomatik olarak eklenecektir.', editSummaryLabel: 'Değişiklik özeti', showPreview: 'Önizlemeyi göster', hidePreview: 'Önizlemeyi gizle', preferencesTitle: 'Tercihler', preferencesIconSetLabel: 'Oy simgesi görünümü', preferencesIconSetCodex: 'Codex simgeleri', preferencesIconSetOnWiki: 'Wiki simgeleri', preferencesFullContextLabel: 'Önizlemede daima tüm bölümü göster', preferencesSave: 'Kaydet', preferencesButtonLabel: 'Tercihler', closeButtonLabel: 'Kapat', toggleFullSectionLabel: 'Tüm bölüm önizlemesini açın/kapatın', loadingPreviewLabel: 'Önizleme yükleniyor', submitting: 'Gönderiliyor. Lütfen internet tarayıcınızı açık tutun.', voteLinkText: 'hızlı oy ver', editError: 'Hata: Bir hata oldu. Klavyenizden F5 tuşuna basarak sayfayı yenileyin ve tekrar deneyin', confirmPossibleDouble: 'Bu başvuruda daha önce görüş yapmışsınız. Devam etmek istediğinizden emin misiniz? Lütfen önceki görüş(ler)inizin üstünü çizerek (<nowiki><s></s></nowiki> kullanarak) oyunuzu geri çekin.' }, voteOptions: { support: { label: 'Destek', template: 'Destek', icon: 'cdxIconCheck', group: 'support', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/9/94/Symbol_support_vote.svg' }, halfsupport: { label: 'Kısmi destek', template: 'Kısmi destek', icon: 'cdxIconCheck', group: 'support', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/8c/GA_candidate.svg' }, oppose: { label: 'Karşı', template: 'Karşı', icon: 'cdxIconClose', group: 'oppose', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/7/7f/Symbol_oppose_vote.svg' }, halfoppose: { label: 'Kısmen karşı', template: 'Kısmen karşı', icon: 'cdxIconClose', group: 'oppose', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/f/f5/BA_candidate.svg' }, neutral: { label: 'Tarafsız', template: 'Tarafsız', icon: 'cdxIconInfoFilled', group: 'neutral', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/8/89/Symbol_neutral_vote.svg' }, comment: { label: 'Yorum', template: 'Yorum', icon: 'cdxIconSpeechBubble', group: 'neutral', iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/d/df/Pictogram_voting_comment_%28orange%29.svg?utm_source=commons.wikimedia.org&utm_campaign=index&utm_content=original', requiresComment: true, skipDoubleCheck: true } }, voteAliases: [], groupOrder: ['support', 'oppose', 'neutral'], groupHeaders: { support: ['Destek', 'Evet'], oppose: ['Karşı', 'Hayır'], neutral: ['Tarafsız', 'Yorum'] }, iconNames: ['cdxIconCheck', 'cdxIconClose', 'cdxIconInfoFilled', 'cdxIconAlert', 'cdxIconSpeechBubble', 'cdxIconEye', 'cdxIconEyeClosed', 'cdxIconSettings'], summfix: ' ([[Kullanıcı:Brightt11/Oyver.js|Oyver (beta)]] ile görüş belirtildi.)', pageConfigs: { 'Vikigezgin:Kullanıcı hakları adaylıkları': { type: 'auto', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [] } }, namespaceConfigs: { 8: { type: 'direct', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [], dbNames: [] }, 828: { type: 'direct', headingSelector: 'div.mw-heading.mw-heading3', skipPrefixes: [], dbNames: [] } } }, icons: {}, prefsPrefix: 'userjs-oyver-', prefsDefaults: { iconSet: 'codex', fullContextPreviewDefault: false }, prefsCache: {}, prefs: {}, currentPageConfig: null, init() { const currentPage = mw.config.get('wgPageName').replace(/_/g, ' '); this.currentPageConfig = this.config.pageConfigs[currentPage] || (this.config.namespaceConfigs || {})[mw.config.get('wgNamespaceNumber')]; if (!this.currentPageConfig) return; const dbNames = this.currentPageConfig.dbNames; if (dbNames && dbNames.length && !dbNames.includes(mw.config.get('wgDBname'))) return; if (mw.user.isAnon()) return; mw.loader.using(['vue', '@wikimedia/codex', 'mediawiki.util', 'mediawiki.api', 'mediawiki.user']).then((require) => { this.Vue = require('vue'); this.Codex = require('@wikimedia/codex'); this.run(); }); }, async loadIcons() { try { const api = new mw.Api(); const data = await api.get({ action: 'query', list: 'codexicons', names: this.config.iconNames.join('|'), formatversion: 2 }); this.icons = (data && data.query && data.query.codexicons) || {}; } catch (error) { this.icons = {}; } }, getPrefFullKey(key) { return this.prefsPrefix + key; }, loadPref(key) { const fullKey = this.getPrefFullKey(key); const fallback = this.prefsDefaults[key]; let value; if (Object.prototype.hasOwnProperty.call(this.prefsCache, fullKey)) { value = this.prefsCache[fullKey]; } else { try { value = window.localStorage ? window.localStorage.getItem(fullKey) : null; } catch (error) { value = null; } } if (value !== null && value !== undefined) { try { return JSON.parse(value); } catch (error) { return fallback; } } try { const serverValue = mw.user.options.get(fullKey, JSON.stringify(fallback)); return JSON.parse(serverValue); } catch (error) { return fallback; } }, loadAllPrefs() { const prefs = {}; Object.keys(this.prefsDefaults).forEach((key) => { prefs[key] = this.loadPref(key); }); return prefs; }, async savePref(key, value) { const fullKey = this.getPrefFullKey(key); const fullValue = JSON.stringify(value); this.prefsCache[fullKey] = fullValue; try { if (window.localStorage) window.localStorage.setItem(fullKey, fullValue); } catch (error) { } try { const api = new mw.Api(); await api.postWithEditToken({ action: 'options', optionname: fullKey, optionvalue: fullValue }); } catch (error) { console.error('Oyver: Hata. Tercih kaydedilemedi.', key, error); } }, async savePrefs(newPrefs) { const keys = Object.keys(newPrefs); for (const key of keys) { await this.savePref(key, newPrefs[key]); } }, async run() { this.addStyle(); this.prefs = this.loadAllPrefs(); await this.loadIcons(); const pageConfig = this.currentPageConfig; const skipPrefixes = (pageConfig.skipPrefixes || []).map((p) => p.toLocaleLowerCase('tr')); $(pageConfig.headingSelector || 'div.mw-heading.mw-heading3').each((_, heading) => { const $heading = $(heading); const editLinks = $heading.find('.mw-editsection'); const headingId = $heading.find('h1,h2,h3,h4,h5,h6').first().attr('id'); if (!headingId) return; const sectionTitle = headingId.replace(/_/g, ' '); const normalizedTitle = sectionTitle.toLocaleLowerCase('tr'); if (skipPrefixes.some((prefix) => normalizedTitle.startsWith(prefix))) return; if (pageConfig.type === 'direct') { this.handleDirectPage(editLinks, sectionTitle, $heading); } else if (pageConfig.type === 'transcluded') { this.handleTranscludedPage(editLinks, sectionTitle, $heading); } else if (pageConfig.type === 'auto') { this.handleAutoPage(editLinks, sectionTitle, $heading); } }); }, handleAutoPage(editLinks, sectionTitle, $heading) { const info = this.getTargetPageInfo(editLinks); if (!info) return; const currentPage = mw.config.get('wgPageName').replace(/_/g, ' '); if (info.targetPage === currentPage && /^\d+$/.test(info.sectionId)) { this.addVoteLink(editLinks, sectionTitle, { type: 'direct', sectionNum: parseInt(info.sectionId, 10) }, $heading); } else { this.addVoteLink(editLinks, sectionTitle, Object.assign({ type: 'transcluded' }, info), $heading); } }, handleDirectPage(editLinks, sectionTitle, $heading) { const sectionNum = this.getSectionNumber(editLinks); if (sectionNum !== -1) { this.addVoteLink(editLinks, sectionTitle, { type: 'direct', sectionNum }, $heading); } }, handleTranscludedPage(editLinks, sectionTitle, $heading) { const targetPageInfo = this.getTargetPageInfo(editLinks); if (targetPageInfo && targetPageInfo.targetPage && targetPageInfo.sectionId) { this.addVoteLink(editLinks, sectionTitle, Object.assign({ type: 'transcluded' }, targetPageInfo), $heading); } }, addStyle() { mw.util.addCSS(` .script-Oyver-link { color: var(--color-progressive, #36c); text-decoration: none; font-weight: bold; } .script-Oyver-link:hover { text-decoration: underline; } .script-Oyver-disabled { color: var(--color-disabled, #a2a9b1) !important; pointer-events: none; } .script-Oyver-divider { border: none; border-top: 1px solid var(--border-color-subtle, #eaecf0); margin: 8px 0 4px; } .script-Oyver-panel { border: 1px solid var(--border-color-base, #a2a9b1); background: var(--background-color-base, #fff); padding: 16px; margin: 8px 0 16px; } .script-Oyver-panel-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } .script-Oyver-panel-title { font-weight: bold; font-size: 15px; } .script-Oyver-version { font-size: 0.7em; font-weight: normal; color: var(--color-subtle, #54595d); vertical-align: middle; margin-left: 2px; } .script-Oyver-header-actions { display: flex; gap: 4px; } .script-Oyver-panel-body p:first-child { margin-top: 0; } .script-Oyver-option { margin: 8px 0; } .script-Oyver-option .cdx-icon { margin-right: 4px; } .script-Oyver-onwiki-icon { margin-right: 4px; vertical-align: middle; } .script-Oyver-field { margin-top: 14px; } .script-Oyver-signature-note { font-style: italic; color: var(--color-subtle, #54595d); font-size: 0.9em; margin-top: 8px; } .script-Oyver-status { margin-top: 12px; } .script-Oyver-buttons { margin-top: 16px; display: flex; gap: 8px; flex-wrap: wrap; } .script-Oyver-preview-wrapper { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border-color-subtle, #eaecf0); } .script-Oyver-preview-toolbar { display: flex; justify-content: flex-end; } .script-Oyver-preview { margin-top: 0; } .script-Oyver-preview-content { font-size: 0.95em; line-height: 1.6; } .script-Oyver-preview-loading { display: flex; align-items: center; gap: 8px; color: var(--color-subtle, #54595d); font-size: 0.88em; } .script-Oyver-prefs { border: 1px solid var(--border-color-subtle, #eaecf0); padding: 12px; } .script-Oyver-prefs-title { font-weight: bold; margin: 0 0 8px; } `); }, getSectionNumber(editLinks) { const editSectionUrl = editLinks.find('a:first').attr('href'); const sectionMatch = editSectionUrl && editSectionUrl.match(/[?&]section=(\d+)(?:&|$)/); return sectionMatch ? parseInt(sectionMatch[1], 10) : -1; }, getTargetPageInfo(editLinks) { const editSectionUrl = editLinks.find('a:first').attr('href'); if (!editSectionUrl) return null; const titleMatch = editSectionUrl.match(/[?&]title=([^&]+)/); const sectionMatch = editSectionUrl.match(/[?&]section=([^&]+)/); if (!titleMatch || !sectionMatch) return null; return { targetPage: decodeURIComponent(titleMatch[1]).replace(/_/g, ' '), sectionId: sectionMatch[1] }; }, extractContent(apiData) { const pages = apiData && apiData.query && apiData.query.pages; if (!pages || !pages[0] || !pages[0].revisions || !pages[0].revisions[0]) return null; return pages[0].revisions[0].content; }, sanitizeSectionTitle(rawTitle) { if (!rawTitle) return ''; const emojiPattern = /(\p{Extended_Pictographic}|\p{Emoji_Presentation}|[\u{1F1E6}-\u{1F1FF}]|\u{FE0F}|\u{200D})/gu; let text = rawTitle.replace(emojiPattern, ''); let cutIndex = -1; for (let i = 0; i < text.length; i++) { if (text[i] === '(') { cutIndex = i; break; } if (text[i] === '[') { if (text[i + 1] === '[') { i++; continue; } cutIndex = i; break; } } if (cutIndex !== -1) { text = text.substring(0, cutIndex); } return text.replace(/\s+/g, ' ').trim(); }, detectGroupHeaderType(line) { const match = line.match(/^;\s*(\S.*)$/); if (!match) return null; const text = match[1].toLocaleLowerCase('tr'); for (const key of this.config.groupOrder) { const words = this.config.groupHeaders[key] || []; for (const word of words) { const w = word.toLocaleLowerCase('tr'); if (text.startsWith(w) && !/^\p{L}/u.test(text.slice(w.length))) { return key; } } } return null; }, findGroupHeaders(lines) { return lines.reduce((acc, line, idx) => { const type = this.detectGroupHeaderType(line); if (type) acc.push({ type, index: idx }); return acc; }, []); }, findGroupBlockEnd(lines, headerIdx) { for (let i = headerIdx + 1; i < lines.length; i++) { if (this.detectGroupHeaderType(lines[i])) return i; } return lines.length; }, getBlockBulletChar(lines, headerIdx, blockEnd) { for (let i = headerIdx + 1; i < blockEnd; i++) { const match = lines[i].match(/^\s*([#*:]+)/); if (match) return match[1]; } return '*'; }, getGroupHeaderText(group) { const words = this.config.groupHeaders[group]; return (words && words[0]) || group; }, buildGroupedVoteText(sectionText, group, voteLine) { const lines = sectionText.split('\n'); const headers = this.findGroupHeaders(lines); const existing = headers.find((h) => h.type === group); const headerLine = ';' + this.getGroupHeaderText(group); if (existing) { const blockEnd = this.findGroupBlockEnd(lines, existing.index); let insertIdx = blockEnd; while (insertIdx - 1 > existing.index && lines[insertIdx - 1].trim() === '') insertIdx--; lines.splice(insertIdx, 0, voteLine); return lines.join('\n'); } const order = this.config.groupOrder; const orderIdx = order.indexOf(group); for (let o = orderIdx + 1; o < order.length; o++) { const laterHeader = headers.find((h) => h.type === order[o]); if (laterHeader) { lines.splice(laterHeader.index, 0, headerLine, voteLine, ''); return lines.join('\n'); } } for (let o = orderIdx - 1; o >= 0; o--) { const earlierHeader = headers.find((h) => h.type === order[o]); if (earlierHeader) { const blockEnd = this.findGroupBlockEnd(lines, earlierHeader.index); lines.splice(blockEnd, 0, '', headerLine, voteLine); return lines.join('\n'); } } let lastNonBlank = lines.length - 1; while (lastNonBlank >= 0 && lines[lastNonBlank].trim() === '') lastNonBlank--; lines.splice(lastNonBlank + 1, 0, '', headerLine, voteLine); return lines.join('\n'); }, hasExistingVote(sectionText, currentUser) { const templates = Object.values(this.config.voteOptions).filter((opt) => !opt.skipDoubleCheck).map((opt) => opt.template); const aliases = this.config.voteAliases || []; const allNames = [...templates, ...aliases].map((name) => mw.util.escapeRegExp(name)); const escapedUser = mw.util.escapeRegExp(currentUser); const pattern = new RegExp('\\{\\{(?:' + allNames.join('|') + ')\\}\\}[^\\n~]{0,300}?' + escapedUser, 'i'); return pattern.test(sectionText); }, addVoteLink(editLinks, sectionTitle, pageInfo, $heading) { const uniqueId = 'script-Oyver-launch-' + Math.random().toString(36).slice(2, 11); const mountId = 'script-Oyver-mount-' + uniqueId; const voteLink = $('<a>') .attr('id', uniqueId) .addClass('script-Oyver-link') .text(this.config.messages.voteLinkText) .attr('href', '#') .click((event) => { event.preventDefault(); if (!voteLink.hasClass('script-Oyver-disabled')) { voteLink.addClass('script-Oyver-disabled'); if (!$('#' + mountId).length) { $heading.after( $('<div>').attr('id', mountId), $('<hr>').addClass('script-Oyver-divider') ); } this.showPanel(sectionTitle, pageInfo, $heading, uniqueId, mountId); } }); editLinks.children().last().before(' | ', voteLink); }, getPreviewComponent() { const loadingLabel = this.config.messages.loadingPreviewLabel; return { components: { CdxProgressBar: this.Codex.CdxProgressBar }, props: { wikitext: { type: String, default: '' } }, data() { return { previewHtml: '', haveHtml: false, loadingLabel }; }, methods: { loadPreview(wikitextToPreview) { new mw.Api().get({ action: 'parse', formatversion: 2, title: mw.config.get('wgPageName'), text: wikitextToPreview, prop: 'text', pst: true, disablelimitreport: true, disableeditsection: true, sectionpreview: true }).then((res) => { if (res && res.parse && res.parse.text) { this.previewHtml = res.parse.text; this.haveHtml = true; } }); } }, watch: { wikitext: { immediate: true, handler(newValue) { this.previewHtml = ''; this.haveHtml = false; this.loadPreview(newValue); } } }, template: ` <div class="script-Oyver-preview"> <div v-if="!haveHtml" class="script-Oyver-preview-loading"> <cdx-progress-bar inline :aria-label="loadingLabel"></cdx-progress-bar> </div> <div v-else class="script-Oyver-preview-content" v-html="previewHtml"></div> </div> ` }; }, showPanel(sectionTitle, pageInfo, $heading, uniqueId, mountId) { const self = this; const rawLiveSectionTitle = $heading.contents().not($heading.find('.mw-editsection')).text().trim() || sectionTitle; const liveSectionTitle = self.sanitizeSectionTitle(rawLiveSectionTitle); const vueAppInstance = self.Vue.createMwApp({ components: { CdxRadio: self.Codex.CdxRadio, CdxCheckbox: self.Codex.CdxCheckbox, CdxTextArea: self.Codex.CdxTextArea, CdxTextInput: self.Codex.CdxTextInput, CdxField: self.Codex.CdxField, CdxAccordion: self.Codex.CdxAccordion, CdxMessage: self.Codex.CdxMessage, CdxButton: self.Codex.CdxButton, CdxIcon: self.Codex.CdxIcon, previewRenderer: self.getPreviewComponent() }, data() { return { version: self.version, messages: self.config.messages, sectionTitle: liveSectionTitle, uniqueId, voteOptions: Object.entries(self.config.voteOptions).map(([value, opt]) => ({ value, label: opt.label, group: opt.group, icon: self.icons[opt.icon] || null, iconUrl: opt.iconUrl, requiresComment: !!opt.requiresComment })), selectedVote: null, comment: '', showSummary: false, editSummaryInput: '/* ' + liveSectionTitle + ' */ ', summaryEdited: false, showPreview: false, submitting: false, submitError: '', showContextPreview: false, contextPreviewWikitext: '', contextPreviewLoading: false, contextPreviewDebounce: null, showPreferences: false, iconSetPref: self.prefs.iconSet, prefsIconSet: self.prefs.iconSet, prefsFullContext: self.prefs.fullContextPreviewDefault, savingPrefs: false, checkIcon: self.icons.cdxIconCheck || null, closeIcon: self.icons.cdxIconClose || null, eyeIcon: self.icons.cdxIconEye || null, eyeClosedIcon: self.icons.cdxIconEyeClosed || null, settingsIcon: self.icons.cdxIconSettings || null }; }, computed: { panelTitle() { return this.messages.windowTitle + ': ' + this.sectionTitle; }, radioName() { return 'script-Oyver-vote-' + this.uniqueId; }, prefsIconSetName() { return 'script-Oyver-prefs-iconset-' + this.uniqueId; }, selectedOption() { return this.voteOptions.find((opt) => opt.value === this.selectedVote) || null; }, defaultSummary() { const base = '/* ' + this.sectionTitle + ' */ '; return this.selectedOption ? base + this.selectedOption.label : base; }, canSubmit() { if (this.submitting || !this.selectedVote) return false; if (this.selectedOption && this.selectedOption.requiresComment && !this.comment.trim()) return false; return true; }, previewToggleIcon() { return this.showPreview ? this.eyeClosedIcon : this.eyeIcon; }, previewToggleLabel() { return this.showPreview ? this.messages.hidePreview : this.messages.showPreview; }, contextToggleIcon() { return this.showContextPreview ? this.eyeClosedIcon : this.eyeIcon; }, previewWikitext() { if (!this.selectedOption) return ''; const templateName = self.config.voteOptions[this.selectedVote].template; const trimmedComment = this.comment.trim(); let voteLine = '* {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' ' + self.signature; return voteLine; } }, watch: { selectedVote() { if (!this.summaryEdited) { this.editSummaryInput = this.defaultSummary; } if (this.showContextPreview) { this.refreshContextPreview(); } }, comment() { if (!this.showContextPreview) return; clearTimeout(this.contextPreviewDebounce); this.contextPreviewDebounce = setTimeout(() => { this.refreshContextPreview(); }, 400); } }, methods: { cancelPanel() { if (this.submitting) return; vueAppInstance.unmount(); $('#' + mountId).empty(); $('#' + uniqueId).removeClass('script-Oyver-disabled'); }, togglePreferences() { if (this.submitting) return; this.prefsIconSet = self.prefs.iconSet; this.prefsFullContext = self.prefs.fullContextPreviewDefault; this.showPreferences = !this.showPreferences; }, async savePreferences() { this.savingPrefs = true; const newPrefs = { iconSet: this.prefsIconSet, fullContextPreviewDefault: this.prefsFullContext }; await self.savePrefs(newPrefs); self.prefs = Object.assign({}, self.prefs, newPrefs); this.iconSetPref = self.prefs.iconSet; this.savingPrefs = false; this.showPreferences = false; }, async togglePreview() { if (this.showPreview) { this.showPreview = false; return; } if (self.prefs.fullContextPreviewDefault && !this.showContextPreview) { await this.toggleContextPreview(); } this.showPreview = true; }, async toggleContextPreview() { if (this.showContextPreview) { this.showContextPreview = false; return; } if (!this.selectedOption) return; await this.refreshContextPreview(); this.showContextPreview = true; }, buildQueryParams() { const queryParams = { action: 'query', prop: 'revisions', rvprop: 'content', formatversion: 2 }; if (pageInfo.type === 'direct') { queryParams.titles = mw.config.get('wgPageName'); queryParams.rvsection = pageInfo.sectionNum; } else { queryParams.titles = pageInfo.targetPage; queryParams.rvsection = pageInfo.sectionId; } return queryParams; }, buildNewSectionText(sectionText) { const templateName = self.config.voteOptions[this.selectedVote].template; const group = self.config.voteOptions[this.selectedVote].group; const trimmedComment = this.comment.trim(); const headers = self.findGroupHeaders(sectionText.split('\n')); if (headers.length) { const lines = sectionText.split('\n'); const existingHeader = headers.find((h) => h.type === group); const blockEnd = existingHeader ? self.findGroupBlockEnd(lines, existingHeader.index) : -1; const bulletChar = existingHeader ? self.getBlockBulletChar(lines, existingHeader.index, blockEnd) : '*'; let voteLine = bulletChar + ' {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' ' + self.signature; return { text: self.buildGroupedVoteText(sectionText, group, voteLine) }; } let voteLine = '\n* {{' + templateName + '}}'; if (trimmedComment) voteLine += ' ' + trimmedComment; voteLine += ' ' + self.signature; return { appendtext: voteLine, previewText: sectionText + voteLine }; }, async refreshContextPreview() { if (!this.selectedOption) return; this.contextPreviewLoading = true; try { const api = new mw.Api(); const res = await api.get(this.buildQueryParams()); const sectionText = self.extractContent(res); if (sectionText === null) throw new Error('Bölüm içeriği alınamadı'); const built = this.buildNewSectionText(sectionText); this.contextPreviewWikitext = built.text !== undefined ? built.text : built.previewText; } catch (error) { console.error('Oyver: bölüm önizlemesi başarısız', error); } finally { this.contextPreviewLoading = false; } }, async doVote() { this.submitError = ''; this.submitting = true; try { const api = new mw.Api(); const res = await api.get(this.buildQueryParams()); const sectionText = self.extractContent(res); if (sectionText === null) throw new Error('Bölüm içeriği alınamadı'); const currentUser = mw.config.get('wgUserName'); if (self.hasExistingVote(sectionText, currentUser)) { if (!confirm(this.messages.confirmPossibleDouble)) { this.submitting = false; return; } } const summaryBase = this.editSummaryInput.trim() || this.defaultSummary; const editSummary = summaryBase + self.config.summfix; const editParams = { action: 'edit', summary: editSummary, token: mw.user.tokens.get('csrfToken') }; if (pageInfo.type === 'direct') { editParams.title = mw.config.get('wgPageName'); editParams.section = pageInfo.sectionNum; } else { editParams.title = pageInfo.targetPage; editParams.section = pageInfo.sectionId; } const built = this.buildNewSectionText(sectionText); if (built.text !== undefined) { editParams.text = built.text; } else { editParams.appendtext = built.appendtext; } await api.postWithEditToken(editParams); setTimeout(() => { window.location.reload(); }, 1200); } catch (error) { console.error('Oyver: gönderim başarısız', error); this.submitError = this.messages.editError; this.submitting = false; } } }, template: ` <div class="script-Oyver-panel"> <div class="script-Oyver-panel-header"> <span class="script-Oyver-panel-title"> {{ panelTitle }} <span class="script-Oyver-version">v{{ version }}</span> </span> <div class="script-Oyver-header-actions"> <cdx-button weight="quiet" :disabled="submitting" :aria-label="messages.preferencesButtonLabel" :title="messages.preferencesButtonLabel" @click="togglePreferences"> <cdx-icon v-if="settingsIcon" :icon="settingsIcon"></cdx-icon> </cdx-button> <cdx-button weight="quiet" :disabled="submitting" :aria-label="messages.closeButtonLabel" :title="messages.closeButtonLabel" @click="cancelPanel"> <cdx-icon v-if="closeIcon" :icon="closeIcon"></cdx-icon> </cdx-button> </div> </div> <div v-if="showPreferences" class="script-Oyver-panel-body"> <div class="script-Oyver-prefs"> <p class="script-Oyver-prefs-title">{{ messages.preferencesTitle }}</p> <cdx-field class="script-Oyver-field"> <template #label>{{ messages.preferencesIconSetLabel }}</template> <cdx-radio v-model="prefsIconSet" input-value="codex" :name="prefsIconSetName"> {{ messages.preferencesIconSetCodex }} </cdx-radio> <cdx-radio v-model="prefsIconSet" input-value="onwiki" :name="prefsIconSetName"> {{ messages.preferencesIconSetOnWiki }} </cdx-radio> </cdx-field> <cdx-checkbox v-model="prefsFullContext" class="script-Oyver-field"> {{ messages.preferencesFullContextLabel }} </cdx-checkbox> <div class="script-Oyver-buttons"> <cdx-button weight="primary" action="progressive" :disabled="savingPrefs" @click="savePreferences"> <cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon> {{ messages.preferencesSave }} </cdx-button> <cdx-button :disabled="savingPrefs" @click="showPreferences = false"> {{ messages.cancelButton }} </cdx-button> </div> </div> </div> <div v-else class="script-Oyver-panel-body"> <p>{{ messages.vote }}</p> <div v-for="option in voteOptions" :key="option.value" class="script-Oyver-option"> <cdx-radio v-model="selectedVote" :input-value="option.value" :name="radioName" :disabled="submitting" > <cdx-icon v-if="iconSetPref === 'codex' && option.icon" :icon="option.icon"></cdx-icon> <img v-else-if="iconSetPref === 'onwiki' && option.iconUrl" :src="option.iconUrl" width="20" height="20" alt="" class="script-Oyver-onwiki-icon" > {{ option.label }} </cdx-radio> </div> <cdx-field class="script-Oyver-field"> <template #label>{{ messages.comment }}</template> <cdx-text-area v-model="comment" :rows="3" :disabled="submitting"></cdx-text-area> </cdx-field> <p class="script-Oyver-signature-note">{{ messages.signatureAutoAdd }}</p> <cdx-accordion v-model="showSummary" class="script-Oyver-field"> <template #title>{{ messages.editSummaryLabel }}</template> <cdx-text-input v-model="editSummaryInput" :disabled="submitting" @input="summaryEdited = true" ></cdx-text-input> </cdx-accordion> <cdx-message v-if="submitError" type="error" :allow-user-dismiss="false" class="script-Oyver-status"> {{ submitError }} </cdx-message> <cdx-message v-if="submitting" type="notice" :allow-user-dismiss="false" class="script-Oyver-status"> {{ messages.submitting }} </cdx-message> <div class="script-Oyver-buttons"> <cdx-button weight="primary" action="progressive" :disabled="!canSubmit" @click="doVote"> <cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon> {{ messages.windowButton }} </cdx-button> <cdx-button :disabled="!selectedVote || submitting" @click="togglePreview"> <cdx-icon v-if="previewToggleIcon" :icon="previewToggleIcon"></cdx-icon> {{ previewToggleLabel }} </cdx-button> <cdx-button :disabled="submitting" @click="cancelPanel"> {{ messages.cancelButton }} </cdx-button> </div> <div v-if="showPreview && selectedVote" class="script-Oyver-preview-wrapper"> <div class="script-Oyver-preview-toolbar"> <cdx-button weight="quiet" :disabled="contextPreviewLoading" :aria-label="messages.toggleFullSectionLabel" :title="messages.toggleFullSectionLabel" @click="toggleContextPreview" > <cdx-icon v-if="contextToggleIcon" :icon="contextToggleIcon"></cdx-icon> </cdx-button> </div> <preview-renderer :wikitext="showContextPreview ? contextPreviewWikitext : previewWikitext"></preview-renderer> </div> </div> </div> ` }); vueAppInstance.mount('#' + mountId); } }; Oyver.init(); }); // </nowiki> 99au7bx31m9ubfagt91ad70qtid6c2k Şablon:İnaktiflik politikası uyarı 10 5090 17859 2026-10-24T16:35:15Z Brightt11 1786 + 17859 wikitext text/x-wiki {{çikutu|metin=Wikimedia Metawiki'de geçerli olan küresel politika gereğince arayüz yöneticiliği, hizmetlilik ve bürokratlık hakları bulunan kullanıcının 2 yıl inaktif kalması durumunda bu haklar geri alınır. Ayrıca geçici görev gören kullanıcılar süre bitiminden 1 hafta önce mesaj sayfalarında görev süresinin dolacağı ile ilgili bir bildirim alacaktır. Bu süre zarfında herhangi bir görev yenileme başvurusu yapılmaması durumunda kullanıcı hakları ikinci bir bildiri olmaksızın geri alınır. Kullanıcı haklarınızı korumak ve görevinize devam etmek istiyorsanız, geçici görev süresinin bitiminden 1 ay önce görev yenileme başvurusu yapmalısınız.}} d0kiyermg1rdndbba8114pth7mtuxr6 Şablon:Kısmi destek 10 5091 17870 2026-10-24T17:12:56Z Brightt11 1786 + 17870 wikitext text/x-wiki [[Dosya:GA candidate.svg|15pik|Kısmî destek|link=şablon:Kısmî destek]] '''{{{1|Kısmî destek}}}'''<noinclude> [[Kategori:Resimli yorum şablonları]] </noinclude> bmilfvkwecwo2p7271d7uvu34cg3ro8 Şablon:Kısmen karşı 10 5092 17871 2026-10-24T17:13:03Z Brightt11 1786 + 17871 wikitext text/x-wiki [[Dosya:BA candidate.svg|15pik|Kısmen karşı|link=şablon:Kısmen karşı]] '''{{{1|Kısmen karşı}}}'''<noinclude> [[Kategori:Resimli yorum şablonları]] </noinclude> 33vzomj4vx5bt3pcqcnh94bpoyytqtk