<commit_before>import * as $ from 'jquery'
import * as pathUtils from 'src/utils/path'
import * as jQUtils from 'src/utils/document'
import {PixivAssistantServer} from 'src/services'
import {RegisteredAction, ExecuteIfSetting, ExecuteOnLoad} from 'src/utils/actionDecorators'
import {GalleryPage} from 'src/pages/gallery'
import SettingKeys from 'src/settingKeys'
import {Model} from 'common/proto'

import {injectUserRelationshipButton} from 'src/injectors/openFolderInjector'
import {injectOpenInTabs} from 'src/injectors/openInTabs'
import {injectCountBadge} from 'src/injectors/countBadge'
import {Container as Deps} from 'src/deps'

import * as pixivBridge from 'src/utils/pixivBridge'

import {prefix} from 'src/utils/log'
let console = prefix('Works Page');

/**
 * The listing of an artist's works. 
 */
export class WorksPage extends GalleryPage {
	public get artistId():number {
		return pathUtils.getArtistId(this.path);
	}
	public get artistName():string {
		return $('h1.user').text();
	}
	public get artist():Model.Artist {
		return { id: this.artistId, name: this.artistName };
	}
	public get allImages():JQuery[] {
		return $('li.image-item').toArray().map(x => $(x));
	}

	protected getTagElements() {
		return [
			'span.tag-badge',
			'div.user-tags li a'
		].map(tag => $(tag)).concat(super.getTagElements());
	}

	protected executeOnEachImage<T>(func:(image:JQuery) => T) {
		$('li.image-item').toArray().forEach(image => func($(image)));
	}

	@ExecuteIfSetting(SettingKeys.global.inject.openToArtistButton)
	public injectOpenFolder(){
		injectUserRelationshipButton(this.artist);
	}
	@ExecuteIfSetting(SettingKeys.pages.works.inject.openInTabs)
	public injectOpenTabs(){
		injectOpenInTabs('Open in Tabs', this.openTabs.bind(this));
		injectOpenInTabs('Open all in Tabs', this.openAllInTabs.bind(this));
	}
	@ExecuteOnLoad
	public injectTotalWorks() {
		pixivBridge.userProfile(this.artistId).then(result => {
			let imageCount = result.body[0].illusts.reduce((acc:number, cur:any) => acc + parseInt(cur.illust_page_count), 0);
			injectCountBadge(`${imageCount} total images`);
		})
	}
	@ExecuteIfSetting(SettingKeys.global.inject.pagingButtons)
	public injectPagingButtons(){
		super.injectPagingButtons();
	}

	@ExecuteIfSetting(SettingKeys.global.fadeDownloadedImages)
	public experimentalFade() {
		let imageMap = this.allImages.reduce((acc: { [id:string] : JQuery }, cur:JQuery) => {
			let imageId = pathUtils.getImageId(cur.find('a.work').attr('href'));
			acc[imageId.toString()] = cur;
			return acc;
		}, <{ [id: string]: JQuery }> {});

		let request = Object.keys(imageMap)
						.map(id => ({ artist: this.artist, image: { id: parseInt(id) } }));

		if (request.length > 0) {
			PixivAssistantServer.bulkImageExists(request)
				.then(matchedImages => matchedImages
					.map(match => match.image.id.toString())
					.forEach(matchId => imageMap[matchId].addClass('pa-hidden-thumbnail')));
		} else {
			console.debug('No images found, avoiding server call');
		}
	}

