lt;%= product.price.toFixed(2) %> </li> <% }); %> </ul> </body> </html> */ // This is similar to view rendering in MVC frameworks: /* public class ProductController { private ProductRepository repository; public ModelAndView showProducts(HttpSession session) { List<Product> products = repository.findAll(); ModelAndView modelAndView = new ModelAndView("products"); modelAndView.addAttribute("title", "Our Products"); modelAndView.addAttribute("products", products); modelAndView.addAttribute("userLoggedIn", session.getAttribute("user") != null); return modelAndView; } } */ ``` ### ORM (Object-Relational Mapping) - Data Abstraction Layer **CS Concept:** Data abstraction and persistence mapping. **Web Development Example (using Sequelize with Node.js):** ```javascript // Defining a model (similar to a class definition) const { Sequelize, DataTypes } = require('sequelize'); const sequelize = new Sequelize('database', 'username', 'password', { host: 'localhost', dialect: 'mysql' }); // User model definition const User = sequelize.define('User', { // Model attributes (similar to class properties) id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true }, username: { type: DataTypes.STRING, allowNull: false, unique: true }, email: { type: DataTypes.STRING, allowNull: false, validate: { isEmail: true } }, role: { type: DataTypes.ENUM('user', 'admin'), defaultValue: 'user' }, lastLogin: { type: DataTypes.DATE } }); // Post model definition const Post = sequelize.define('Post', { title: { type: DataTypes.STRING, allowNull: false }, content: { type: DataTypes.TEXT }, published: { type: DataTypes.BOOLEAN, defaultValue: false } }); // Define relationships (similar to database foreign keys) User.hasMany(Post); Post.belongsTo(User); // Using the models (CRUD operations) async function createUser() { try { // Create a new user (similar to instantiating a class) const user = await User.create({ username: 'johndoe', email: '[email protected]', role: 'user' }); console.log('User created:', user.toJSON()); return user; } catch (error) { console.error('Error creating user:', error); } } async function findUsers() { try { // Query users (similar to database queries) const users = await User.findAll({ where: { role: 'admin' }, include: [Post] // Include related posts (similar to JOIN) }); console.log(`Found ${users.length} admin users`); return users; } catch (error) { console.error('Error finding users:', error); } } // This is similar to ORM concepts in CS: /* @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(unique = true, nullable = false) private String username; @Column(nullable = false) private String email; @Enumerated(EnumType.STRING) private Role role = Role.USER; @Column(name = "last_login") private Date lastLogin; @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) private List<Post> posts; // Getters and setters... } */ ``` ### Microservices - Distributed Systems Architecture **CS Concept:** Service-oriented architecture and distributed systems. **Web Development Example:** ```javascript // User Service (auth-service.js) const express = require('express'); const app = express(); app.use(express.json()); // In-memory user database (in real app, this would be a proper database) const users = [ { id: 1, username: 'alice', passwordHash: 'hashed_password_1', role: 'user' }, { id: 2, username: 'bob', passwordHash: 'hashed_password_2', role: 'admin' } ]; // Authentication endpoint app.post('/auth/login', (req, res) => { const { username, password } = req.body; // Find user (simplified authentication) const user = users.find(u => u.username === username); if (!user || !validatePassword(password, user.passwordHash)) { return res.status(401).json({ error: 'Invalid credentials' }); } // Generate JWT token (simplified) const token = generateToken(user); res.json({ token, userId: user.id, role: user.role }); }); app.listen(3001, () => { console.log('Auth service running on port 3001'); }); // Product Service (product-service.js) const express = require('express'); const app = express(); app.use(express.json()); // In-memory product database const products = [ { id: 1, name: 'Laptop', price: 999.99, stock: 50 }, { id: 2, name: 'Smartphone', price: 699.99, stock: 100 }, { id: 3, name: 'Tablet', price: 349.99, stock: 75 } ]; // Authentication middleware (verifies token from auth service) function authenticate(req, res, next) { const token = req.headers.authorization?.split(' ')[1]; if (!token) { return res.status(401).json({ error: 'Authentication required' }); } try { // Verify token (simplified) const decoded = verifyToken(token); req.user = decoded; next(); } catch (error) { res.status(401).json({ error: 'Invalid token' }); } } // Get all products app.get('/products', (req, res) => { res.json(products); }); // Get product by ID app.get('/products/:id', (req, res) => { const product = products.find(p => p.id === parseInt(req.params.id)); if (!product) { return res.status(404).json({ error: 'Product not found' }); } res.json(product); }); // Create product (requires authentication) app.post('/products', authenticate, (req, res) => { // Check if user is admin if (req.user.role !== 'admin') { return res.status(403).json({ error: 'Permission denied' }); } const { name, price, stock } = req.body; const newProduct = { id: products.length + 1, name, price, stock }; products.push(newProduct); res.status(201).json(newProduct); }); app.listen(3002, () => { console.log('Product service running on port 3002'); }); // API Gateway (gateway.js) const express = require('express'); const { createProxyMiddleware } = require('http-proxy-middleware'); const app = express(); // Route to auth service app.use('/auth', createProxyMiddleware({ target: 'http://localhost:3001', pathRewrite: {'^/auth': '/auth'} })); // Route to product service app.use('/products', createProxyMiddleware({ target: 'http://localhost:3002', pathRewrite: {'^/products': '/products'} })); app.listen(3000, () => { console.log('API Gateway running on port 3000'); }); // This is similar to distributed systems in CS: /* interface AuthService { AuthResponse authenticate(String username, String password); } interface ProductService { List<Product> getAllProducts(); Product getProductById(Long id); Product createProduct(Product product); } class ApiGateway { private final AuthService authService; private final ProductService productService; public Response handleRequest(Request request) { if (request.getPath().startsWith("/auth")) { return routeToAuthService(request); } else if (request.getPath().startsWith("/products")) { return routeToProductService(request); } return new Response(404, "Not Found"); } } */ ``` ## CSS Concepts ### CSS Box Model - Layout Algorithm **CS Concept:** Layout calculation and rendering model. **Web Development Example:** ```css /* CSS Box Model demonstration */ .box { /* Content dimensions */ width: 200px; height: 100px; /* Padding (inside the border) */ padding-top: 10px; padding-right: 20px; padding-bottom: 10px; padding-left: 20px; /* Border */ border-width: 5px; border-style: solid; border-color: #333; /* Margin (outside the border) */ margin: 15px; /* Background (fills content and padding areas) */ background-color: #f0f0f0; } /* Total width calculation: 200px (content) + 20px (left padding) + 20px (right padding) + 5px (left border) + 5px (right border) = 250px Total height calculation: 100px (content) + 10px (top padding) + 10px (bottom padding) + 5px (top border) + 5px (bottom border) = 130px Space occupied including margin: 250px (total width) + 15px (left margin) + 15px (right margin) = 280px 130px (total height) + 15px (top margin) + 15px (bottom margin) = 160px */ /* Alternative box sizing (more intuitive) */ .border-box { /* Make width/height include padding and border */ box-sizing: border-box; width: 200px; /* Total width including padding and border will be 200px */ height: 100px; /* Total height including padding and border will be 100px */ padding: 10px; border: 5px solid #333; margin: 15px; } /* This is similar to layout algorithms in graphics programming: class BoxModel { int contentWidth; int contentHeight; Insets padding; Insets border; Insets margin; // Calculate total width int getTotalWidth() { return contentWidth + padding.left + padding.right + border.left + border.right; } // Calculate total height int getTotalHeight() { return contentHeight + padding.top + padding.bottom + border.top + border.bottom; } } */ ``` ### Flexbox - Layout Algorithm **CS Concept:** Constraint-based layout system. **Web Development Example:** ```css /* Flexbox Container */ .container { display: flex; /* Main axis direction */ flex-direction: row; /* horizontal (default) */ /* flex-direction: column; */ /* vertical */ /* How items wrap when they don't fit */ flex-wrap: wrap; /* Alignment along main axis */ justify-content: space-between; /* Options: flex-start, flex-end, center, space-around, space-evenly */ /* Alignment along cross axis */ align-items: center; /* Options: flex-start, flex-end, stretch, baseline */ /* Gap between items */ gap: 10px; /* Container styling */ border: 2px solid #333; padding: 10px; } /* Flex Items */ .item { background-color: #f0f0f0; padding: 20px; border: 1px solid #999; text-align: center; /* Flex grow (proportion of extra space to take) */ flex-grow: 1; /* Flex shrink (how much item can shrink) */ flex-shrink: 1; /* Flex basis (initial main size) */ flex-basis: 100px; /* Shorthand for grow, shrink, basis */ /* flex: 1 1 100px; */ } /* Make the second item grow twice as much */ .item:nth-child(2) { flex-grow: 2; background-color: #e0e0ff; } /* Make the third item not grow */ .item:nth-child(3) { flex-grow: 0; flex-shrink: 0; /* Also prevents shrinking */ background-color: #ffe0e0; } /* This is similar to constraint-based layout in CS: class FlexLayout { enum Direction { ROW, COLUMN } enum Alignment { START, END, CENTER, SPACE_BETWEEN, SPACE_AROUND } Direction direction; Alignment mainAxisAlignment; Alignment crossAxisAlignment; boolean wrap; List<FlexItem> items; void calculateLayout(int availableWidth, int availableHeight) { // Distribute space among items based on flex properties int remainingSpace = (direction == Direction.ROW) ? availableWidth : availableHeight; // Subtract fixed-size items for (FlexItem item : items) { if (item.flexGrow == 0) { remainingSpace -= item.size; } } // Calculate total flex units int totalFlexUnits = 0; for (FlexItem item : items) { totalFlexUnits += item.flexGrow; } // Distribute remaining space for (FlexItem item : items) { if (item.flexGrow > 0) { item.allocatedSize = item.baseSize + (remainingSpace * item.flexGrow / totalFlexUnits); } } } } */ ``` ## Advanced JavaScript ### Promises and Async Programming - Concurrency Model **CS Concept:** Asynchronous programming and concurrency. **Web Development Example:** ```javascript // Promise-based asynchronous function function fetchUserData(userId) { return new Promise((resolve, reject) => { // Simulate API call with setTimeout setTimeout(() => { if (userId > 0) { const userData = { id: userId, name: `User ${userId}`, email: `user${userId}@example.com` }; resolve(userData); // Success case } else { reject(new Error('Invalid user ID')); // Error case } }, 1000); // 1 second delay }); } // Using promises with then/catch function displayUserWithPromises(userId) { console.log(`Fetching data for user ${userId}...`); fetchUserData(userId) .then(user => { console.log('User data received:', user); return fetchUserPosts(user.id); // Return another promise }) .then(posts => { console.log('User posts received:', posts); }) .catch(error => { console.error('Error:', error.message); }) .finally(() => { console.log('Operation completed'); }); console.log('Request initiated'); // This runs before the promises resolve } // Using async/await (cleaner syntax for promises) async function displayUserWithAsync(userId) { console.log(`Fetching data for user ${userId}...`); try { console.log('Request initiated'); // await pauses execution until promise resolves const user = await fetchUserData(userId); console.log('User data received:', user); const posts = await fetchUserPosts(user.id); console.log('User posts received:', posts); } catch (error) { console.error('Error:', error.message); } finally { console.log('Operation completed'); } } // Promise combinators async function fetchMultipleUsers(userIds) { try { // Promise.all - wait for all promises to resolve (parallel execution) const users = await Promise.all( userIds.map(id => fetchUserData(id)) ); console.log('All users:', users); // Promise.race - resolve/reject as soon as the first promise resolves/rejects const fastestResponse = await Promise.race([ fetchUserData(1).then(() => 'User API won'), fetchWeatherData().then(() => 'Weather API won') ]); console.log('Fastest response:', fastestResponse); // Promise.allSettled - wait for all promises to settle (resolve or reject) const results = await Promise.allSettled( userIds.map(id => fetchUserData(id)) ); // Handle mixed results results.forEach((result, index) => { if (result.status === 'fulfilled') { console.log(`User ${userIds[index]} data:`, result.value); } else { console.log(`User ${userIds[index]} error:`, result.reason.message); } }); } catch (error) { console.error('Error:', error.message); } } // This is similar to concurrent programming in CS: /* class AsyncOperation<T> { private CompletableFuture<T> future; public AsyncOperation<T> then(Function<T, U> callback) { return new AsyncOperation<>(future.thenApply(callback)); } public AsyncOperation<T> catch(Function<Exception, T> handler) { return new AsyncOperation<>(future.exceptionally(handler)); } public static <T> AsyncOperation<List<T>> all(List<AsyncOperation<T>> operations) { List<CompletableFuture<T>> futures = operations.stream() .map(op -> op.future) .collect(Collectors.toList()); CompletableFuture<List<T>> allFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) .thenApply(v -> futures.stream() .map(CompletableFuture::join) .collect(Collectors.toList())); return new AsyncOperation<>(allFuture); } } */ ``` ### Progressive Web Apps (PWAs) - Hybrid Application Model **CS Concept:** Offline-capable web applications with native features. #chromeExtension **Web Development Example:** ```javascript // manifest.json - Application metadata (similar to app descriptor) /* { "name": "Weather App", "short_name": "Weather", "description": "Check weather forecasts", "start_url": "/", "display": "standalone", "background_color": "#ffffff", "theme_color": "#4285f4", "icons": [ { "src": "/icons/icon-72x72.png", "sizes": "72x72", "type": "image/png" }, { "src": "/icons/icon-192x192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/icons/icon-512x512.png", "sizes": "512x512", "type": "image/png" } ] } */ // service-worker.js - Offline capabilities const CACHE_NAME = 'weather-app-v1'; const URLS_TO_CACHE = [ '/', '/index.html', '/styles/main.css', '/scripts/app.js', '/images/logo.png', '/offline.html' ]; // Installation event - cache assets self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => cache.addAll(URLS_TO_CACHE)) ); }); // Fetch event - serve from cache or network self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request) .then(response => { // Cache hit - return response if (response) { return response; } // Clone the request const fetchRequest = event.request.clone(); return fetch(fetchRequest) .then(response => { // Check if valid response if (!response || response.status !== 200 || response.type !== 'basic') { return response; } // Clone the response const responseToCache = response.clone(); caches.open(CACHE_NAME) .then(cache => { cache.put(event.request, responseToCache); }); return response; }) .catch(() => { // If network fails, serve offline page for navigate requests if (event.request.mode === 'navigate') { return caches.match('/offline.html'); } }); }) ); }); // App.js - Using service worker and other PWA features if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/service-worker.js') .then(registration => { console.log('ServiceWorker registered with scope:', registration.scope); }) .catch(error => { console.error('ServiceWorker registration failed:', error); }); }); } // Check if app is installed or can be installed let deferredPrompt; window.addEventListener('beforeinstallprompt', event => { // Prevent Chrome from automatically showing the prompt event.preventDefault(); // Stash the event so it can be triggered later deferredPrompt = event; // Show the install button document.getElementById('install-button').style.display = 'block'; }); // Handle install button click document.getElementById('install-button').addEventListener('click', () => { // Hide the install button document.getElementById('install-button').style.display = 'none'; // Show the prompt deferredPrompt.prompt(); // Wait for the user to respond to the prompt deferredPrompt.userChoice.then(choiceResult => { if (choiceResult.outcome === 'accepted') { console.log('User accepted the A2HS prompt'); } else { console.log('User dismissed the A2HS prompt'); } // Clear the saved prompt deferredPrompt = null; }); }); // Using the Cache API directly (for dynamic caching) function cacheApiResponse(url, response) { return caches.open('api-cache') .then(cache => { // Store response in cache with timestamp const clonedResponse = response.clone(); return cache.put(url, clonedResponse); }); } // This is similar to hybrid application development in CS: /* class HybridApp { private CacheManager cacheManager; private NetworkManager networkManager; private LocalStorage localStorage; public Response handleRequest(Request request) { // Try cache first Response cachedResponse = cacheManager.get(request.getUrl()); if (cachedResponse != null) { return cachedResponse; } // Try network try { Response networkResponse = networkManager.fetch(request); cacheManager.put(request.getUrl(), networkResponse.clone()); return networkResponse; } catch (NetworkException e) { // Fall back to offline content return cacheManager.getOfflineResponse(); } } } */ ``` ### OAuth 2.0 - Delegated Authorization Protocol **CS Concept:** Authentication protocols, delegation, and token-based security. **Web Development Example:** ```javascript // OAuth 2.0 client implementation (simplified) class OAuth2Client { constructor(config) { this.clientId = config.clientId; this.clientSecret = config.clientSecret; this.redirectUri = config.redirectUri; this.authUrl = config.authUrl; this.tokenUrl = config.tokenUrl; this.scope = config.scope || ''; } // Generate authorization URL (redirect user to this URL) getAuthorizationUrl() { const params = new URLSearchParams({ client_id: this.clientId, redirect_uri: this.redirectUri, response_type: 'code', scope: this.scope, state: this.generateRandomState() // Prevent CSRF attacks }); return `${this.authUrl}?${params.toString()}`; } // Exchange authorization code for access token async getAccessToken(authorizationCode) { const params = new URLSearchParams({ client_id: this.clientId, client_secret: this.clientSecret, redirect_uri: this.redirectUri, grant_type: 'authorization_code', code: authorizationCode }); const response = await fetch(this.tokenUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: params.toString() }); if (!response.ok) { throw new Error('Failed to get access token'); } return response.json(); // Returns { access_token, refresh_token, expires_in, ... } } // Refresh an expired access token async refreshToken(refreshToken) { const params = new URLSearchParams({ client_id: this.clientId, client_secret: this.clientSecret, grant_type: 'refresh_token', refresh_token: refreshToken }); const response = await fetch(this.tokenUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: params.toString() }); if (!response.ok) { throw new Error('Failed to refresh token'); } return response.json(); // Returns new { access_token, refresh_token, expires_in, ... } } // Generate random state for CSRF protection generateRandomState() { return Math.random().toString(36).substring(2, 15); } } // Usage example - OAuth 2.0 flow in a web application // 1. Initialize the client const oauthClient = new OAuth2Client({ clientId: 'your-client-id', clientSecret: 'your-client-secret', redirectUri: 'https://your-app.com/callback', authUrl: 'https://auth-provider.com/authorize', tokenUrl: 'https://auth-provider.com/token', scope: 'profile email' }); // 2. Redirect user to authorization URL function redirectToLogin() { const authUrl = oauthClient.getAuthorizationUrl(); window.location.href = authUrl; } // 3. Handle callback (in your redirect_uri endpoint) async function handleOAuthCallback() { // Extract code from URL query parameters const urlParams = new URLSearchParams(window.location.search); const code = urlParams.get('code'); const state = urlParams.get('state'); // Verify state (CSRF protection) if (state !== sessionStorage.getItem('oauth_state')) { throw new Error('Invalid state parameter'); } // Exchange code for tokens try { const tokenData = await oauthClient.getAccessToken(code); // Store tokens securely securelyStoreTokens(tokenData); // Redirect to protected area window.location.href = '/dashboard'; } catch (error) { console.error('Authentication error:', error); window.location.href = '/login?error=authentication_failed'; } } // This is similar to authentication protocols in CS: /* public class AuthProtocol { private final String clientId; private final String clientSecret; private final String redirectUri; public String generateAuthorizationRequest() { String state = generateRandomState(); saveStateForVerification(state); return buildAuthorizationUrl(clientId, redirectUri, state); } public TokenResponse exchangeAuthorizationCode(String code) { // Validate and exchange code for token return httpClient.post(tokenUrl) .withFormData("grant_type", "authorization_code") .withFormData("client_id", clientId) .withFormData("client_secret", clientSecret) .withFormData("code", code) .withFormData("redirect_uri", redirectUri) .execute() .parseAs(TokenResponse.class); } } */ ``` ## Data Structures in Web Development ### Trie for Autocomplete - Prefix Tree Implementation **CS Concept:** Trie data structure for efficient prefix searching. **Web Development Example:** ```javascript // Trie data structure for autocomplete functionality class TrieNode { constructor() { this.children = new Map(); // Map each character to a child node this.isEndOfWord = false; // Flag to indicate complete words this.suggestions = []; // Suggestions at this node (for optimization) } } class AutocompleteTrie { constructor(maxSuggestions = 5) { this.root = new TrieNode(); this.maxSuggestions = maxSuggestions; } // Insert a word into the trie insert(word) { if (!word || typeof word !== 'string') return; let node = this.root; // Travel the trie, creating nodes as needed for (const char of word.toLowerCase()) { if (!node.children.has(char)) { node.children.set(char, new TrieNode()); } node = node.children.get(char); // Add the word to suggestions at each node (for quick lookup) if (!node.suggestions.includes(word) && node.suggestions.length < this.maxSuggestions) { node.suggestions.push(word); } } node.isEndOfWord = true; } // Insert multiple words insertAll(words) { words.forEach(word => this.insert(word)); } // Get suggestions for a prefix getSuggestions(prefix) { if (!prefix || typeof prefix !== 'string') return []; let node = this.root; prefix = prefix.toLowerCase(); // Travel to the node representing the prefix for (const char of prefix) { if (!node.children.has(char)) { return []; // Prefix not found } node = node.children.get(char); } // Return stored suggestions or collect them if needed if (node.suggestions.length > 0) { return node.suggestions; } else { return this.collectWords(node, prefix, []); } } // Collect all words starting from a node (depth-first search) collectWords(node, currentPrefix, result) { // If we reached the end of a word, add it to results if (node.isEndOfWord) { result.push(currentPrefix); } // If we have enough suggestions, stop searching if (result.length >= this.maxSuggestions) { return result; } // Explore all children for (const [char, childNode] of node.children) { this.collectWords(childNode, currentPrefix + char, result); // If we have enough suggestions after exploring this child, stop if (result.length >= this.maxSuggestions) { break; } } return result; } } // Usage example - Autocomplete search input function initializeAutocomplete() { const trie = new AutocompleteTrie(); // Insert sample words trie.insertAll([ 'apple', 'application', 'append', 'algorithm', 'algebra', 'banana', 'ball', 'baseball', 'battery', 'bake', 'book', 'cat', 'car', 'cargo', 'camera', 'castle', 'celery' ]); const searchInput = document.getElementById('search-input'); const suggestionsList = document.getElementById('suggestions-list'); // Update suggestions as user types searchInput.addEventListener('input', () => { const prefix = searchInput.value.trim(); // Clear previous suggestions suggestionsList.innerHTML = ''; if (prefix.length > 0) { // Get suggestions for current prefix const suggestions = trie.getSuggestions(prefix); // Display suggestions suggestions.forEach(suggestion => { const li = document.createElement('li'); li.textContent = suggestion; li.addEventListener('click', () => { searchInput.value = suggestion; suggestionsList.innerHTML = ''; }); suggestionsList.appendChild(li); }); } }); } // This is similar to trie data structures in CS: /* class TrieNode { private Map<Character, TrieNode> children = new HashMap<>(); private boolean isEndOfWord; private List<String> suggestions = new ArrayList<>(); public void insert(String word, int index) { if (index == word.length()) { this.isEndOfWord = true; return; } char ch = word.charAt(index); TrieNode child = children.getOrDefault(ch, new TrieNode()); children.put(ch, child); if (suggestions.size() < MAX_SUGGESTIONS && !suggestions.contains(word)) { suggestions.add(word); } child.insert(word, index + 1); } public List<String> searchPrefix(String prefix, int index) { if (index == prefix.length()) { return suggestions; } char ch = prefix.charAt(index); TrieNode child = children.get(ch); if (child == null) { return Collections.emptyList(); } return child.searchPrefix(prefix, index + 1); } } */ ``` ## Web Animation and Graphics ### Canvas API - Immediate Mode Rendering **CS Concept:** 2D graphics rendering and drawing algorithms. **Web Development Example:** ```javascript // Canvas API for custom graphics rendering class ParticleSystem { constructor(canvasId, particleCount = 100) { // Get the canvas element and its context this.canvas = document.getElementById(canvasId); this.ctx = this.canvas.getContext('2d'); // Set canvas size to match display size this.resizeCanvas(); // Initialize particle array this.particles = []; this.particleCount = particleCount; // Mouse position for interaction this.mouseX = 0; this.mouseY = 0; // Setup event listeners window.addEventListener('resize', () => this.resizeCanvas()); this.canvas.addEventListener('mousemove', (e) => this.trackMouse(e)); // Create initial particles this.createParticles(); // Start animation loop this.animate(); } // Resize canvas to match display size resizeCanvas() { const { width, height } = this.canvas.getBoundingClientRect(); // Set the canvas resolution to match display this.canvas.width = width; this.canvas.height = height; // Recreate particles if canvas was already initialized if (this.particles.length > 0) { this.createParticles(); } } // Track mouse position for interactive particles trackMouse(event) { const rect = this.canvas.getBoundingClientRect(); this.mouseX = event.clientX - rect.left; this.mouseY = event.clientY - rect.top; } // Create particles createParticles() { this.particles = []; for (let i = 0; i < this.particleCount; i++) { this.particles.push({ x: Math.random() * this.canvas.width, y: Math.random() * this.canvas.height, size: Math.random() * 5 + 1, speedX: Math.random() * 2 - 1, speedY: Math.random() * 2 - 1, color: `hsl(${Math.random() * 360}, 70%, 60%)` }); } } // Update particle positions updateParticles() { for (const particle of this.particles) { // Basic movement particle.x += particle.speedX; particle.y += particle.speedY; // Mouse attraction/repulsion const dx = this.mouseX - particle.x; const dy = this.mouseY - particle.y; const distance = Math.sqrt(dx * dx + dy * dy); // Apply a small force towards/away from mouse if (distance < 100) { const forceDirectionX = dx / distance; const forceDirectionY = dy / distance; const force = (100 - distance) / 100; particle.speedX += forceDirectionX * force * 0.2; particle.speedY += forceDirectionY * force * 0.2; } // Limit speed const speed = Math.sqrt(particle.speedX * particle.speedX + particle.speedY * particle.speedY); if (speed > 2) { particle.speedX = (particle.speedX / speed) * 2; particle.speedY = (particle.speedY / speed) * 2; } // Bounce off walls if (particle.x < 0 || particle.x > this.canvas.width) { particle.speedX *= -1; } if (particle.y < 0 || particle.y > this.canvas.height) { particle.speedY *= -1; } } } // Draw all particles drawParticles() { // Clear the canvas this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); // Draw each particle for (const particle of this.particles) { this.ctx.beginPath(); this.ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2); this.ctx.fillStyle = particle.color; this.ctx.fill(); } // Draw connections between close particles this.ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)'; this.ctx.lineWidth = 0.5; for (let i = 0; i < this.particles.length; i++) { for (let j = i + 1; j < this.particles.length; j++) { const dx = this.particles[i].x - this.particles[j].x; const dy = this.particles[i].y - this.particles[j].y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < 85) { this.ctx.beginPath(); this.ctx.moveTo(this.particles[i].x, this.particles[i].y); this.ctx.lineTo(this.particles[j].x, this.particles[j].y); this.ctx.stroke(); } } } } // Animation loop animate() { this.updateParticles(); this.drawParticles(); // Continue animation requestAnimationFrame(() => this.animate()); } } // Usage example document.addEventListener('DOMContentLoaded', () => { const particleSystem = new ParticleSystem('particle-canvas', 150); }); // This is similar to graphics libraries in CS: /* class GraphicsEngine { private Canvas canvas; private List<Particle> particles; private Vector2D mousePosition; public void update(float deltaTime) { for (Particle particle : particles) { // Update position particle.position.add(particle.velocity.multiply(deltaTime)); // Apply physics Vector2D direction = mousePosition.subtract(particle.position); float distance = direction.length(); if (distance < 100) { direction.normalize(); float force = (100 - distance) / 100; particle.velocity.add(direction.multiply(force * 0.2f)); } // Collision detection if (particle.position.x < 0 || particle.position.x > canvas.width) { particle.velocity.x *= -1; } if (particle.position.y < 0 || particle.position.y > canvas.height) { particle.velocity.y *= -1; } } } public void render() { canvas.clear(); for (Particle particle : particles) { canvas.drawCircle(particle.position, particle.size, particle.color); } // Draw connections for (int i = 0; i < particles.size(); i++) { for (int j = i + 1; j < particles.size(); j++) { float distance = particles.get(i).position.distanceTo(particles.get(j).position); if (distance < 85) { canvas.drawLine(particles.get(i).position, particles.get(j).position, Color.WHITE_TRANSPARENT); } } } } } */ ``` ## Web Workers and Multithreading ### Web Workers - Browser Multithreading **CS Concept:** Parallel computation and worker threads. **Web Development Example:** ```javascript // main.js - Main thread code class ImageProcessor { constructor() { // Initialize the worker this.worker = new Worker('image-worker.js'); // Set up event listener for messages from the worker this.worker.addEventListener('message', this.handleWorkerMessage.bind(this)); } // Process an image using the worker processImage(imageData, filter) { const canvas = document.getElementById('preview-canvas'); const ctx = canvas.getContext('2d'); // Show loading indicator this.showLoading(true); // Send the image data and filter to the worker this.worker.postMessage({ type: 'processImage', imageData: imageData, filter: filter }); } // Handle messages from the worker handleWorkerMessage(event) { const { type, processedImageData, duration } = event.data; if (type === 'processComplete') { // Get the canvas const canvas = document.getElementById('preview-canvas'); const ctx = canvas.getContext('2d'); // Put the processed image data back onto the canvas ctx.putImageData(processedImageData, 0, 0); // Show processing time document.getElementById('processing-time').textContent = `Processing time: ${duration.toFixed(2)}ms`; // Hide loading indicator this.showLoading(false); } } // Toggle loading indicator showLoading(isLoading) { document.getElementById('loading-indicator').style.display = isLoading ? 'block' : 'none'; } } // Handle file upload and processing document.addEventListener('DOMContentLoaded', () => { const imageProcessor = new ImageProcessor(); const fileInput = document.getElementById('image-input'); const filterSelect = document.getElementById('filter-select'); const canvas = document.getElementById('preview-canvas'); const ctx = canvas.getContext('2d'); // When a file is selected fileInput.addEventListener('change', (event) => { const file = event.target.files[0]; if (file && file.type.match('image.*')) { const reader = new FileReader(); reader.onload = (e) => { const img = new Image(); img.onload = () => { // Resize canvas to match image canvas.width = img.width; canvas.height = img.height; // Draw the original image ctx.drawImage(img, 0, 0); // Get the image data const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); // Apply the selected filter const filter = filterSelect.value; imageProcessor.processImage(imageData, filter); }; img.src = e.target.result; }; reader.readAsDataURL(file); } }); // When filter is changed filterSelect.addEventListener('change', () => { // If we already have an image, reprocess with new filter if (canvas.width > 0 && canvas.height > 0) { const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const filter = filterSelect.value; imageProcessor.processImage(imageData, filter); } }); }); // image-worker.js - Worker thread code self.addEventListener('message', function(event) { const { type, imageData, filter } = event.data; if (type === 'processImage') { const startTime = performance.now(); // Process the image based on the selected filter const processedImageData = applyFilter(imageData, filter); const endTime = performance.now(); const duration = endTime - startTime; // Send the processed image data back to the main thread self.postMessage({ type: 'processComplete', processedImageData: processedImageData, duration: duration }); } }); // Apply different image filters function applyFilter(imageData, filter) { const { data, width, height } = imageData; const newImageData = new ImageData( new Uint8ClampedArray(data), width, height ); switch (filter) { case 'grayscale': applyGrayscale(newImageData.data); break; case 'sepia': applySepia(newImageData.data); break; case 'invert': applyInvert(newImageData.data); break; case 'blur': applyBlur(newImageData.data, width, height); break; case 'sharpen': applySharpen(newImageData.data, width, height); break; // Add more filters as needed } return newImageData; } // Filter implementations function applyGrayscale(data) { for (let i = 0; i < data.length; i += 4) { const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; // Weighted average for human eye perception const gray = 0.299 * r + 0.587 * g + 0.114 * b; data[i] = gray; // Red data[i + 1] = gray; // Green data[i + 2] = gray; // Blue // Alpha channel (data[i + 3]) remains unchanged } } function applySepia(data) { for (let i = 0; i < data.length; i += 4) { const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; data[i] = Math.min(255, (r * 0.393) + (g * 0.769) + (b * 0.189)); // Red data[i + 1] = Math.min(255, (r * 0.349) + (g * 0.686) + (b * 0.168)); // Green data[i + 2] = Math.min(255, (r * 0.272) + (g * 0.534) + (b * 0.131)); // Blue } } function applyInvert(data) { for (let i = 0; i < data.length; i += 4) { data[i] = 255 - data[i]; // Red data[i + 1] = 255 - data[i + 1]; // Green data[i + 2] = 255 - data[i + 2]; // Blue } } function applyBlur(data, width, height) { // Simple box blur implementation (3x3 kernel) const kernel = [ 1/9, 1/9, 1/9, 1/9, 1/9, 1/9, 1/9, 1/9, 1/9 ]; applyConvolution(data, width, height, kernel); } function applySharpen(data, width, height) { // Sharpen kernel const kernel = [ 0, -1, 0, -1, 5, -1, 0, -1, 0 ]; applyConvolution(data, width, height, kernel); } // Convolution operation (applies a kernel to the image) function applyConvolution(data, width, height, kernel) { // Create a copy of the original data const origData = new Uint8ClampedArray(data); const kernelSize = Math.sqrt(kernel.length); const kernelRadius = Math.floor(kernelSize / 2); // Apply convolution to each pixel for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { const index = (y * width + x) * 4; let r = 0, g = 0, b = 0; // Apply kernel to pixel and its neighbors for (let ky = 0; ky < kernelSize; ky++) { for (let kx = 0; kx < kernelSize; kx++) { const kernelIndex = ky * kernelSize + kx; // Calculate neighbor coordinates with edge handling const nx = Math.min(width - 1, Math.max(0, x + kx - kernelRadius)); const ny = Math.min(height - 1, Math.max(0, y + ky - kernelRadius)); // Get neighbor pixel const neighborIndex = (ny * width + nx) * 4; // Apply kernel weight r += origData[neighborIndex] * kernel[kernelIndex]; g += origData[neighborIndex + 1] * kernel[kernelIndex]; b += origData[neighborIndex + 2] * kernel[kernelIndex]; } } // Update pixel data data[index] = Math.min(255, Math.max(0, r)); data[index + 1] = Math.min(255, Math.max(0, g)); data[index + 2] = Math.min(255, Math.max(0, b)); // Alpha channel remains unchanged } } } // This is similar to multithreaded programming in CS: /* class ImageProcessor { private ExecutorService threadPool; public ImageProcessor() { this.threadPool = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors() ); } public Future<BufferedImage> processImage(BufferedImage image, String filter) { return threadPool.submit(() -> { // Apply filter in background thread return applyFilter(image, filter); }); } private BufferedImage applyFilter(BufferedImage image, String filter) { int width = image.getWidth(); int height = image.getHeight(); BufferedImage result = new BufferedImage(width, height, image.getType()); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int rgb = image.getRGB(x, y); int r = (rgb >> 16) & 0xFF; int g = (rgb >> 8) & 0xFF; int b = rgb & 0xFF; // Apply filter to pixel int[] newRgb = filterPixel(r, g, b, filter); int newColor = (newRgb[0] << 16) | (newRgb[1] << 8) | newRgb[2]; result.setRGB(x, y, newColor); } } return result; } } */ ``` ## Browser Storage - IndexedDB(Warning - dirty - proof of concept) ### IndexedDB - Client-Side Database **CS Concept:** Transactional database operations in a browser environment. **Web Development Example:** ```javascript // IndexedDB Database - Client-side database implementation class TaskDatabase { constructor(dbName = 'taskManager', version = 1) { this.dbName = dbName; this.version = version; this.db = null; } // Open the database connection async open() { return new Promise((resolve, reject) => { const request = indexedDB.open(this.dbName, this.version); // Handle database upgrade (called when creating or upgrading db) request.onupgradeneeded = (event) => { const db = event.target.result; // Create object store if it doesn't exist if (!db.objectStoreNames.contains('tasks')) { // Create the tasks object store with auto-incrementing ID const store = db.createObjectStore('tasks', { keyPath: 'id', autoIncrement: true }); // Create indexes for efficient querying store.createIndex('title', 'title', { unique: false }); store.createIndex('priority', 'priority', { unique: false }); store.createIndex('status', 'status', { unique: false }); store.createIndex('dueDate', 'dueDate', { unique: false }); } }; // Handle success request.onsuccess = (event) => { this.db = event.target.result; resolve(this.db); }; // Handle errors request.onerror = (event) => { reject(`Database error: ${event.target.error}`); }; }); } // Add a new task async addTask(task) { if (!this.db) await this.open(); return new Promise((resolve, reject) => { const transaction = this.db.transaction(['tasks'], 'readwrite'); const store = transaction.objectStore('tasks'); // Add default values if not provided const newTask = { title: task.title, description: task.description || '', priority: task.priority || 'medium', status: task.status || 'pending', dueDate: task.dueDate || new Date(), createdAt: new Date() }; const request = store.add(newTask); request.onsuccess = (event) => { // Return the inserted task with its new ID newTask.id = event.target.result; resolve(newTask); }; request.onerror = (event) => { reject(`Error adding task: ${event.target.error}`); }; // Handle transaction completion transaction.oncomplete = () => { console.log('Transaction completed: Task added'); }; // Handle transaction errors transaction.onerror = (event) => { reject(`Transaction error: ${event.target.error}`); }; }); } // Get a task by ID async getTask(id) { if (!this.db) await this.open(); return new Promise((resolve, reject) => { const transaction = this.db.transaction(['tasks'], 'readonly'); const store = transaction.objectStore('tasks'); const request = store.get(id); request.onsuccess = (event) => { resolve(event.target.result); }; request.onerror = (event) => { reject(`Error getting task: ${event.target.error}`); }; }); } // Update an existing task async updateTask(task) { if (!this.db) await this.open(); return new Promise((resolve, reject) => { const transaction = this.db.transaction(['tasks'], 'readwrite'); const store = transaction.objectStore('tasks'); // Add updatedAt timestamp task.updatedAt = new Date(); const request = store.put(task); request.onsuccess = () => { resolve(task); }; request.onerror = (event) => { reject(`Error updating task: ${event.target.error}`); }; }); } // Delete a task async deleteTask(id) { if (!this.db) await this.open(); return new Promise((resolve, reject) => { const transaction = this.db.transaction(['tasks'], 'readwrite'); const store = transaction.objectStore('tasks'); const request = store.delete(id); request.onsuccess = () => { resolve(true); }; request.onerror = (event) => { reject(`Error deleting task: ${event.target.error}`); }; }); } // Get all tasks async getAllTasks() { if (!this.db) await this.open(); return new Promise((resolve, reject) => { const transaction = this.db.transaction(['tasks'], 'readonly'); const store = transaction.objectStore('tasks'); const request = store.getAll(); request.onsuccess = (event) => { resolve(event.target.result); }; request.onerror = (event) => { reject(`Error getting tasks: ${event.target.error}`); }; }); } // Get tasks by status (using an index) async getTasksByStatus(status) { if (!this.db) await this.open(); return new Promise((resolve, reject) => { const transaction = this.db.transaction(['tasks'], 'readonly'); const store = transaction.objectStore('tasks'); const index = store.index('status'); const request = index.getAll(status); request.onsuccess = (event) => { resolve(event.target.result); }; request.onerror = (event) => { reject(`Error getting tasks by status: ${event.target.error}`); }; }); } // Get tasks due within a date range (using cursor) async getTasksDueSoon(days = 7) { if (!this.db) await this.open(); return new Promise((resolve, reject) => { const transaction = this.db.transaction(['tasks'], 'readonly'); const store = transaction.objectStore('tasks'); const index = store.index('dueDate'); // Calculate the date range const now = new Date(); const futureDate = new Date(); futureDate.setDate(now.getDate() + days); // Create a key range for the dates const dateRange = IDBKeyRange.bound(now, futureDate); // Use a cursor to iterate through the range const tasks = []; const request = index.openCursor(dateRange); request.onsuccess = (event) => { const cursor = event.target.result; if (cursor) { tasks.push(cursor.value); cursor.continue(); } else { // No more entries resolve(tasks); } }; request.onerror = (event) => { reject(`Error getting tasks due soon: ${event.target.error}`); }; }); } // Close the database connection close() { if (this.db) { this.db.close(); this.db = null; } } } // Usage example - Task Manager application async function initTaskManager() { try { const taskDB = new TaskDatabase(); await taskDB.open(); console.log('Task database opened successfully'); // DOM elements const taskForm = document.getElementById('task-form'); const taskList = document.getElementById('task-list'); const taskFilter = document.getElementById('task-filter'); // Load and display tasks await displayTasks(); // Add task form submission taskForm.addEventListener('submit', async (e) => { e.preventDefault(); const newTask = { title: taskForm.title.value, description: taskForm.description.value, priority: taskForm.priority.value, dueDate: new Date(taskForm.dueDate.value) }; try { await taskDB.addTask(newTask); taskForm.reset(); await displayTasks(); } catch (error) { console.error('Error adding task:', error); } }); // Task filter change taskFilter.addEventListener('change', async () => { await displayTasks(); }); // Display tasks based on filter async function displayTasks() { let tasks; const filter = taskFilter.value; try { if (filter === 'all') { tasks = await taskDB.getAllTasks(); } else if (filter === 'due-soon') { tasks = await taskDB.getTasksDueSoon(7); } else { // Filter by status tasks = await taskDB.getTasksByStatus(filter); } // Sort tasks by due date tasks.sort((a, b) => new Date(a.dueDate) - new Date(b.dueDate)); // Clear current list taskList.innerHTML = ''; // Add tasks to the DOM tasks.forEach(task => { const taskElement = createTaskElement(task); taskList.appendChild(taskElement); }); } catch (error) { console.error('Error displaying tasks:', error); } } // Create task DOM element function createTaskElement(task) { const taskEl = document.createElement('div'); taskEl.className = `task-item priority-${task.priority}`; taskEl.dataset.id = task.id; // Format due date const dueDate = new Date(task.dueDate); const formattedDate = dueDate.toLocaleDateString(); taskEl.innerHTML = ` <h3>${task.title}</h3> <p>${task.description}</p> <div class="task-meta"> <span class="task-priority">${task.priority}</span> <span class="task-status">${task.status}</span> <span class="task-due-date">Due: ${formattedDate}</span> </div> <div class="task-actions"> <button class="complete-btn">Complete</button> <button class="edit-btn">Edit</button> <button class="delete-btn">Delete</button> </div> `; // Add event listeners const completeBtn = taskEl.querySelector('.complete-btn'); const editBtn = taskEl.querySelector('.edit-btn'); const deleteBtn = taskEl.querySelector('.delete-btn'); completeBtn.addEventListener('click', async () => { try { const taskData = await taskDB.getTask(task.id); taskData.status = 'completed'; await taskDB.updateTask(taskData); await displayTasks(); } catch (error) { console.error('Error completing task:', error); } }); editBtn.addEventListener('click', async () => { try { const taskData = await taskDB.getTask(task.id); // Populate form for editing taskForm.title.value = taskData.title; taskForm.description.value = taskData.description; taskForm.priority.value = taskData.priority; // Format date for input field (YYYY-MM-DD) const dueDate = new Date(taskData.dueDate); const year = dueDate.getFullYear(); const month = String(dueDate.getMonth() + 1).padStart(2, '0'); const day = String(dueDate.getDate()).padStart(2, '0'); taskForm.dueDate.value = `${year}-${month}-${day}`; // Change form to update mode taskForm.dataset.mode = 'edit'; taskForm.dataset.taskId = task.id; document.querySelector('#form-submit-btn').textContent = 'Update Task'; } catch (error) { console.error('Error editing task:', error); } }); deleteBtn.addEventListener('click', async () => { if (confirm('Are you sure you want to delete this task?')) { try { await taskDB.deleteTask(task.id); await displayTasks(); } catch (error) { console.error('Error deleting task:', error); } } }); return taskEl; } } catch (error) { console.error('Failed to initialize task manager:', error); } } // Initialize app when DOM is ready document.addEventListener('DOMContentLoaded', initTaskManager); // This is similar to database operations in CS: /* public class TaskRepository { private Connection connection; public TaskRepository(Connection connection) { this.connection = connection; initializeDatabase(); } private void initializeDatabase() { try { Statement stmt = connection.createStatement(); stmt.execute( "CREATE TABLE IF NOT EXISTS tasks (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "title TEXT NOT NULL, " + "description TEXT, " + "priority TEXT, " + "status TEXT, " + "due_date TIMESTAMP, " + "created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, " + "updated_at TIMESTAMP)" ); // Create indexes stmt.execute("CREATE INDEX IF NOT EXISTS idx_status ON tasks(status)"); stmt.execute("CREATE INDEX IF NOT EXISTS idx_due_date ON tasks(due_date)"); } catch (SQLException e) { throw new RuntimeException("Failed to initialize database", e); } } public Task addTask(Task task) { try { PreparedStatement stmt = connection.prepareStatement( "INSERT INTO tasks (title, description, priority, status, due_date) " + "VALUES (?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS ); stmt.setString(1, task.getTitle()); stmt.setString(2, task.getDescription()); stmt.setString(3, task.getPriority()); stmt.setString(4, task.getStatus()); stmt.setTimestamp(5, new Timestamp(task.getDueDate().getTime())); int affectedRows = stmt.executeUpdate(); if (affectedRows == 0) { throw new SQLException("Creating task failed, no rows affected."); } ResultSet generatedKeys = stmt.getGeneratedKeys(); if (generatedKeys.next()) { task.setId(generatedKeys.getLong(1)); } else { throw new SQLException("Creating task failed, no ID obtained."); } return task; } catch (SQLException e) { throw new RuntimeException("Failed to add task", e); } } public List<Task> getTasksByStatus(String status) { List<Task> tasks = new ArrayList<>(); try { PreparedStatement stmt = connection.prepareStatement( "SELECT * FROM tasks WHERE status = ?" ); stmt.setString(1, status); ResultSet rs = stmt.executeQuery(); while (rs.next()) { Task task = new Task(); task.setId(rs.getLong("id")); task.setTitle(rs.getString("title")); task.setDescription(rs.getString("description")); task.setPriority(rs.getString("priority")); task.setStatus(rs.getString("status")); task.setDueDate(rs.getTimestamp("due_date")); tasks.add(task); } } catch (SQLException e) { throw new RuntimeException("Failed to get tasks by status", e); } return tasks; } } */ ```