function renderNotifications() {
	const unreadNotifications = storage.get()
		.filter(shouldNotificationAppearHere);

	if (unreadNotifications.length === 0) {
		return;
	}

	// Don’t simplify selector, it’s for cross-extension compatibility
	let pageList = select('#notification-center .notifications-list');

	if (!pageList) {
		pageList = <div class="notifications-list"></div>;
		select('.blankslate').replaceWith(pageList);
	}

	unreadNotifications.forEach(notification => {
		const group = getNotificationGroup(notification);
		const item = getNotification(notification);

		pageList.prepend(group);
		group
			.querySelector('ul.notifications')
			.prepend(item);
	});

	// Make sure that all the boxes with unread items are at the top
	// This is necessary in the "All notifications" view
	for (const repo of select.all('.boxed-group')) {
		if (select.exists('.unread', repo)) {
			pageList.prepend(repo);
		}
	}
}
Example #2
0
function create(html){
  if (html.charAt(0) == '<') {
    return select(newElement(html));
  }

  return select(document.createElement(html));
}
Example #3
0
function addError (error) {
  var top = select('.top');

  if (!top) {
    setup();
    top = select('.top');
  };

  classes.add(select('.top'), 'failed');
  error.stack = format(templates.stack, error.stack.replace(/\n\s+/g, templates['stack-line']));

  error.code = format(templates.code, {
    'first-line-num': error.source[0].line,
    'first-line-source': escape(error.source[0].code),
    'second-line-num': error.source[1].line,
    'second-line-source': escape(error.source[1].code),
    'third-line-num': error.source[2].line,
    'third-line-source': escape(error.source[2].code)
  });

  if (error.expected != undefined) {
    error.diff = format(templates.diff, JSON.stringify(error.expected, null, " "), JSON.stringify(error.actual, null, " "));
  }

  if (addError.last != error.test) {
    error.title = '<h3>' + error.test +'</h3>';
    addError.last = error.test;
  } else {
    error.title = '';
  }

  dom.add(select('.results .errors'), templates.error, error);
}
Example #4
0
const maxPixelsAvailable = () => {
	// Unfortunately can't cache this value, as it'll change with the browsers zoom level
	const filenameLeftOffset = select('.diff-toolbar-filename').getBoundingClientRect().left;
	const diffStatLeftOffset = select('.diffbar > .diffstat').getBoundingClientRect().left;

	return diffStatLeftOffset - filenameLeftOffset;
};
export default async () => {
	const count = await updateReleasesCount();
	if (count === 0) {
		return;
	}

	const releasesTab = (
		<a href={`/${repoUrl}/releases`} class="reponav-item" data-hotkey="g r">
			{icons.tag()}
			<span> Releases </span>
			{count === undefined ? '' : <span class="Counter">{count}</span>}
		</a>
	);
	select('.reponav-dropdown').before(releasesTab);

	registerShortcut('repos', 'g r', 'Go to Releases');

	if (pageDetect.isReleasesOrTags()) {
		const selected = select('.reponav-item.selected');
		if (selected) {
			selected.classList.remove('js-selected-navigation-item', 'selected');
		}
		releasesTab.classList.add('js-selected-navigation-item', 'selected');
		releasesTab.setAttribute('data-selected-links', 'repo_releases'); // Required for ajaxLoad
	}
};
Example #6
0
function toggleFrame () {
  var results = select('.results');
  var frame = select('.frame');

  classes.toggle(results, 'open');
  classes.toggle(frame, 'open');

  updateFramePosition();
}
Example #7
0
function setupGrep () {
  var el = select('#grep');

  on(select('.grep label'), 'click', function () {
    el.focus();
  });

  bindKey(el, 'enter', function () {
    grep(el.value);
  });
}
Example #8
0
function status (msg) {
  var el = select('.status');

  if (!el) {
    document.body.innerHTML = format(templates.waiting, {
      message: msg
    });
    return;
  }

  el.innerHTML = msg;
  select('.waiting').className = 'waiting center ' + msg;
}
Example #9
0
  return statusElements.some(li => {
    // 首先得是登录用户自己的消息
    const authorUrl = select('.author', li).href
    if (authorUrl !== getLoggedInUserProfilePageUrl()) return false

    // 且消息的时间应该在发送之后,否则认为是之前发送过但是没有加载进 TL 的消息
    const statusTime = (
      Date.parse(select('.time', li).getAttribute('stime')) +
      timeDifference
    )
    if (startTime > statusTime) return false

    return true
  })
  // 把当前登录用户的信息保存到切换列表里,或者更新用户信息
  async function addOrUpdateCurrentUser() {
    const loggedInUserId = getLoggedInUserId()
    const allCookies = Cookies.get()
    const userData = {
      userId: loggedInUserId,
      nickname: select('#user_top h3').textContent,
      avatarUrl: select('#user_top img').src,
      cookies: omitBy(allCookies, (_, key) => (
        key.startsWith('_') || // 跳过键名以「_」开头的 cookie
        key === 'uuid' // 跳过键名为「uuid」的 cookie
      )),
    }

    await updateUserData(loggedInUserId, userData)
  }