	public openAllInTabs():void {
		pixivBridge.userProfile(this.artistId).then(result => {
			result.body[0].illusts.forEach((illust:any) => {
				let link = pathUtils.generateImageLink(illust.illust_id);
				if (parseInt(illust.illust_page_count) > 1) {
					link = link.replace('medium','manga');
				}
				Deps.openInTab(link);
			})
		});
		return;
	}
	@RegisteredAction({ id: 'pa_button_open_in_tabs', label: 'Open in Tabs', icon: 'new-window' })
	public openTabs():void {
		console.trace('Opening images in tabs');
		
		Deps.getSetting(SettingKeys.pages.works.openTabsImagesOnly).then(imagesOnly => {
			if(imagesOnly) {
				console.log('opening images only.')
				/*
				For each image
					- if a manga or ugoira page, return the viewing url directly
					- if an illustration page, return the original image url
						- use the API to get illustration details to find file path
						- use string manipulation to get the original-size image path from the thumbnail
						- use string manipulation to attach the proper extension (thumbnails are always jpg)
				Once each url is loaded, open them all in new tabs.
					- Use a hacked navigate instead of chrome's tabs.create because pixiv needs referer information
					  to access direct images or it gives 403 Forbidden.
				*/
				Promise.all<string>($('li.image-item a.work').toArray().map((imgEntry:HTMLAnchorElement) => {
					if (imgEntry.classList.contains('multiple') || imgEntry.classList.contains('ugoku-illust')) {
						return Promise.resolve(imgEntry.href);
					} else {
						let url = $(imgEntry).find('img').attr('src');
						let illustId = pathUtils.getImageIdFromSourceUrl(url);
						return Deps.execOnPixiv(
							(pixiv, props) => pixiv.api.illust.detail([props.illustId], {}),
							{
								illustId
							}
						).then((response:any) => {
							let extension = response.body[illustId].illust_ext;
							let extensionWithDot = (extension.charAt(0) === '.') ? extension : `.${extension}`;
							let newSrc = pathUtils.experimentalMaxSizeImageUrl(url).replace(/\.(\w+)$/, extensionWithDot);

							return newSrc;
						});
					}
					
				})).then(newUrls => newUrls.forEach(url => jQUtils.hackedNewTab($, url)));
			} else {
				$('li.image-item a.work').toArray().forEach((image:HTMLAnchorElement) => Deps.openInTab(image.href));
			}
		});
	}

	@RegisteredAction({id: 'pa_button_open_folder', label: 'Open Folder', icon: 'folder-open'})
	public openFolder():void {
		PixivAssistantServer.openFolder(this.artist);
	}

	@ExecuteIfSetting(SettingKeys.global.directToManga)
	public replaceMangaThumbnailLinksToFull(){
		super.replaceMangaThumbnailLinksToFull();
	}

}<commit_msg>Open in tabs when loading images directly no longer uses the old pixiv.api.illust.detail function<commit_after>import * as $ from 'jquery'
import * as pathUtils from 'src/utils/path'
import * as jQUtils from 'src/utils/document'
import {PixivAssistantServer} from 'src/services'
import {RegisteredAction, ExecuteIfSetting, ExecuteOnLoad} from 'src/utils/actionDecorators'
import {GalleryPage} from 'src/pages/gallery'
import SettingKeys from 'src/settingKeys'
import {Model} from 'common/proto'

import {injectUserRelationshipButton} from 'src/injectors/openFolderInjector'
import {injectOpenInTabs} from 'src/injectors/openInTabs'
import {injectCountBadge} from 'src/injectors/countBadge'
import {Container as Deps} from 'src/deps'

import * as pixivBridge from 'src/utils/pixivBridge'

import {prefix} from 'src/utils/log'
let console = prefix('Works Page');

/**
 * The listing of an artist's works. 
 */
export class WorksPage extends GalleryPage {
	public get artistId():number {
		return pathUtils.getArtistId(this.path);
	}
	public get artistName():string {
		return $('h1.user').text();
	}
	public get artist():Model.Artist {
		return { id: this.artistId, name: this.artistName };
	}
	public get allImages():JQuery[] {
		return $('li.image-item').toArray().map(x => $(x));
	}

	protected getTagElements() {
		return [
			'span.tag-badge',
			'div.user-tags li a'
		].map(tag => $(tag)).concat(super.getTagElements());
	}

	protected executeOnEachImage<T>(func:(image:JQuery) => T) {
		$('li.image-item').toArray().forEach(image => func($(image)));
	}

	@ExecuteIfSetting(SettingKeys.global.inject.openToArtistButton)
	public injectOpenFolder(){
		injectUserRelationshipButton(this.artist);
	}
	@ExecuteIfSetting(SettingKeys.pages.works.inject.openInTabs)
	public injectOpenTabs(){
		injectOpenInTabs('Open in Tabs', this.openTabs.bind(this));
		injectOpenInTabs('Open all in Tabs', this.openAllInTabs.bind(this));
	}
	@ExecuteOnLoad
	public injectTotalWorks() {
		pixivBridge.userProfile(this.artistId).then(result => {
			let imageCount = result.body[0].illusts.reduce((acc:number, cur:any) => acc + parseInt(cur.illust_page_count), 0);
			injectCountBadge(`${imageCount} total images`);
		})
	}
	@ExecuteIfSetting(SettingKeys.global.inject.pagingButtons)
	public injectPagingButtons(){
		super.injectPagingButtons();
	}

