Methods
(async) registerAdmin(email, password) → {Promise.<{email}>}
Register an admin
Name | Type | Description |
---|---|---|
email | String | email of the admin |
password | String | password of the account |
- Source
the email of the account created
- Type:
- Promise.<{email}>
diff --git a/docs/nijobs-be/1.0.0/AccountService.html b/docs/nijobs-be/1.0.0/AccountService.html deleted file mode 100644 index f1c66c7e..00000000 --- a/docs/nijobs-be/1.0.0/AccountService.html +++ /dev/null @@ -1,3 +0,0 @@ -
Class Services of an account
Register an admin
Name | Type | Description |
---|---|---|
email | String | email of the admin |
password | String | password of the account |
the email of the account created
import { StatusCodes as HTTPStatus } from "http-status-codes";
-import { validationResult } from "express-validator";
-import { ensureArray } from "./validators/validatorUtils.js";
-import ValidationReasons from "./validators/validationReasons.js";
-
-export const buildErrorResponse = (error_code, errors) => ({
- error_code,
- errors: ensureArray(errors),
-});
-
-export class APIError extends Error {
- constructor(status_code, error_code, info, payload) {
- super(info);
- // info: array of errors or error message
- this.errors = Array.isArray(info) ? info : [{ msg: info }];
- this.status_code = status_code;
- this.error_code = error_code;
- this.payload = payload;
- }
-
- toObject() {
- return { ...buildErrorResponse(this.error_code, this.errors), ...this.payload };
- }
-
- sendResponse(res) {
- return res.status(this.status_code).json(this.toObject());
- }
-}
-
-export class UnknownAPIError extends APIError {
- constructor() {
- super(
- HTTPStatus.INTERNAL_SERVER_ERROR,
- ErrorTypes.UNEXPECTED_ERROR,
- ValidationReasons.UNKNOWN
- );
- }
-}
-
-export const ErrorTypes = Object.freeze({
- VALIDATION_ERROR: 1,
- // Possibly nested in the future
- FILE_ERROR: 2,
- FORBIDDEN: 3,
- UNEXPECTED_ERROR: 99,
-});
-
-// Automatically run validators in order to have a standardized error response
-export const useExpressValidators = (validators) => async (req, res, next) => {
- await Promise.all(validators.map((validator) => validator.run(req)));
-
- const errors = validationResult(req);
- if (errors.isEmpty()) {
- return next();
- }
-
- return next(new APIError(HTTPStatus.UNPROCESSABLE_ENTITY, ErrorTypes.VALIDATION_ERROR, errors.array()));
-};
-
-/**
- * Converts error to UnknownAPIError if it's not an instance of APIError
- * @param {*} error
- */
-export const hideInsecureError = (error) => {
- if (error instanceof APIError) return error;
- else return new UnknownAPIError();
-};
-
-export const defaultErrorHandler = (err, req, res, _) => {
- if (!(err instanceof APIError)) console.error("UNEXPECTED ERROR:", err);
- hideInsecureError(err).sendResponse(res);
-};
-
import { StatusCodes as HTTPStatus } from "http-status-codes";
-import lodash from "lodash";
-import { APIError, ErrorTypes, hideInsecureError } from "./errorHandler.js";
-import ValidationReasons from "./validators/validationReasons.js";
-
-export const DEFAULT_ERROR_CODE = ErrorTypes.VALIDATION_ERROR;
-export const DEFAULT_ERROR_MSG = ValidationReasons.UNKNOWN;
-export const DEFAULT_STATUS_CODE = HTTPStatus.BAD_REQUEST;
-export const MAX_FILE_SIZE_MB = 10;
-
-/**
- * Combines array of middleware using OR logic. Only fails if ALL functions fail (either by throwing or calling next(error))
- *
- * Each middleware will receive a different req object, no not rely on it to be shared among them
- *
- * The failed middleware errors will be available in the `or` field of the response.
- * However, only APIErrors will show the actual error message, in order to prevent unwanted errors (such as DB's) to leak here
- *
- * @param {Function[]} middleware: Array of express middleware to be run
- * @param {object} Options:
- * - error_code: error code in case of error (default: ErrorTypes.VALIDATION_ERROR)
- * - msg: the message in case of error (default: ValidationReasons.UNKNOWN)
- * - status_code: The status used in the HTTP Response in case of error (default: BAD_REQUEST (400))
- */
-export const or = (
- [...middlewares],
- {
- error_code = DEFAULT_ERROR_CODE,
- msg = DEFAULT_ERROR_MSG,
- status_code = DEFAULT_STATUS_CODE
- } = {}
-) => async (initialReq, res, next) => {
- let success = false;
- const errors = [];
- for (const middleware of middlewares) {
- const req = lodash.cloneDeep(initialReq);
- try {
- await middleware(req, res, (error) => {
- if (error) errors.push(hideInsecureError(error).toObject());
- else success = true;
- });
- } catch (error) {
- console.error(error);
- errors.push(hideInsecureError(error).toObject());
- }
- if (success) return next();
- }
-
- return next(new APIError(status_code, error_code, msg, { or: errors }));
-};
-
-/**
- * Util to allow running conditionally a middleware
- *
- *
- * @param verify: Function that returns a boolean or a boolean itself indicating if the validator should be ran
- * @param {Function} middleware: Express middleware to be run
- * @param {object} Options:
- * - error_code: error code in case of error (default: ErrorTypes.VALIDATION_ERROR)
- * - msg: the message in case of error (default: ValidationReasons.UNKNOWN)
- * - status_code: The status used in the HTTP Response in case of error (default: BAD_REQUEST (400))
- */
-export const when = (
- verify,
- middleware,
- {
- error_code = DEFAULT_ERROR_CODE,
- msg = DEFAULT_ERROR_MSG,
- status_code = DEFAULT_STATUS_CODE
- } = {}
-) => async (req, res, next) => {
- if ((typeof verify !== "function" && verify) ||
- (typeof verify === "function" && verify(req))) {
- try {
- return await middleware(req, res, next);
- } catch (error) {
- console.error(error);
- if (error instanceof APIError) {
- return next(error);
- }
- return next(new APIError(status_code, error_code, msg, hideInsecureError(error).toObject()));
- }
- }
- return next();
-};
-
-export const storeInLocals = (req, obj) => {
- if (!req.locals) {
- req.locals = {};
- }
-
- req.locals = {
- ...req.locals,
- ...obj,
- };
-};
-
import mongoose from "mongoose";
-import { parseHTML } from "linkedom";
-import ValidationReasons from "./validationReasons.js";
-import Account from "../../../models/Account.js";
-import CompanyService from "../../../services/company.js";
-import CompanyConstants from "../../../models/constants/Company.js";
-
-const { Types } = mongoose;
-
-/**
- * Returns a validator that checks whether all of the elements of an array belong to the provided set of values
- * @param {Array} set
- */
-export const valuesInSet = (set) => (arr) => {
- for (const item of arr) {
- if (!set.includes(item)) {
- throw new Error(ValidationReasons.IN_ARRAY(set));
- }
- }
-
- return true;
-};
-
-/**
- * Throws an error if it already exists a account with the given email.
- * @param {String} email
- */
-export const checkDuplicatedEmail = async (email) => {
- const acc = await Account.findOne({ email }).exec();
- if (acc) {
- throw new Error(ValidationReasons.ALREADY_EXISTS("email"));
- }
-};
-
-/**
- * Sanitizes the input val to return an array. If val is an array, this is a no-op
- * Otherwise wraps val in an array
- *
- * This is especially helpful when you expect an array in a query param,
- * but a one-element array is given, therefore it is parsed as a string instead
- * @param {*} val
- */
-export const ensureArray = (val) => {
- if (Array.isArray(val)) return val;
-
- else return [val];
-};
-
-export const isObjectId = (id) => {
- try {
- Types.ObjectId(id);
- } catch {
- return false;
- }
- return true;
-};
-
-const sortOffersByFieldAscending = (field) => (offer1, offer2) => Date.parse(offer1[field]) - Date.parse(offer2[field]);
-
-/**
- * Checks if the concurrent offers of a given owner have not exceeded the defined limit.
- * If the offers in the timed period exceed the limit, checks how many are concurrent.
- * @param {*} OfferModel Either the default Offer model or an instance's constructor
- * @param {*} owner Owner of the offer
- * @param {*} publishDate Publish date of the
- * @param {*} publishEndDate Date in which the offer will end
- * @param {*} offerId the id of the offer to exclude from the count, if defined
- */
-export const concurrentOffersNotExceeded = (OfferModel) => async (owner, publishDate, publishEndDate, offerId) => {
- // We need to pass the offer model in case we're inside an Offer instance
- const offersInTimePeriod = await (new CompanyService())
- .getOffersInTimePeriod(owner, publishDate, publishEndDate, OfferModel)
- .withoutHidden()
- .withoutArchived()
- .find(offerId ? { _id: { $ne: offerId } } : {});
-
- const offerNumber = offersInTimePeriod.length;
- if (offerNumber < CompanyConstants.offers.max_concurrent) return true;
-
- // This algorithm is explained in https://github.com/NIAEFEUP/nijobs-be/issues/123#issuecomment-782272539
-
- const offersSortedByStart = offersInTimePeriod; // we won't need this array unmodified
- const offersSortedByEnd = [...offersInTimePeriod];
- offersSortedByStart.sort(sortOffersByFieldAscending("publishDate"));
- offersSortedByEnd.sort(sortOffersByFieldAscending("publishEndDate"));
-
- let counter = 0, maxConcurrent = 0;
- let startIndex = 0, endIndex = 0;
- while (startIndex < offerNumber || endIndex < offerNumber) {
- if (startIndex < offerNumber &&
- (endIndex >= offerNumber || offersSortedByStart[startIndex].publishDate <= offersSortedByEnd[endIndex].publishEndDate)) {
-
- counter++;
- startIndex++;
- if (counter > maxConcurrent) maxConcurrent = counter;
- } else {
- counter--;
- endIndex++;
- }
- }
- return maxConcurrent < CompanyConstants.offers.max_concurrent;
-};
-
-export const maxHTMLContentLength = (max) => (text) => {
- const { document } = parseHTML();
- const node = document.createElement("pre");
- node.innerHTML = text;
- if (node.textContent.length > max) {
- throw new Error(ValidationReasons.TOO_LONG(max));
- }
- return true;
-};
-
-export const normalizeDate = (date) => (new Date(Date.parse(date))).toISOString();
-
Register an admin
"},{"title":"CompanyApplicationService#find","link":"find"},{"title":"CompanyApplicationService#findAll","link":"findAll"},{"title":"CompanyService#_sendCompanyNotification","link":"_sendCompanyNotification","description":"E-mails the given company using the provided notification template.
"},{"title":"CompanyService#block","link":"block"},{"title":"CompanyService#changeAttributes","link":"changeAttributes","description":"Changes the attributes of a company
"},{"title":"CompanyService#findAll","link":"findAll"},{"title":"CompanyService#findAndDeleteById","link":"findAndDeleteById","description":"Deletes a company by its ID and returns it
"},{"title":"CompanyService#findById","link":"findById"},{"title":"CompanyService#unblock","link":"unblock"},{"title":"OfferSchema.query.withoutArchived","link":"withoutArchived","description":"Currently active and non-archived Offers
"},{"title":"OfferSchema.statics.filterCurrent","link":"filterCurrent","description":"Currently active Offers (publish date was before Date.now and end date is after Date.now)
"},{"title":"OfferSchema.statics.filterNonHidden","link":"filterNonHidden","description":"Currently active and non-hidden Offers
"},{"title":"OfferService#_buildInitialSearchQuery","link":"_buildInitialSearchQuery","description":"Builds an initial search query. Cannot be used when loading more offers.\nOtherwise, use _buildSearchContinuationQuery().
"},{"title":"OfferService#_buildSearchContinuationQuery","link":"_buildSearchContinuationQuery","description":"Builds a search continuation query. Only use this when loading more offers.\nOtherwise, use _buildInitialSearchQuery().
"},{"title":"OfferService#decodeQueryToken","link":"decodeQueryToken","description":"Decodes a query token, extracting the FTS score and remaining offer's information
"},{"title":"OfferService#encodeQueryToken","link":"encodeQueryToken","description":"Encodes a query token, by taking an id and FTS score if present, and encoding them in safe url base64
"},{"title":"OfferService#get","link":"get","description":"Fetches offers according to specified options\nLearn more about keyset search here: https://github.com/NIAEFEUP/nijobs-be/issues/129
"},{"title":"OfferService#getOffersByCompanyId","link":"getOffersByCompanyId","description":"Gets all the offers from a specific company that are visible to a specific user\nNote: This function will show even unpublished/inactive offers
"},{"title":"OfferService#isVisibleOffer","link":"isVisibleOffer","description":"Checks whether a given offer is visible to a specific userCompanyId.\nUnpublished/inactive offers may still be visible
"},{"title":"checkDuplicatedEmail","link":"checkDuplicatedEmail","description":"Throws an error if it already exists a account with the given email.
"},{"title":"concurrentOffersNotExceeded","link":"concurrentOffersNotExceeded","description":"Checks if the concurrent offers of a given owner have not exceeded the defined limit.\nIf the offers in the timed period exceed the limit, checks how many are concurrent.
"},{"title":"ensureArray","link":"ensureArray","description":"Sanitizes the input val to return an array. If val is an array, this is a no-op\nOtherwise wraps val in an array
\nThis is especially helpful when you expect an array in a query param,\nbut a one-element array is given, therefore it is parsed as a string instead
"},{"title":"hideInsecureError","link":"hideInsecureError","description":"Converts error to UnknownAPIError if it's not an instance of APIError
"},{"title":"or","link":"or","description":"Combines array of middleware using OR logic. Only fails if ALL functions fail (either by throwing or calling next(error))
\nEach middleware will receive a different req object, no not rely on it to be shared among them
\nThe failed middleware errors will be available in the or
field of the response.\nHowever, only APIErrors will show the actual error message, in order to prevent unwanted errors (such as DB's) to leak here
ahgsdjagsdjg
"},{"title":"valuesInSet","link":"valuesInSet","description":"Returns a validator that checks whether all of the elements of an array belong to the provided set of values
"},{"title":"when","link":"when","description":"Util to allow running conditionally a middleware
"}]} \ No newline at end of file diff --git a/docs/nijobs-be/1.0.0/fonts/Inconsolata-Regular.ttf b/docs/nijobs-be/1.0.0/fonts/Inconsolata-Regular.ttf deleted file mode 100644 index 457d262c..00000000 Binary files a/docs/nijobs-be/1.0.0/fonts/Inconsolata-Regular.ttf and /dev/null differ diff --git a/docs/nijobs-be/1.0.0/fonts/OpenSans-Regular.ttf b/docs/nijobs-be/1.0.0/fonts/OpenSans-Regular.ttf deleted file mode 100644 index e21ff5f1..00000000 Binary files a/docs/nijobs-be/1.0.0/fonts/OpenSans-Regular.ttf and /dev/null differ diff --git a/docs/nijobs-be/1.0.0/fonts/WorkSans-Bold.ttf b/docs/nijobs-be/1.0.0/fonts/WorkSans-Bold.ttf deleted file mode 100644 index 0caaf4d4..00000000 Binary files a/docs/nijobs-be/1.0.0/fonts/WorkSans-Bold.ttf and /dev/null differ diff --git a/docs/nijobs-be/1.0.0/global.html b/docs/nijobs-be/1.0.0/global.html deleted file mode 100644 index c7d584e0..00000000 --- a/docs/nijobs-be/1.0.0/global.html +++ /dev/null @@ -1,3 +0,0 @@ -Throws an error if it already exists a account with the given email.
Checks if the concurrent offers of a given owner have not exceeded the defined limit. If the offers in the timed period exceed the limit, checks how many are concurrent.
Sanitizes the input val to return an array. If val is an array, this is a no-op Otherwise wraps val in an array
This is especially helpful when you expect an array in a query param, but a one-element array is given, therefore it is parsed as a string instead
Converts error to UnknownAPIError if it's not an instance of APIError
Combines array of middleware using OR logic. Only fails if ALL functions fail (either by throwing or calling next(error))
Each middleware will receive a different req object, no not rely on it to be shared among them
The failed middleware errors will be available in the or
field of the response. However, only APIErrors will show the actual error message, in order to prevent unwanted errors (such as DB's) to leak here
Returns a validator that checks whether all of the elements of an array belong to the provided set of values
Util to allow running conditionally a middleware
ahgsdjagsdjg
A platform for companies to advertise their job opportunities to the students.
API Documentation is available for the main branch and for dev as well.
Made with ❤️ by NIAEFEUP.
To start developing, please check the documentation on how to configure your local development.
import config from "./config/env.js";
-
-import loaders from "./loaders/index.js";
-import express from "express";
-import https from "https";
-
-const app = express();
-/**
- * ahgsdjagsdjg
- * @returns {Promise<void>}
- */
-const startServer = async () => {
- await loaders({ expressApp: app });
-
- // Running the application in test mode does not start listening because parallel tests would result in EADDRINUSE
- if (process.env.NODE_ENV !== "test") {
-
- let server = app;
- if (process.env.NODE_ENV !== "production") {
- const { promises: fs } = await import("fs");
-
- const [key, cert] = await Promise.all([
- fs.readFile(new URL("../certs/key.pem", import.meta.url).pathname),
- fs.readFile(new URL("../certs/cert.pem", import.meta.url).pathname),
- ]);
- server = https.createServer({ key: key, cert: cert }, app);
- }
-
- server.listen(config.port, (err) => {
- if (err) {
- console.error(err);
- return;
- }
-
- console.info(`Server listening on port ${config.port}`);
- });
- }
-};
-
-startServer();
-
-if (process.env.NODE_ENV === "test") {
- // Necessary for test HTTP requests (End-to-End testing)
- module.exports = app;
-}
-
import mongoose from "mongoose";
-
-import JobTypes from "./constants/JobTypes.js";
-import { FieldTypes, MIN_FIELDS, MAX_FIELDS } from "./constants/FieldTypes.js";
-import { TechnologyTypes, MIN_TECHNOLOGIES, MAX_TECHNOLOGIES } from "./constants/TechnologyTypes.js";
-import PointSchema from "./Point.js";
-import { MONTH_IN_MS, OFFER_MAX_LIFETIME_MONTHS } from "./constants/TimeConstants.js";
-import { noDuplicatesValidator, lengthBetweenValidator, validImageURL, validApplyURL } from "./modelUtils.js";
-import OfferConstants from "./constants/Offer.js";
-import { concurrentOffersNotExceeded, maxHTMLContentLength } from "../api/middleware/validators/validatorUtils.js";
-
-const { Schema, Types } = mongoose;
-
-const OfferSchema = new Schema({
- title: { type: String, maxlength: OfferConstants.title.max_length, minlength: OfferConstants.title.min_length, required: true },
- publishDate: {
- type: Date,
- required: true,
- validate: [
- validatePublishDate,
- "`publishDate` must be earlier than `publishEndDate`",
- ],
- },
-
- publishEndDate: {
- type: Date,
- required: true,
- validate: [
- validateEndDate,
- `\`publishEndDate\` must not differ from \`publishDate\` by more than ${OFFER_MAX_LIFETIME_MONTHS} months`,
- ],
- },
-
- jobMinDuration: {
- type: Number,
- required: true,
- },
- jobMaxDuration: {
- type: Number,
- required: true,
- validate: [
- validateJobMaxDuration,
- "`jobMaxDuration` must be larger than `jobMinDuration`",
- ],
- },
- jobStartDate: { type: Date },
- description: {
- type: String,
- required: true,
- validator: validateDescription
- },
-
- contacts: {
- type: [String],
- required: true,
- validate: [
- (val) => val.length >= 1,
- "There must be at least one contact.",
- ],
- },
-
- isPaid: { type: Boolean },
- vacancies: { type: Number, min: OfferConstants.vacancies.min },
- jobType: { type: String, required: true, enum: JobTypes },
- fields: {
- type: [{ type: String, enum: FieldTypes }],
- required: true,
- validate: (val) => lengthBetweenValidator(val, MIN_FIELDS, MAX_FIELDS) && noDuplicatesValidator(val),
- },
- technologies: {
- type: [{ type: String, enum: TechnologyTypes }],
- required: true,
- validate: (val) => lengthBetweenValidator(val, MIN_TECHNOLOGIES, MAX_TECHNOLOGIES) && noDuplicatesValidator(val),
- },
- hiddenReason: {
- type: String,
- enum: OfferConstants.HiddenOfferReasons,
- },
- adminReason: {
- type: String,
- },
- isHidden: {
- type: Boolean,
- default: false
- },
- isArchived: {
- type: Boolean,
- default: false
- },
- owner: {
- type: Types.ObjectId,
- ref: "Company",
- required: true,
- validator: validateOwnerConcurrentOffers,
- },
- requirements: {
- type: [String],
- required: true,
- validate: [
- (val) => val.length >= 1,
- "There must be at least one requirement"
- ],
- },
- ownerName: { type: String, required: true },
- ownerLogo: { type: String, required: true, validate: (val) => validImageURL(val) },
- location: { type: String, required: true },
- coordinates: { type: PointSchema, required: false },
- applyURL: { type: String, validate: (val) => validApplyURL(val) },
-});
-
-OfferSchema.set("timestamps", true);
-
-OfferSchema.index(
- { title: "text", ownerName: "text", jobType: "text", fields: "text", technologies: "text", location: "text" },
- { name: "Search index", weights: { title: 10, ownerName: 5, jobType: 5, location: 5, fields: 5, technologies: 5 } }
-);
-
-// Checking if the publication date is less than or equal than the end date.
-function validatePublishDate(value) {
- return value <= this.publishEndDate;
-}
-
-function validateEndDate(value) {
- return validatePublishEndDateLimit(this.publishDate, value);
-}
-
-export function validatePublishEndDateLimit(publishDate, publishEndDate) {
-
- // Milisseconds from publish date to end date (Offer is no longer valid)
- const timeDiff = publishEndDate.getTime() - publishDate.getTime();
- const diffInMonths = timeDiff / MONTH_IN_MS;
-
- return diffInMonths <= OFFER_MAX_LIFETIME_MONTHS;
-}
-
-// jobMaxDuration must be larger than jobMinDuration
-function validateJobMaxDuration(value) {
- return value >= this.jobMinDuration;
-}
-
-function validateOwnerConcurrentOffers(value) {
- return concurrentOffersNotExceeded(this.constructor)(value, this.publishDate, this.publishEndDate);
-}
-
-function validateDescription(value) {
- return maxHTMLContentLength(OfferConstants.description.max_length)(value);
-}
-
-/**
- * Currently active Offers (publish date was before Date.now and end date is after Date.now)
- */
-OfferSchema.statics.filterCurrent = () => ({
- publishDate: {
- $lte: new Date(Date.now()),
- },
- publishEndDate: {
- $gt: new Date(Date.now()),
- },
-});
-OfferSchema.query.current = function() {
- return this.where(this.model.filterCurrent());
-};
-
-/**
- * Currently active and non-hidden Offers
- */
-OfferSchema.statics.filterNonHidden = () => ({ isHidden: false });
-OfferSchema.query.withoutHidden = function() {
- return this.where(this.model.filterNonHidden());
-};
-
-/**
- * Currently active and non-archived Offers
- */
-OfferSchema.query.withoutArchived = function() {
- return this.where({ isArchived: false });
-};
-
-const Offer = mongoose.model("Offer", OfferSchema);
-
-// Useful for testing correct field implementation
-// console.log("DBG: ", OfferSchema.path("location"));
-
-export default Offer;
-
element wrapping the first line of selected code - var tdAnchor = selection.anchorNode; - while (tdAnchor.nodeName !== 'TD') { - tdAnchor = tdAnchor.parentNode; - } - - // get the | element wrapping the last line of selected code - var tdFocus = selection.focusNode; - while (tdFocus.nodeName !== 'TD') { - tdFocus = tdFocus.parentNode; - } - - // extract line numbers - var firstLineNumber = parseInt(tdAnchor.dataset.lineNumber); - var lastLineNumber = parseInt(tdFocus.dataset.lineNumber); - - // multi-lines copied case - if (firstLineNumber != lastLineNumber) { - - var firstLineText = tdAnchor.textContent; - var lastLineText = tdFocus.textContent; - - // if the selection was made backward, swap values - if (firstLineNumber > lastLineNumber) { - var tmp = firstLineNumber; - firstLineNumber = lastLineNumber; - lastLineNumber = tmp; - tmp = firstLineText; - firstLineText = lastLineText; - lastLineText = tmp; - } - - // discard not copied characters in first line - while (selectionText.indexOf(firstLineText) !== 0) { - firstLineText = firstLineText.slice(1); - } - - // discard not copied characters in last line - while (selectionText.lastIndexOf(lastLineText) === -1) { - lastLineText = lastLineText.slice(0, -1); - } - - // reconstruct and return the real copied text - var selectedText = firstLineText; - var hljsLnTable = getHljsLnTable(tdAnchor); - for (var i = firstLineNumber + 1 ; i < lastLineNumber ; ++i) { - var codeLineSel = format('.{0}[{1}="{2}"]', [CODE_BLOCK_NAME, DATA_ATTR_NAME, i]); - var codeLineElt = hljsLnTable.querySelector(codeLineSel); - selectedText += '\n' + codeLineElt.textContent; - } - selectedText += '\n' + lastLineText; - return selectedText; - // single copied line case - } else { - return selectionText; - } - } - - // ensure consistent code copy/paste behavior across all browsers - // (see https://github.com/wcoder/highlightjs-line-numbers.js/issues/51) - document.addEventListener('copy', function(e) { - // get current selection - var selection = window.getSelection(); - // override behavior when one wants to copy line of codes - if (isHljsLnCodeDescendant(selection.anchorNode)) { - var selectionText; - // workaround an issue with Microsoft Edge as copied line breaks - // are removed otherwise from the selection string - if (window.navigator.userAgent.indexOf('Edge') !== -1) { - selectionText = edgeGetSelectedCodeLines(selection); - } else { - // other browsers can directly use the selection string - selectionText = selection.toString(); - } - e.clipboardData.setData( - 'text/plain', - selectionText - .replace(/(^\t)/gm, '') - ); - e.preventDefault(); - } - }); - - function addStyles () { - var css = d.createElement('style'); - css.type = 'text/css'; - css.innerHTML = format( - '.{0}{border-collapse:collapse}' + - '.{0} td{padding:0}' + - '.{1}:before{content:attr({2})}', - [ - TABLE_NAME, - NUMBER_LINE_NAME, - DATA_ATTR_NAME - ]); - d.getElementsByTagName('head')[0].appendChild(css); - } - - function initLineNumbersOnLoad (options) { - if (d.readyState === 'interactive' || d.readyState === 'complete') { - documentReady(options); - } else { - w.addEventListener('DOMContentLoaded', function () { - documentReady(options); - }); - } - } - - function documentReady (options) { - try { - var blocks = d.querySelectorAll('code.hljs,code.nohighlight'); - - for (var i in blocks) { - if (blocks.hasOwnProperty(i)) { - if (!isPluginDisabledForBlock(blocks[i])) { - lineNumbersBlock(blocks[i], options); - } - } - } - } catch (e) { - w.console.error('LineNumbers error: ', e); - } - } - - function isPluginDisabledForBlock(element) { - return element.classList.contains('nohljsln'); - } - - function lineNumbersBlock (element, options) { - if (typeof element !== 'object') return; - - async(function () { - element.innerHTML = lineNumbersInternal(element, options); - }); - } - - function lineNumbersValue (value, options) { - if (typeof value !== 'string') return; - - var element = document.createElement('code') - element.innerHTML = value - - return lineNumbersInternal(element, options); - } - - function lineNumbersInternal (element, options) { - - var internalOptions = mapOptions(element, options); - - duplicateMultilineNodes(element); - - return addLineNumbersBlockFor(element.innerHTML, internalOptions); - } - - function addLineNumbersBlockFor (inputHtml, options) { - var lines = getLines(inputHtml); - - // if last line contains only carriage return remove it - if (lines[lines.length-1].trim() === '') { - lines.pop(); - } - - if (lines.length > 1 || options.singleLine) { - var html = ''; - - for (var i = 0, l = lines.length; i < l; i++) { - html += format( - ' |
' + - ' | ' + - '' + - '{6}' + - ' | ' + - '