Example #11
0
function ifNecessary (child, parent) {
  if (Array.isArray(child)) {
    child = child[0];
  }

  if ( typeof child != 'string') {
    return child;
  }

  if (typeof parent == 'string') {
    parent = select(parent, document);
  }

  return select(child, parent);
}
Example #12
0
function addReadmeButtons() {
	const readmeContainer = select('#readme.readme');
	if (!readmeContainer) {
		return;
	}

	const buttons = <div id="refined-github-readme-buttons"></div>;

	/**
	 * Generate Release button
	 */
	const tags = select.all('.branch-select-menu [data-tab-filter="tags"] .select-menu-item')
		.map(element => [
			element.getAttribute('data-name'),
			element.getAttribute('href')
		]);
	const releases = new Map(tags);
	const [latestRelease] = toSemver([...releases.keys()], {clean: false});
	if (latestRelease) {
		buttons.appendChild(
			<a
				class="tooltipped tooltipped-nw"
				href={`${releases.get(latestRelease)}#readme`}
				aria-label={`View this file at the latest version (${latestRelease})`}>
				{icons.tag}
			</a>
		);
	}

	/**
	 * Generate Edit button
	 */
	if (select('.branch-select-menu i').textContent === 'Branch:') {
		const readmeName = select('#readme > h3').textContent.trim();
		const path = select('.breadcrumb').textContent.trim().split('/').slice(1).join('/');
		const currentBranch = select('.branch-select-menu .select-menu-item.selected').textContent.trim();
		buttons.appendChild(
			<a
				href={`/${repoUrl}/edit/${currentBranch}/${path}${readmeName}`}
				class="tooltipped tooltipped-nw"
				aria-label="Edit this file">
				{icons.edit}
			</a>
		);
	}

	readmeContainer.appendChild(buttons);
}
function addCustomAllReadBtn() {
	const hasMarkAllReadBtnExists = select.exists('#notification-center a[href="#mark_as_read_confirm_box"]');
	if (hasMarkAllReadBtnExists || storage.get().length === 0) {
		return;
	}

	select('.tabnav .float-right').append(
		<a href="#mark_as_read_confirm_box" class="btn btn-sm" rel="facebox">Mark all as read</a>
	);
	document.body.append(
		<div id="mark_as_read_confirm_box" style={{display: 'none'}}>
			<h2 class="facebox-header" data-facebox-id="facebox-header">Are you sure?</h2>

			<p data-facebox-id="facebox-description">Are you sure you want to mark all unread notifications as read?</p>

			<div class="full-button">
				<button id="clear-local-notification" class="btn btn-block">Mark all notifications as read</button>
			</div>
		</div>
	);

	delegate('#clear-local-notification', 'click', () => {
		storage.set([]);
		location.reload();
	});
}
Example #14
0
function appendReleasesCount(count) {
	if (!count) {
		return;
	}

	select('.reponav-releases').append(<span class="Counter">{count}</span>);
}
Example #15
0
function addProjectNewLink() {
	if (select.exists('#projects-feature:checked') && !select.exists('#refined-github-project-new-link')) {
		select(`#projects-feature ~ p.note`).insertAdjacentElement('afterEnd',
			<a href={`/${repoUrl}/projects/new`} class="btn btn-sm" id="refined-github-project-new-link">Add a project</a>
		);
	}
}
Example #16
0
function markTest (test) {
  var overview = select('.overview');
  if (overview) return;

  var list = select('.waiting .list');

  if (!list) {
    dom.add(select('.waiting h1'), templates.overview);
    list = select('.waiting .list');
  }

  dom.add(list, templates.test, {
    icon: '',
    name: test.name
  });
}
	gitHubInjection(async () => {
		destroy();

		if (pageDetect.isNotifications()) {
			renderNotifications();
			addCustomAllReadBtn();
			updateLocalNotificationsCount();
			updateLocalParticipatingCount();
			listeners.push(
				delegate('.btn-link.delete-note', 'click', markNotificationRead),
				delegate('.js-mark-all-read', 'click', markAllNotificationsRead),
				delegate('.js-delete-notification button', 'click', updateUnreadIndicator),
				delegate('form[action="/notifications/mark"] button', 'click', () => {
					storage.set([]);
				})
			);
		} else if (pageDetect.isPR() || pageDetect.isIssue()) {
			markRead(location.href);

			// The sidebar changes when new comments are added or the issue status changes
			observeEl('.discussion-sidebar', addMarkUnreadButton);
		} else if (pageDetect.isIssueList()) {
			await domLoaded;
			for (const discussion of storage.get()) {
				const url = new URL(discussion.url);
				const listItem = select(`.read [href='${url.pathname}']`);
				if (listItem) {
					listItem.closest('.read').classList.replace('read', 'unread');
				}
			}
		}

		updateUnreadIndicator();
	});