	@ExecuteIfSetting(SettingKeys.global.fadeDownloadedImages)
	public experimentalFade() {
		let imageMap = this.allImages.reduce((acc: { [id:string] : JQuery }, cur:JQuery) => {
			let imageId = pathUtils.getImageId(cur.find('a.work').attr('href'));
			acc[imageId.toString()] = cur;
			return acc;
		}, <{ [id: string]: JQuery }> {});

		let request = Object.keys(imageMap)
						.map(id => ({ artist: this.artist, image: { id: parseInt(id) } }));

		if (request.length > 0) {
			PixivAssistantServer.bulkImageExists(request)
				.then(matchedImages => matchedImages
					.map(match => match.image.id.toString())
					.forEach(matchId => imageMap[matchId].addClass('pa-hidden-thumbnail')));
		} else {
			console.debug('No images found, avoiding server call');
		}
	}

	public openAllInTabs():void {
		pixivBridge.userProfile(this.artistId).then(result => {
			result.body[0].illusts.forEach((illust:any) => {
				let link = pathUtils.generateImageLink(illust.illust_id);
				if (parseInt(illust.illust_page_count) > 1) {
					link = link.replace('medium','manga');
				}
				Deps.openInTab(link);
			})
		});
		return;
	}
	@RegisteredAction({ id: 'pa_button_open_in_tabs', label: 'Open in Tabs', icon: 'new-window' })
	public openTabs():void {
		console.trace('Opening images in tabs');
		
		Deps.getSetting(SettingKeys.pages.works.openTabsImagesOnly).then(imagesOnly => {
			if(imagesOnly) {
				console.log('opening images only.')
				/*
				For each image
					- if a manga or ugoira page, return the viewing url directly
					- if an illustration page, return the original image url
						- use the API to get illustration details to find file path
						- use string manipulation to get the original-size image path from the thumbnail
						- use string manipulation to attach the proper extension (thumbnails are always jpg)
				Once each url is loaded, open them all in new tabs.
					- Use a hacked navigate instead of chrome's tabs.create because pixiv needs referer information
					  to access direct images or it gives 403 Forbidden.
				*/
				Promise.all<string>($('li.image-item a.work').toArray().map((imgEntry:HTMLAnchorElement) => {
					if (imgEntry.classList.contains('multiple') || imgEntry.classList.contains('ugoku-illust')) {
						return Promise.resolve(imgEntry.href);
					} else {
						let url = $(imgEntry).find('img').attr('src');
						let illustId = pathUtils.getImageIdFromSourceUrl(url);
						return pixivBridge.illustDetail(illustId).then((response: any) => { 
							let extension = response.body[illustId].illust_ext;
							let extensionWithDot = (extension.charAt(0) === '.') ? extension : `.${extension}`;
							let newSrc = pathUtils.experimentalMaxSizeImageUrl(url).replace(/\.(\w+)$/, extensionWithDot);

							return newSrc;
						});
					}
					
				})).then(newUrls => newUrls.forEach(url => jQUtils.hackedNewTab($, url)));
			} else {
				$('li.image-item a.work').toArray().forEach((image:HTMLAnchorElement) => Deps.openInTab(image.href));
			}
		});
	}

	@RegisteredAction({id: 'pa_button_open_folder', label: 'Open Folder', icon: 'folder-open'})
	public openFolder():void {
		PixivAssistantServer.openFolder(this.artist);
	}

	@ExecuteIfSetting(SettingKeys.global.directToManga)
	public replaceMangaThumbnailLinksToFull(){
		super.replaceMangaThumbnailLinksToFull();
	}

}
{'fraction_non_alphanumeric': 0.1445719329214475, 'fraction_numerical': 0.00176522506619594, 'mean_word_length': 4.4034334763948495, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 52, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '764125', 'n_tokens_mistral': 4190, 'n_tokens_neox': 3668, 'n_words': 763}