export default function () {
	registerShortcut('releases', 'c', 'Create a new release');
	const createReleaseButton = select('a[href$="/releases/new"]:not([data-hotkey])');
	if (createReleaseButton) {
		createReleaseButton.setAttribute('data-hotkey', 'c');
	}
}
function getNotification(notification) {
	const {
		participants,
		title,
		state,
		type,
		dateTitle,
		date,
		url
	} = notification;

	const existing = select(`a.js-notification-target[href^="${stripHash(url)}"]`);
	if (existing) {
		const item = existing.closest('.js-notification');
		item.classList.replace('read', 'unread');
		return item;
	}

	const usernames = participants
		.map(participant => participant.username)
		.join(' and ')
		.replace(/ and (.+) and/, ', $1, and'); // 3 people only: A, B, and C

	const avatars = participants.map(participant =>
		<a href={`/${participant.username}`} class="avatar">
			<img alt={`@${participant.username}`} height="20" src={participant.avatar} width="20"/>
		</a>
	);

	return (
		<li class={`list-group-item js-notification js-navigation-item unread ${type}-notification rgh-unread`}>
			<span class="list-group-item-name css-truncate">
				<span class={`type-icon type-icon-state-${state}`}>
					{stateIcons[type][state]()}
				</span>
				<a class="css-truncate-target js-notification-target js-navigation-open list-group-item-link" href={url}>
					{title}
				</a>
			</span>
			<ul class="notification-actions">
				<li class="delete">
					<button class="btn-link delete-note">
						{icons.check()}
					</button>
				</li>
				<li class="mute tooltipped tooltipped-w" aria-label={`${type === 'issue' ? 'Issue' : 'PR'} manually marked as unread`}>
					{icons.info()}
				</li>
				<li class="age">
					<relative-time datetime={date} title={dateTitle}/>
				</li>
				<div class="AvatarStack AvatarStack--three-plus AvatarStack--right clearfix d-inline-block" style={{marginTop: 1}}>
					<div class="AvatarStack-body tooltipped tooltipped-sw tooltipped-align-right-1" aria-label={usernames}>
						{avatars}
					</div>
				</div>
			</ul>
		</li>
	);
}
Example #20
0
	observeEl(select('#partial-discussion-header').parentNode, () => {
		const title = select('.js-issue-title:not(.refined-linkified-title)');
		if (title) {
			title.classList.add('refined-linkified-title');
			editTextNodes(linkifyIssues, title);
		}
	});
Example #21
0
File: test.js Project: azer/hide
it('hides the given element', function(){
  var div = select('div');
  hide(div);
  expect(div.style.position).to.equal('absolute');
  expect(div.style.top).to.equal('-9999px');
  expect(div.style.left).to.equal('-9999px');
});
Example #22
0
 function byId(root, id, el) {
   // if doc, query on it, else query the parent doc or if a detached fragment rewrite the query and run on the fragment
   return root[nodeType] === 9 ? root.getElementById(id) :
     root.ownerDocument &&
       (((el = root.ownerDocument.getElementById(id)) && isAncestor(el, root) && el) ||
         (!isAncestor(root, root.ownerDocument) && select('[id="' + id + '"]', root)[0]))
 }
Example #23
0
  function createButton() {
    const existingButton = select('#pagination-totop')
    const container = (
      (select('#stream') && select('#stream').parentElement) ||
      select('.inner-content')
    )
    toTopButton = (
      <a id="pagination-totop" onClick={clickHandler}>返回顶部</a>
    )

    if (existingButton) existingButton.remove()
    container.appendChild(toTopButton)
    hideButton()

    return toTopButton
  }
Example #24
0
function setup () {
  dom.remove(select('.waiting'));

  var template = format(templates['layout'], templates);
  dom.add(document.body, template, {
    grep: grep() || '',
    conn: ''
  });

  on(select('.frame-button'), 'click', toggleFrame);
  on(select('.run-again'), 'click', run);

  updateConn();

  setupGrep();
  updateFramePosition();
}
function markUnread() {
	const participants = select.all('.participant-avatar').slice(0, 3).map(el => ({
		username: el.getAttribute('aria-label'),
		avatar: el.querySelector('img').src
	}));

	const {ownerName, repoName} = pageDetect.getOwnerAndRepo();
	const repository = `${ownerName}/${repoName}`;
	const title = select('.js-issue-title').textContent.trim();
	const type = pageDetect.isPR() ? 'pull-request' : 'issue';
	const url = stripHash(location.href);

	const stateLabel = select('.gh-header-meta .State');
	let state;

	if (stateLabel.classList.contains('State--green')) {
		state = 'open';
	} else if (stateLabel.classList.contains('State--purple')) {
		state = 'merged';
	} else if (stateLabel.classList.contains('State--red')) {
		state = 'closed';
	}

	const lastCommentTime = select.all('.timeline-comment-header relative-time').pop();
	const dateTitle = lastCommentTime.title;
	const date = lastCommentTime.getAttribute('datetime');

	const unreadNotifications = storage.get();

	unreadNotifications.push({
		participants,
		repository,
		title,
		state,
		type,
		dateTitle,
		date,
		url
	});

	storage.set(unreadNotifications);
	updateUnreadIndicator();

	this.setAttribute('disabled', 'disabled');
	this.textContent = 'Marked as unread';
}
export default function () {
	const repoUrl = pageDetect.getRepoURL();
	const readmeContainer = select('.repository-content > #readme');

	if (readmeContainer && select('.branch-select-menu i').textContent === 'Branch:') {
		const readmeName = select('#readme > h3').textContent.trim();
		const path = select('.breadcrumb').textContent.trim().split('/').slice(1).join('/');
		const currentBranch = select('.branch-select-menu .select-menu-item.selected').textContent.trim();
		readmeContainer.append(
			<div id="refined-github-readme-buttons">
				<a href={`/${repoUrl}/edit/${currentBranch}/${path}${readmeName}`}>
					{icons.edit()}
				</a>
			</div>
		);
	}
}
	delegate('.js-comment-field', 'keydown', event => {
		const field = event.target;

		// Don't do anything if the suggester box is active
		if (select.exists('.suggester:not([hidden])', field.form)) {
			return;
		}

		if (event.key === 'Tab' && !event.shiftKey) {
			indentTextarea(field);
			event.preventDefault();
		} else if (event.key === 'Enter' && event.shiftKey) {
			const singleCommentButton = select('.review-simple-reply-button', field.form);

			if (singleCommentButton) {
				singleCommentButton.click();
				event.preventDefault();
			}
		} else if (event.key === 'Escape') {
			// Cancel buttons have different classes for inline comments and editable comments
			const cancelButton = select(`
				.js-hide-inline-comment-form,
				.js-comment-cancel-button
			`, field.form);

			// Cancel if there is a button, else blur the field
			if (cancelButton) {
				cancelButton.click();
			} else {
				blurAccessibly(field);
			}
			event.stopImmediatePropagation();
			event.preventDefault();
		} else if (event.key === 'ArrowUp' && field.id === 'new_comment_field' && field.value === '') {
			const lastOwnComment = select.all('.js-comment.current-user').pop();

			if (lastOwnComment) {
				select('.js-comment-edit-button', lastOwnComment).click();

				// Move caret to end of field
				requestAnimationFrame(() => {
					select('.js-comment-field', lastOwnComment).selectionStart = Number.MAX_SAFE_INTEGER;
				});
			}
		}
	});
export default function () {
	if (pageDetect.isRepoRoot()) {
		const meta = select('.repository-meta');
		if (select.exists('em', meta) && !select.exists('.js-edit-repo-meta-button')) {
			meta.style.display = 'none';
		}
	}
}
export default () => {
	if (pageDetect.isRepoTree()) {
		const uploadFilesButton = select(`.file-navigation a[href^="/${repoUrl}/upload"]`);
		if (uploadFilesButton) {
			uploadFilesButton.remove();
		}
	}
};
Example #30
0
    allStatuses.forEach((li, index) => {
      const isInRange = littleIndex <= index && index <= bigIndex
      const isPreviousSelected = previousSelected.includes(li)
      const checkbox = select(`.${CLASSNAME_CHECKBOX}`, li)

      li.classList.toggle(CLASSNAME_MULTIPLE_SELECTION_IN_RANGE, isInRange)
      checkbox.checked = isInRange || isPreviousSelected
    })